PageSourceSearch

https://websitescreenshots.com/js/angular.js

js websitescreenshots.com collected 2026-10-03 20:37:15 UTC 1,072,872 bytes, 29,134 lines download raw bytes

1// AngularJS v1.4.7 ie8 build
2
3(function () {
4
5  // detect IE8 one way or another
6  /*one way
7	var ie=function(){
8		for(var e,i=3,n=document.createElement("div"),t=n.getElementsByTagName("i");
9		n.innerHTML="<!--[if gt IE "+ ++i+"]><i></i><![endif]-->",t[0];);return i>4?i:e
10	}();
11	if (ie !== 8) {
12		// proper browsers exit here
13		return;
14	}
15  */
16
17  // another way
18  if (window.addEventListener) {
19    // proper browsers exit here
20    return;
21  }
22
23  // ie8 ftw...
24  if (!window.XMLHttpRequest) {
25    //console.log('XMLHttpRequest not enabled');
26
27    window.XMLHttpRequest = function () {
28      var xmlhttp = new ActiveXObject('Microsoft.XMLHTTP'),
29        out = {
30          isFake: true,
31          send: function (a) {
32            return xmlhttp.send(a);
33          },
34          open: function (a, b, c, d, e) {
35            return xmlhttp.open(a, b, c, d, e);
36          },
37          abort: function () {
38            return xmlhttp.abort();
39          },
40          setRequestHeader: function (a, b) {
41            return xmlhttp.setRequestHeader(a, b);
42          },
43          getResponseHeader: function (a) {
44            return xmlhttp.getResponseHeader(a);
45          },
46          getAllResponseHeaders: function () {
47            return xmlhttp.getAllResponseHeaders();
48          },
49          overrideMimeType: function (a) {
50            return xmlhttp.overrideMimeType(a);
51          }
52        };
53
54      xmlhttp.onreadystatechange = function () {
55        out.readyState = xmlhttp.readyState;
56
57        if (xmlhttp.readyState === 4 && xmlhttp.status === 200) {
58          out.status = xmlhttp.status;
59          out.responseText = xmlhttp.responseText;
60          out.responseXML = xmlhttp.responseXML;
61          out.statusText = xmlhttp.statusText;
62
63          if (out.onload) {
64            out.onload.apply(this, arguments);
65          }
66        }
67
68        if (out.onreadystatechange) {
69          out.onreadystatechange.apply(this, arguments);
70        }
71
72      };
73
74      return out;
75
76    };
77  }
78
79  // ------ XMLHttpRequest.onload ------
80  // monkey patch XMLHttpRequest to make IE8 call onload when readyState === 4
81  var sendFn = XMLHttpRequest.prototype.send;
82  XMLHttpRequest.prototype.send = function () {
83    // only if onreadystatechange has not already been set
84    // to avoid breaking anything outside of angular
85    if (!this.onreadystatechange) {
86      this.onreadystatechange = function () {
87        if (this.readyState === 4 && this.onload) {
88          this.onload();
89        }
90      };
91    }
92    // apply this & args to original send
93    sendFn.apply(this, arguments);
94  };
95
96
97  // ------ Object.create ------
98  // force Object.create to this implementation for IE8
99  // (es5-sham version doesn't work in this instance)
100  // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create
101  Object.create = (function () {
102    var Object = function () {};
103    return function (prototype) {
104      if (arguments.length > 1) {
105        throw Error('Second argument not supported');
106      }
107      if (typeof prototype != 'object') {
108        throw TypeError('Argument must be an object');
109      }
110      Object.prototype = prototype;
111      var result = new Object();
112      Object.prototype = null;
113      return result;
114    };
115  })();
116
117
118  // ------ Object.getPrototypeOf ------
119  // http://stackoverflow.com/a/15851520/674863
120  if (typeof Object.getPrototypeOf !== 'function') {
121    Object.getPrototypeOf = ''.__proto__ === String.prototype ? function (object) {
122      return object.__proto__;
123    } : function (object) {
124      // May break if the constructor has been tampered with
125      return object.constructor.prototype;
126    };
127  }
128
129  // textContent shim using Sizzle / jQuery get & set text methods
130  (function () {
131
132    var getText = function (elem) {
133      var node,
134        ret = '',
135        i = 0,
136        nodeType = elem.nodeType;
137
138      if (!nodeType) {
139        // If no nodeType, this is expected to be an array
140        while ((node = elem[i++])) {
141          // Do not traverse comment nodes
142          ret += getText(node);
143        }
144      } else if (nodeType === 1 || nodeType === 9 || nodeType === 11) {
145        // Traverse its children
146        for (elem = elem.firstChild; elem; elem = elem.nextSibling) {
147          ret += getText(elem);
148        }
149      } else if (nodeType === 3 || nodeType === 4) {
150        return elem.nodeValue;
151      }
152      // Do not include comment or processing instruction nodes
153
154      return ret;
155    };
156
157    if (Object.defineProperty && Object.getOwnPropertyDescriptor &&
158      Object.getOwnPropertyDescriptor(Element.prototype, 'textContent') &&
159      !Object.getOwnPropertyDescriptor(Element.prototype, 'textContent').get) {
160      var innerText = Object.getOwnPropertyDescriptor(Element.prototype, 'innerText');
161      Object.defineProperty(Element.prototype, 'textContent', {
162        get: function () {
163          return getText(this);
164        },
165        set: function (x) {
166          //var $this = $(this);
167          //return $this.empty().append(($this[0] && $this[0].ownerDocument || document).createTextNode(x));
168          // empty
169          while (this.hasChildNodes()) {
170            this.removeChild(this.lastChild);
171          }
172          // add text node
173          return this.appendChild((this && this.ownerDocument || document).createTextNode(x));
174        }
175      });
176    }
177
178  })();
179
180  // ------ addEventListener ------
181  // https://gist.github.com/jonathantneal/3748027
182  !window.addEventListener && (function (WindowPrototype, DocumentPrototype, ElementPrototype, addEventListener, removeEventListener, dispatchEvent, registry) {
183    WindowPrototype[addEventListener] = DocumentPrototype[addEventListener] = ElementPrototype[addEventListener] = function (type, listener) {
184      var target = this;
185      registry.unshift([target, type, listener,
186    function (event) {
187          event.currentTarget = target;
188          event.preventDefault = function () {
189            event.returnValue = false;
190          };
191          event.stopPropagation = function () {
192            event.cancelBubble = true;
193          };
194          event.target = event.srcElement || target;
195          listener.call(target, event);
196    }]);
197
198      // http://msdn.microsoft.com/en-us/library/ie/hh180173%28v=vs.85%29.aspx
199      if (type === 'load' && this.tagName && this.tagName === 'SCRIPT') {
200        var reg = registry[0][3];
201        this.onreadystatechange = function (event) {
202          if (this.readyState === "loaded" || this.readyState === "complete") {
203            reg.call(this, {
204              type: "load"
205            });
206          }
207        }
208      } else {
209        this.attachEvent('on' + type, registry[0][3]);
210      }
211    };
212    WindowPrototype[removeEventListener] = DocumentPrototype[removeEventListener] = ElementPrototype[removeEventListener] = function (type, listener) {
213      for (var index = 0, register; register = registry[index]; ++index) {
214        if (register[0] == this && register[1] == type && register[2] == listener) {
215          if (type === 'load' && this.tagName && this.tagName === 'SCRIPT') {
216            this.onreadystatechange = null;
217          }
218
219          return this.detachEvent('on' + type, registry.splice(index, 1)[0][3]);
220        }
221      }
222    };
223    WindowPrototype[dispatchEvent] = DocumentPrototype[dispatchEvent] = ElementPrototype[dispatchEvent] = function (eventObject) {
224      return this.fireEvent('on' + eventObject.type, eventObject);
225    };
226  })(Window.prototype, HTMLDocument.prototype, Element.prototype, 'addEventListener', 'removeEventListener', 'dispatchEvent', []);
227
228}());
229
230/**
231 * @license AngularJS v1.4.7
232 * (c) 2010-2015 Google, Inc. http://angularjs.org
233 * License: MIT
234 */
235(function(window, document, undefined) {'use strict';
236
237/**
238 * @description
239 *
240 * This object provides a utility for producing rich Error messages within
241 * Angular. It can be called as follows:
242 *
243 * var exampleMinErr = minErr('example');
244 * throw exampleMinErr('one', 'This {0} is {1}', foo, bar);
245 *
246 * The above creates an instance of minErr in the example namespace. The
247 * resulting error will have a namespaced error code of example.one.  The
248 * resulting error will replace {0} with the value of foo, and {1} with the
249 * value of bar. The object is not restricted in the number of arguments it can
250 * take.
251 *
252 * If fewer arguments are specified than necessary for interpolation, the extra
253 * interpolation markers will be preserved in the final string.
254 *
255 * Since data will be parsed statically during a build step, some restrictions
256 * are applied with respect to how minErr instances are created and called.
257 * Instances should have names of the form namespaceMinErr for a minErr created
258 * using minErr('namespace') . Error codes, namespaces and template strings
259 * should all be static strings, not variables or general expressions.
260 *
261 * @param {string} module The namespace to use for the new minErr instance.
262 * @param {function} ErrorConstructor Custom error constructor to be instantiated when returning
263 *   error from returned function, for cases when a particular type of error is useful.
264 * @returns {function(code:string, template:string, ...templateArgs): Error} minErr instance
265 */
266
267function minErr(module, ErrorConstructor) {
268  ErrorConstructor = ErrorConstructor || Error;
269  return function() {
270    var SKIP_INDEXES = 2;
271
272    var templateArgs = arguments,
273      code = templateArgs[0],
274      message = '[' + (module ? module + ':' : '') + code + '] ',
275      template = templateArgs[1],
276      paramPrefix, i;
277
278    message += template.replace(/\{\d+\}/g, function(match) {
279      var index = +match.slice(1, -1),
280        shiftedIndex = index + SKIP_INDEXES;
281
282      if (shiftedIndex < templateArgs.length) {
283        return toDebugString(templateArgs[shiftedIndex]);
284      }
285
286      return match;
287    });
288
289    message += '\nhttp://errors.angularjs.org/1.4.7/' +
290      (module ? module + '/' : '') + code;
291
292    for (i = SKIP_INDEXES, paramPrefix = '?'; i < templateArgs.length; i++, paramPrefix = '&') {
293      message += paramPrefix + 'p' + (i - SKIP_INDEXES) + '=' +
294        encodeURIComponent(toDebugString(templateArgs[i]));
295    }
296
297    return new ErrorConstructor(message);
298  };
299}
300
301/* We need to tell jshint what variables are being exported */
302/* global angular: true,
303  msie: true,
304  jqLite: true,
305  jQuery: true,
306  slice: true,
307  splice: true,
308  push: true,
309  toString: true,
310  ngMinErr: true,
311  angularModule: true,
312  uid: true,
313  REGEX_STRING_REGEXP: true,
314  VALIDITY_STATE_PROPERTY: true,
315
316  lowercase: true,
317  uppercase: true,
318  manualLowercase: true,
319  manualUppercase: true,
320  nodeName_: true,
321  isArrayLike: true,
322  forEach: true,
323  forEachSorted: true,
324  reverseParams: true,
325  nextUid: true,
326  setHashKey: true,
327  extend: true,
328  toInt: true,
329  inherit: true,
330  merge: true,
331  noop: true,
332  identity: true,
333  valueFn: true,
334  isUndefined: true,
335  isDefined: true,
336  isObject: true,
337  isBlankObject: true,
338  isString: true,
339  isNumber: true,
340  isDate: true,
341  isArray: true,
342  isFunction: true,
343  isRegExp: true,
344  isWindow: true,
345  isScope: true,
346  isFile: true,
347  isFormData: true,
348  isBlob: true,
349  isBoolean: true,
350  isPromiseLike: true,
351  trim: true,
352  escapeForRegexp: true,
353  isElement: true,
354  makeMap: true,
355  includes: true,
356  arrayRemove: true,
357  copy: true,
358  shallowCopy: true,
359  equals: true,
360  csp: true,
361  jq: true,
362  concat: true,
363  sliceArgs: true,
364  bind: true,
365  toJsonReplacer: true,
366  toJson: true,
367  fromJson: true,
368  convertTimezoneToLocal: true,
369  timezoneToOffset: true,
370  startingTag: true,
371  tryDecodeURIComponent: true,
372  parseKeyValue: true,
373  toKeyValue: true,
374  encodeUriSegment: true,
375  encodeUriQuery: true,
376  angularInit: true,
377  bootstrap: true,
378  getTestability: true,
379  snake_case: true,
380  bindJQuery: true,
381  assertArg: true,
382  assertArgFn: true,
383  assertNotHasOwnProperty: true,
384  getter: true,
385  getBlockNodes: true,
386  hasOwnProperty: true,
387  createMap: true,
388
389  NODE_TYPE_ELEMENT: true,
390  NODE_TYPE_ATTRIBUTE: true,
391  NODE_TYPE_TEXT: true,
392  NODE_TYPE_COMMENT: true,
393  NODE_TYPE_DOCUMENT: true,
394  NODE_TYPE_DOCUMENT_FRAGMENT: true,
395*/
396
397////////////////////////////////////
398
399/**
400 * @ngdoc module
401 * @name ng
402 * @module ng
403 * @description
404 *
405 * # ng (core module)
406 * The ng module is loaded by default when an AngularJS application is started. The module itself
407 * contains the essential components for an AngularJS application to function. The table below
408 * lists a high level breakdown of each of the services/factories, filters, directives and testing
409 * components available within this core module.
410 *
411 * <div doc-module-components="ng"></div>
412 */
413
414var REGEX_STRING_REGEXP = /^\/(.+)\/([a-z]*)$/;
415
416// The name of a form control's ValidityState property.
417// This is used so that it's possible for internal tests to create mock ValidityStates.
418var VALIDITY_STATE_PROPERTY = 'validity';
419
420/**
421 * @ngdoc function
422 * @name angular.lowercase
423 * @module ng
424 * @kind function
425 *
426 * @description Converts the specified string to lowercase.
427 * @param {string} string String to be converted to lowercase.
428 * @returns {string} Lowercased string.
429 */
430var lowercase = function(string) {return isString(string) ? string.toLowerCase() : string;};
431var hasOwnProperty = Object.prototype.hasOwnProperty;
432
433/**
434 * @ngdoc function
435 * @name angular.uppercase
436 * @module ng
437 * @kind function
438 *
439 * @description Converts the specified string to uppercase.
440 * @param {string} string String to be converted to uppercase.
441 * @returns {string} Uppercased string.
442 */
443var uppercase = function(string) {return isString(string) ? string.toUpperCase() : string;};
444
445
446var manualLowercase = function(s) {
447  /* jshint bitwise: false */
448  return isString(s)
449      ? s.replace(/[A-Z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) | 32);})
450      : s;
451};
452var manualUppercase = function(s) {
453  /* jshint bitwise: false */
454  return isString(s)
455      ? s.replace(/[a-z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) & ~32);})
456      : s;
457};
458
459
460// String#toLowerCase and String#toUpperCase don't produce correct results in browsers with Turkish
461// locale, for this reason we need to detect this case and redefine lowercase/uppercase methods
462// with correct but slower alternatives.
463if ('i' !== 'I'.toLowerCase()) {
464  lowercase = manualLowercase;
465  uppercase = manualUppercase;
466}
467
468
469var
470    msie,             // holds major version number for IE, or NaN if UA is not IE.
471    jqLite,           // delay binding since jQuery could be loaded after us.
472    jQuery,           // delay binding
473    slice             = [].slice,
474    splice            = [].splice,
475    push              = [].push,
476    toString          = Object.prototype.toString,
477    getPrototypeOf    = Object.getPrototypeOf,
478    ngMinErr          = minErr('ng'),
479
480    /** @name angular */
481    angular           = window.angular || (window.angular = {}),
482    angularModule,
483    uid               = 0;
484
485/**
486 * documentMode is an IE-only property
487 * http://msdn.microsoft.com/en-us/library/ie/cc196988(v=vs.85).aspx
488 */
489msie = document.documentMode;
490
491
492/**
493 * @private
494 * @param {*} obj
495 * @return {boolean} Returns true if `obj` is an array or array-like object (NodeList, Arguments,
496 *                   String ...)
497 */
498function isArrayLike(obj) {
499  if (obj == null || isWindow(obj)) {
500    return false;
501  }
502
503  // Support: iOS 8.2 (not reproducible in simulator)
504  // "length" in obj used to prevent JIT error (gh-11508)
505  var length = "length" in Object(obj) && obj.length;
506
507  if (obj.nodeType === NODE_TYPE_ELEMENT && length) {
508    return true;
509  }
510
511  return isString(obj) || isArray(obj) || length === 0 ||
512         typeof length === 'number' && length > 0 && (length - 1) in obj;
513}
514
515/**
516 * @ngdoc function
517 * @name angular.forEach
518 * @module ng
519 * @kind function
520 *
521 * @description
522 * Invokes the `iterator` function once for each item in `obj` collection, which can be either an
523 * object or an array. The `iterator` function is invoked with `iterator(value, key, obj)`, where `value`
524 * is the value of an object property or an array element, `key` is the object property key or
525 * array element index and obj is the `obj` itself. Specifying a `context` for the function is optional.
526 *
527 * It is worth noting that `.forEach` does not iterate over inherited properties because it filters
528 * using the `hasOwnProperty` method.
529 *
530 * Unlike ES262's
531 * [Array.prototype.forEach](http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.18),
532 * Providing 'undefined' or 'null' values for `obj` will not throw a TypeError, but rather just
533 * return the value provided.
534 *
535   ```js
536     var values = {name: 'misko', gender: 'male'};
537     var log = [];
538     angular.forEach(values, function(value, key) {
539       this.push(key + ': ' + value);
540     }, log);
541     expect(log).toEqual(['name: misko', 'gender: male']);
542   ```
543 *
544 * @param {Object|Array} obj Object to iterate over.
545 * @param {Function} iterator Iterator function.
546 * @param {Object=} context Object to become context (`this`) for the iterator function.
547 * @returns {Object|Array} Reference to `obj`.
548 */
549
550function forEach(obj, iterator, context) {
551  var key, length;
552  if (obj) {
553    if (isFunction(obj)) {
554      for (key in obj) {
555        // Need to check if hasOwnProperty exists,
556        // as on IE8 the result of querySelectorAll is an object without a hasOwnProperty function
557        if (key != 'prototype' && key != 'length' && key != 'name' && (!obj.hasOwnProperty || obj.hasOwnProperty(key))) {
558          iterator.call(context, obj[key], key, obj);
559        }
560      }
561    } else if (isArray(obj) || isArrayLike(obj)) {
562      var isPrimitive = typeof obj !== 'object';
563      for (key = 0, length = obj.length; key < length; key++) {
564        if (isPrimitive || key in obj) {
565          iterator.call(context, obj[key], key, obj);
566        }
567      }
568    } else if (obj.forEach && obj.forEach !== forEach) {
569        obj.forEach(iterator, context, obj);
570    } else if (isBlankObject(obj)) {
571      // createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty
572      for (key in obj) {
573        iterator.call(context, obj[key], key, obj);
574      }
575    } else if (typeof obj.hasOwnProperty === 'function') {
576      // Slow path for objects inheriting Object.prototype, hasOwnProperty check needed
577      for (key in obj) {
578        if (obj.hasOwnProperty(key)) {
579          iterator.call(context, obj[key], key, obj);
580        }
581      }
582    } else {
583      // Slow path for objects which do not have a method `hasOwnProperty`
584      for (key in obj) {
585        if (hasOwnProperty.call(obj, key)) {
586          iterator.call(context, obj[key], key, obj);
587        }
588      }
589    }
590  }
591  return obj;
592}
593
594function forEachSorted(obj, iterator, context) {
595  var keys = Object.keys(obj).sort();
596  for (var i = 0; i < keys.length; i++) {
597    iterator.call(context, obj[keys[i]], keys[i]);
598  }
599  return keys;
600}
601
602
603/**
604 * when using forEach the params are value, key, but it is often useful to have key, value.
605 * @param {function(string, *)} iteratorFn
606 * @returns {function(*, string)}
607 */
608function reverseParams(iteratorFn) {
609  return function(value, key) { iteratorFn(key, value); };
610}
611
612/**
613 * A consistent way of creating unique IDs in angular.
614 *
615 * Using simple numbers allows us to generate 28.6 million unique ids per second for 10 years before
616 * we hit number precision issues in JavaScript.
617 *
618 * Math.pow(2,53) / 60 / 60 / 24 / 365 / 10 = 28.6M
619 *
620 * @returns {number}
vendor: 9,733 bytes, lines 620-1044
620 an unique alpha-numeric string
621 */
622function nextUid() {
623  return ++uid;
624}
625
626
627/**
628 * Set or clear the hashkey for an object.
629 * @param obj object
630 * @param h the hashkey (!truthy to delete the hashkey)
631 */
632function setHashKey(obj, h) {
633  if (h) {
634    obj.$$hashKey = h;
635  } else {
636    delete obj.$$hashKey;
637  }
638}
639
640
641function baseExtend(dst, objs, deep) {
642  var h = dst.$$hashKey;
643
644  for (var i = 0, ii = objs.length; i < ii; ++i) {
645    var obj = objs[i];
646    if (!isObject(obj) && !isFunction(obj)) continue;
647    var keys = Object.keys(obj);
648    for (var j = 0, jj = keys.length; j < jj; j++) {
649      var key = keys[j];
650      var src = obj[key];
651
652      if (deep && isObject(src)) {
653        if (isDate(src)) {
654          dst[key] = new Date(src.valueOf());
655        } else if (isRegExp(src)) {
656          dst[key] = new RegExp(src);
657        } else {
658          if (!isObject(dst[key])) dst[key] = isArray(src) ? [] : {};
659          baseExtend(dst[key], [src], true);
660        }
661      } else {
662        dst[key] = src;
663      }
664    }
665  }
666
667  setHashKey(dst, h);
668  return dst;
669}
670
671/**
672 * @ngdoc function
673 * @name angular.extend
674 * @module ng
675 * @kind function
676 *
677 * @description
678 * Extends the destination object `dst` by copying own enumerable properties from the `src` object(s)
679 * to `dst`. You can specify multiple `src` objects. If you want to preserve original objects, you can do so
680 * by passing an empty object as the target: `var object = angular.extend({}, object1, object2)`.
681 *
682 * **Note:** Keep in mind that `angular.extend` does not support recursive merge (deep copy). Use
683 * {@link angular.merge} for this.
684 *
685 * @param {Object} dst Destination object.
686 * @param {...Object} src Source object(s).
687 * @returns {Object} Reference to `dst`.
688 */
689function extend(dst) {
690  return baseExtend(dst, slice.call(arguments, 1), false);
691}
692
693
694/**
695* @ngdoc function
696* @name angular.merge
697* @module ng
698* @kind function
699*
700* @description
701* Deeply extends the destination object `dst` by copying own enumerable properties from the `src` object(s)
702* to `dst`. You can specify multiple `src` objects. If you want to preserve original objects, you can do so
703* by passing an empty object as the target: `var object = angular.merge({}, object1, object2)`.
704*
705* Unlike {@link angular.extend extend()}, `merge()` recursively descends into object properties of source
706* objects, performing a deep copy.
707*
708* @param {Object} dst Destination object.
709* @param {...Object} src Source object(s).
710* @returns {Object} Reference to `dst`.
711*/
712function merge(dst) {
713  return baseExtend(dst, slice.call(arguments, 1), true);
714}
715
716
717
718function toInt(str) {
719  return parseInt(str, 10);
720}
721
722
723function inherit(parent, extra) {
724  return extend(Object.create(parent), extra);
725}
726
727/**
728 * @ngdoc function
729 * @name angular.noop
730 * @module ng
731 * @kind function
732 *
733 * @description
734 * A function that performs no operations. This function can be useful when writing code in the
735 * functional style.
736   ```js
737     function foo(callback) {
738       var result = calculateResult();
739       (callback || angular.noop)(result);
740     }
741   ```
742 */
743function noop() {}
744noop.$inject = [];
745
746
747/**
748 * @ngdoc function
749 * @name angular.identity
750 * @module ng
751 * @kind function
752 *
753 * @description
754 * A function that returns its first argument. This function is useful when writing code in the
755 * functional style.
756 *
757   ```js
758     function transformer(transformationFn, value) {
759       return (transformationFn || angular.identity)(value);
760     };
761   ```
762  * @param {*} value to be returned.
763  * @returns {*} the value passed in.
764 */
765function identity($) {return $;}
766identity.$inject = [];
767
768
769function valueFn(value) {return function() {return value;};}
770
771function hasCustomToString(obj) {
772  return isFunction(obj.toString) && obj.toString !== Object.prototype.toString;
773}
774
775
776/**
777 * @ngdoc function
778 * @name angular.isUndefined
779 * @module ng
780 * @kind function
781 *
782 * @description
783 * Determines if a reference is undefined.
784 *
785 * @param {*} value Reference to check.
786 * @returns {boolean} True if `value` is undefined.
787 */
788function isUndefined(value) {return typeof value === 'undefined';}
789
790
791/**
792 * @ngdoc function
793 * @name angular.isDefined
794 * @module ng
795 * @kind function
796 *
797 * @description
798 * Determines if a reference is defined.
799 *
800 * @param {*} value Reference to check.
801 * @returns {boolean} True if `value` is defined.
802 */
803function isDefined(value) {return typeof value !== 'undefined';}
804
805
806/**
807 * @ngdoc function
808 * @name angular.isObject
809 * @module ng
810 * @kind function
811 *
812 * @description
813 * Determines if a reference is an `Object`. Unlike `typeof` in JavaScript, `null`s are not
814 * considered to be objects. Note that JavaScript arrays are objects.
815 *
816 * @param {*} value Reference to check.
817 * @returns {boolean} True if `value` is an `Object` but not `null`.
818 */
819function isObject(value) {
820  // http://jsperf.com/isobject4
821  return value !== null && typeof value === 'object';
822}
823
824
825/**
826 * Determine if a value is an object with a null prototype
827 *
828 * @returns {boolean} True if `value` is an `Object` with a null prototype
829 */
830function isBlankObject(value) {
831  return value !== null && typeof value === 'object' && !getPrototypeOf(value);
832}
833
834
835/**
836 * @ngdoc function
837 * @name angular.isString
838 * @module ng
839 * @kind function
840 *
841 * @description
842 * Determines if a reference is a `String`.
843 *
844 * @param {*} value Reference to check.
845 * @returns {boolean} True if `value` is a `String`.
846 */
847function isString(value) {return typeof value === 'string';}
848
849
850/**
851 * @ngdoc function
852 * @name angular.isNumber
853 * @module ng
854 * @kind function
855 *
856 * @description
857 * Determines if a reference is a `Number`.
858 *
859 * This includes the "special" numbers `NaN`, `+Infinity` and `-Infinity`.
860 *
861 * If you wish to exclude these then you can use the native
862 * [`isFinite'](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/isFinite)
863 * method.
864 *
865 * @param {*} value Reference to check.
866 * @returns {boolean} True if `value` is a `Number`.
867 */
868function isNumber(value) {return typeof value === 'number';}
869
870
871/**
872 * @ngdoc function
873 * @name angular.isDate
874 * @module ng
875 * @kind function
876 *
877 * @description
878 * Determines if a value is a date.
879 *
880 * @param {*} value Reference to check.
881 * @returns {boolean} True if `value` is a `Date`.
882 */
883function isDate(value) {
884  return toString.call(value) === '[object Date]';
885}
886
887
888/**
889 * @ngdoc function
890 * @name angular.isArray
891 * @module ng
892 * @kind function
893 *
894 * @description
895 * Determines if a reference is an `Array`.
896 *
897 * @param {*} value Reference to check.
898 * @returns {boolean} True if `value` is an `Array`.
899 */
900var isArray = Array.isArray;
901
902/**
903 * @ngdoc function
904 * @name angular.isFunction
905 * @module ng
906 * @kind function
907 *
908 * @description
909 * Determines if a reference is a `Function`.
910 *
911 * @param {*} value Reference to check.
912 * @returns {boolean} True if `value` is a `Function`.
913 */
914function isFunction(value) {return typeof value === 'function';}
915
916
917/**
918 * Determines if a value is a regular expression object.
919 *
920 * @private
921 * @param {*} value Reference to check.
922 * @returns {boolean} True if `value` is a `RegExp`.
923 */
924function isRegExp(value) {
925  return toString.call(value) === '[object RegExp]';
926}
927
928
929/**
930 * Checks if `obj` is a window object.
931 *
932 * @private
933 * @param {*} obj Object to check
934 * @returns {boolean} True if `obj` is a window obj.
935 */
936function isWindow(obj) {
937  return obj && obj.window === obj;
938}
939
940
941function isScope(obj) {
942  return obj && obj.$evalAsync && obj.$watch;
943}
944
945
946function isFile(obj) {
947  return toString.call(obj) === '[object File]';
948}
949
950
951function isFormData(obj) {
952  return toString.call(obj) === '[object FormData]';
953}
954
955
956function isBlob(obj) {
957  return toString.call(obj) === '[object Blob]';
958}
959
960
961function isBoolean(value) {
962  return typeof value === 'boolean';
963}
964
965
966function isPromiseLike(obj) {
967  return obj && isFunction(obj.then);
968}
969
970
971var TYPED_ARRAY_REGEXP = /^\[object (Uint8(Clamped)?)|(Uint16)|(Uint32)|(Int8)|(Int16)|(Int32)|(Float(32)|(64))Array\]$/;
972function isTypedArray(value) {
973  return TYPED_ARRAY_REGEXP.test(toString.call(value));
974}
975
976
977var trim = function(value) {
978  return isString(value) ? value.trim() : value;
979};
980
981// Copied from:
982// http://docs.closure-library.googlecode.com/git/local_closure_goog_string_string.js.source.html#line1021
983// Prereq: s is a string.
984var escapeForRegexp = function(s) {
985  return s.replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g, '\\$1').
986           replace(/\x08/g, '\\x08');
987};
988
989
990/**
991 * @ngdoc function
992 * @name angular.isElement
993 * @module ng
994 * @kind function
995 *
996 * @description
997 * Determines if a reference is a DOM element (or wrapped jQuery element).
998 *
999 * @param {*} value Reference to check.
1000 * @returns {boolean} True if `value` is a DOM element (or wrapped jQuery element).
1001 */
1002function isElement(node) {
1003  return !!(node &&
1004    (node.nodeName  // we are a direct element
1005    || (node.prop && node.attr && node.find)));  // we have an on and find method part of jQuery API
1006}
1007
1008/**
1009 * @param str 'key1,key2,...'
1010 * @returns {object} in the form of {key1:true, key2:true, ...}
1011 */
1012function makeMap(str) {
1013  var obj = {}, items = str.split(","), i;
1014  for (i = 0; i < items.length; i++) {
1015    obj[items[i]] = true;
1016  }
1017  return obj;
1018}
1019
1020
1021function nodeName_(element) {
1022  return lowercase(element.nodeName || (element[0] && element[0].nodeName));
1023}
1024
1025function includes(array, obj) {
1026  return Array.prototype.indexOf.call(array, obj) != -1;
1027}
1028
1029function arrayRemove(array, value) {
1030  var index = array.indexOf(value);
1031  if (index >= 0) {
1032    array.splice(index, 1);
1033  }
1034  return index;
1035}
1036
1037/**
1038 * @ngdoc function
1039 * @name angular.copy
1040 * @module ng
1041 * @kind function
1042 *
1043 * @description
1044 * Creates a deep copy of `source`, which should be an object or an array.
1045 *
1046 * * If no destination is supplied, a copy of the object or array is created.
1047 * * If a destination is provided, all of its elements (for arrays) or properties (for objects)
1048 *   are deleted and then all elements/properties from the source are copied to it.
1049 * * If `source` is not an object or array (inc. `null` and `undefined`), `source` is returned.
1050 * * If `source` is identical to 'destination' an exception will be thrown.
1051 *
1052 * @param {*} source The source that will be used to make a copy.
1053 *                   Can be any type, including primitives, `null`, and `undefined`.
1054 * @param {(Object|Array)=} destination Destination into which the source is copied. If
1055 *     provided, must be of the same type as `source`.
1056 * @returns {*} The copy or updated `destination`, if `destination` was specified.
1057 *
1058 * @example
1059 <example module="copyExample">
1060 <file name="index.html">
1061 <div ng-controller="ExampleController">
1062 <form novalidate class="simple-form">
1063 Name: <input type="text" ng-model="user.name" /><br />
1064 E-mail: <input type="email" ng-model="user.email" /><br />
1065 Gender: <input type="radio" ng-model="user.gender" value="male" />male
1066 <input type="radio" ng-model="user.gender" value="female" />female<br />
1067 <button ng-click="reset()">RESET</button>
1068 <button ng-click="update(user)">SAVE</button>
1069 </form>
1070 <pre>form = {{user | json}}</pre>
1071 <pre>master = {{master | json}}</pre>
1072 </div>
1073
1074 <script>
1075  angular.module('copyExample', [])
1076    .controller('ExampleController', ['$scope', function($scope) {
1077      $scope.master= {};
1078
1079      $scope.update = function(user) {
1080        // Example with 1 argument
1081        $scope.master= angular.copy(user);
1082      };
1083
1084      $scope.reset = function() {
1085        // Example with 2 arguments
1086        angular.copy($scope.master, $scope.user);
1087      };
1088
1089      $scope.reset();
1090    }]);
1091 </script>
1092 </file>
1093 </example>
1094 */
1095function copy(source, destination, stackSource, stackDest) {
1096  if (isWindow(source) || isScope(source)) {
1097    throw ngMinErr('cpws',
1098      "Can't copy! Making copies of Window or Scope instances is not supported.");
1099  }
1100  if (isTypedArray(destination)) {
1101    throw ngMinErr('cpta',
1102      "Can't copy! TypedArray destination cannot be mutated.");
1103  }
1104
1105  if (!destination) {
1106    destination = source;
1107    if (isObject(source)) {
1108      var index;
1109      if (stackSource && (index = stackSource.indexOf(source)) !== -1) {
1110        return stackDest[index];
1111      }
1112
1113      // TypedArray, Date and RegExp have specific copy functionality and must be
1114      // pushed onto the stack before returning.
1115      // Array and other objects create the base object and recurse to copy child
1116      // objects. The array/object will be pushed onto the stack when recursed.
1117      if (isArray(source)) {
1118        return copy(source, [], stackSource, stackDest);
1119      } else if (isTypedArray(source)) {
1120        destination = new source.constructor(source);
1121      } else if (isDate(source)) {
1122        destination = new Date(source.getTime());
1123      } else if (isRegExp(source)) {
1124        destination = new RegExp(source.source, source.toString().match(/[^\/]*$/)[0]);
1125        destination.lastIndex = source.lastIndex;
1126      } else if (isFunction(source.cloneNode)) {
1127          destination = source.cloneNode(true);
1128      } else {
1129        var emptyObject = Object.create(getPrototypeOf(source));
1130        return copy(source, emptyObject, stackSource, stackDest);
1131      }
1132
1133      if (stackDest) {
1134        stackSource.push(source);
1135        stackDest.push(destination);
1136      }
1137    }
1138  } else {
1139    if (source === destination) throw ngMinErr('cpi',
1140      "Can't copy! Source and destination are identical.");
1141
1142    stackSource = stackSource || [];
1143    stackDest = stackDest || [];
1144
1145    if (isObject(source)) {
1146      stackSource.push(source);
1147      stackDest.push(destination);
1148    }
1149
1150    var result, key;
1151    if (isArray(source)) {
1152      destination.length = 0;
1153      for (var i = 0; i < source.length; i++) {
1154        destination.push(copy(source[i], null, stackSource, stackDest));
1155      }
1156    } else {
1157      var h = destination.$$hashKey;
1158      if (isArray(destination)) {
1159        destination.length = 0;
1160      } else {
vendor: 15,070 bytes, lines 1161-1659
1161        forEach(destination, function(value, key) {
1162          delete destination[key];
1163        });
1164      }
1165      if (isBlankObject(source)) {
1166        // createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty
1167        for (key in source) {
1168          destination[key] = copy(source[key], null, stackSource, stackDest);
1169        }
1170      } else if (source && typeof source.hasOwnProperty === 'function') {
1171        // Slow path, which must rely on hasOwnProperty
1172        for (key in source) {
1173          if (source.hasOwnProperty(key)) {
1174            destination[key] = copy(source[key], null, stackSource, stackDest);
1175          }
1176        }
1177      } else {
1178        // Slowest path --- hasOwnProperty can't be called as a method
1179        for (key in source) {
1180          if (hasOwnProperty.call(source, key)) {
1181            destination[key] = copy(source[key], null, stackSource, stackDest);
1182          }
1183        }
1184      }
1185      setHashKey(destination,h);
1186    }
1187  }
1188  return destination;
1189}
1190
1191/**
1192 * Creates a shallow copy of an object, an array or a primitive.
1193 *
1194 * Assumes that there are no proto properties for objects.
1195 */
1196function shallowCopy(src, dst) {
1197  if (isArray(src)) {
1198    dst = dst || [];
1199
1200    for (var i = 0, ii = src.length; i < ii; i++) {
1201      dst[i] = src[i];
1202    }
1203  } else if (isObject(src)) {
1204    dst = dst || {};
1205
1206    for (var key in src) {
1207      if (!(key.charAt(0) === '$' && key.charAt(1) === '$')) {
1208        dst[key] = src[key];
1209      }
1210    }
1211  }
1212
1213  return dst || src;
1214}
1215
1216
1217/**
1218 * @ngdoc function
1219 * @name angular.equals
1220 * @module ng
1221 * @kind function
1222 *
1223 * @description
1224 * Determines if two objects or two values are equivalent. Supports value types, regular
1225 * expressions, arrays and objects.
1226 *
1227 * Two objects or values are considered equivalent if at least one of the following is true:
1228 *
1229 * * Both objects or values pass `===` comparison.
1230 * * Both objects or values are of the same type and all of their properties are equal by
1231 *   comparing them with `angular.equals`.
1232 * * Both values are NaN. (In JavaScript, NaN == NaN => false. But we consider two NaN as equal)
1233 * * Both values represent the same regular expression (In JavaScript,
1234 *   /abc/ == /abc/ => false. But we consider two regular expressions as equal when their textual
1235 *   representation matches).
1236 *
1237 * During a property comparison, properties of `function` type and properties with names
1238 * that begin with `$` are ignored.
1239 *
1240 * Scope and DOMWindow objects are being compared only by identify (`===`).
1241 *
1242 * @param {*} o1 Object or value to compare.
1243 * @param {*} o2 Object or value to compare.
1244 * @returns {boolean} True if arguments are equal.
1245 */
1246function equals(o1, o2) {
1247  if (o1 === o2) return true;
1248  if (o1 === null || o2 === null) return false;
1249  if (o1 !== o1 && o2 !== o2) return true; // NaN === NaN
1250  var t1 = typeof o1, t2 = typeof o2, length, key, keySet;
1251  if (t1 == t2) {
1252    if (t1 == 'object') {
1253      if (isArray(o1)) {
1254        if (!isArray(o2)) return false;
1255        if ((length = o1.length) == o2.length) {
1256          for (key = 0; key < length; key++) {
1257            if (!equals(o1[key], o2[key])) return false;
1258          }
1259          return true;
1260        }
1261      } else if (isDate(o1)) {
1262        if (!isDate(o2)) return false;
1263        return equals(o1.getTime(), o2.getTime());
1264      } else if (isRegExp(o1)) {
1265        return isRegExp(o2) ? o1.toString() == o2.toString() : false;
1266      } else {
1267        if (isScope(o1) || isScope(o2) || isWindow(o1) || isWindow(o2) ||
1268          isArray(o2) || isDate(o2) || isRegExp(o2)) return false;
1269        keySet = createMap();
1270        for (key in o1) {
1271          if (key.charAt(0) === '$' || isFunction(o1[key])) continue;
1272          if (!equals(o1[key], o2[key])) return false;
1273          keySet[key] = true;
1274        }
1275        for (key in o2) {
1276          if (!(key in keySet) &&
1277              key.charAt(0) !== '$' &&
1278              isDefined(o2[key]) &&
1279              !isFunction(o2[key])) return false;
1280        }
1281        return true;
1282      }
1283    }
1284  }
1285  return false;
1286}
1287
1288var csp = function() {
1289  if (!isDefined(csp.rules)) {
1290
1291
1292    var ngCspElement = (document.querySelector('[ng-csp]') ||
1293                    document.querySelector('[data-ng-csp]'));
1294
1295    if (ngCspElement) {
1296      var ngCspAttribute = ngCspElement.getAttribute('ng-csp') ||
1297                    ngCspElement.getAttribute('data-ng-csp');
1298      csp.rules = {
1299        noUnsafeEval: !ngCspAttribute || (ngCspAttribute.indexOf('no-unsafe-eval') !== -1),
1300        noInlineStyle: !ngCspAttribute || (ngCspAttribute.indexOf('no-inline-style') !== -1)
1301      };
1302    } else {
1303      csp.rules = {
1304        noUnsafeEval: noUnsafeEval(),
1305        noInlineStyle: false
1306      };
1307    }
1308  }
1309
1310  return csp.rules;
1311
1312  function noUnsafeEval() {
1313    try {
1314      /* jshint -W031, -W054 */
1315      new Function('');
1316      /* jshint +W031, +W054 */
1317      return false;
1318    } catch (e) {
1319      return true;
1320    }
1321  }
1322};
1323
1324/**
1325 * @ngdoc directive
1326 * @module ng
1327 * @name ngJq
1328 *
1329 * @element ANY
1330 * @param {string=} ngJq the name of the library available under `window`
1331 * to be used for angular.element
1332 * @description
1333 * Use this directive to force the angular.element library.  This should be
1334 * used to force either jqLite by leaving ng-jq blank or setting the name of
1335 * the jquery variable under window (eg. jQuery).
1336 *
1337 * Since angular looks for this directive when it is loaded (doesn't wait for the
1338 * DOMContentLoaded event), it must be placed on an element that comes before the script
1339 * which loads angular. Also, only the first instance of `ng-jq` will be used and all
1340 * others ignored.
1341 *
1342 * @example
1343 * This example shows how to force jqLite using the `ngJq` directive to the `html` tag.
1344 ```html
1345 <!doctype html>
1346 <html ng-app ng-jq>
1347 ...
1348 ...
1349 </html>
1350 ```
1351 * @example
1352 * This example shows how to use a jQuery based library of a different name.
1353 * The library name must be available at the top most 'window'.
1354 ```html
1355 <!doctype html>
1356 <html ng-app ng-jq="jQueryLib">
1357 ...
1358 ...
1359 </html>
1360 ```
1361 */
1362var jq = function() {
1363  if (isDefined(jq.name_)) return jq.name_;
1364  var el;
1365  var i, ii = ngAttrPrefixes.length, prefix, name;
1366  for (i = 0; i < ii; ++i) {
1367    prefix = ngAttrPrefixes[i];
1368    if (el = document.querySelector('[' + prefix.replace(':', '\\:') + 'jq]')) {
1369      name = el.getAttribute(prefix + 'jq');
1370      break;
1371    }
1372  }
1373
1374  return (jq.name_ = name);
1375};
1376
1377function concat(array1, array2, index) {
1378  return array1.concat(slice.call(array2, index));
1379}
1380
1381function sliceArgs(args, startIndex) {
1382  return slice.call(args, startIndex || 0);
1383}
1384
1385
1386/* jshint -W101 */
1387/**
1388 * @ngdoc function
1389 * @name angular.bind
1390 * @module ng
1391 * @kind function
1392 *
1393 * @description
1394 * Returns a function which calls function `fn` bound to `self` (`self` becomes the `this` for
1395 * `fn`). You can supply optional `args` that are prebound to the function. This feature is also
1396 * known as [partial application](http://en.wikipedia.org/wiki/Partial_application), as
1397 * distinguished from [function currying](http://en.wikipedia.org/wiki/Currying#Contrast_with_partial_function_application).
1398 *
1399 * @param {Object} self Context which `fn` should be evaluated in.
1400 * @param {function()} fn Function to be bound.
1401 * @param {...*} args Optional arguments to be prebound to the `fn` function call.
1402 * @returns {function()} Function that wraps the `fn` with all the specified bindings.
1403 */
1404/* jshint +W101 */
1405function bind(self, fn) {
1406  var curryArgs = arguments.length > 2 ? sliceArgs(arguments, 2) : [];
1407  if (isFunction(fn) && !(fn instanceof RegExp)) {
1408    return curryArgs.length
1409      ? function() {
1410          return arguments.length
1411            ? fn.apply(self, concat(curryArgs, arguments, 0))
1412            : fn.apply(self, curryArgs);
1413        }
1414      : function() {
1415          return arguments.length
1416            ? fn.apply(self, arguments)
1417            : fn.call(self);
1418        };
1419  } else {
1420    // in IE, native methods are not functions so they cannot be bound (note: they don't need to be)
1421    return fn;
1422  }
1423}
1424
1425
1426function toJsonReplacer(key, value) {
1427  var val = value;
1428
1429  if (typeof key === 'string' && key.charAt(0) === '$' && key.charAt(1) === '$') {
1430    val = undefined;
1431  } else if (isWindow(value)) {
1432    val = '$WINDOW';
1433  } else if (value &&  document === value) {
1434    val = '$DOCUMENT';
1435  } else if (isScope(value)) {
1436    val = '$SCOPE';
1437  }
1438
1439  return val;
1440}
1441
1442
1443/**
1444 * @ngdoc function
1445 * @name angular.toJson
1446 * @module ng
1447 * @kind function
1448 *
1449 * @description
1450 * Serializes input into a JSON-formatted string. Properties with leading $$ characters will be
1451 * stripped since angular uses this notation internally.
1452 *
1453 * @param {Object|Array|Date|string|number} obj Input to be serialized into JSON.
1454 * @param {boolean|number} [pretty=2] If set to true, the JSON output will contain newlines and whitespace.
1455 *    If set to an integer, the JSON output will contain that many spaces per indentation.
1456 * @returns {string|undefined} JSON-ified string representing `obj`.
1457 */
1458function toJson(obj, pretty) {
1459  if (typeof obj === 'undefined') return undefined;
1460  if (!isNumber(pretty)) {
1461    pretty = pretty ? 2 : null;
1462  }
1463  return JSON.stringify(obj, toJsonReplacer, pretty);
1464}
1465
1466
1467/**
1468 * @ngdoc function
1469 * @name angular.fromJson
1470 * @module ng
1471 * @kind function
1472 *
1473 * @description
1474 * Deserializes a JSON string.
1475 *
1476 * @param {string} json JSON string to deserialize.
1477 * @returns {Object|Array|string|number} Deserialized JSON string.
1478 */
1479function fromJson(json) {
1480  return isString(json)
1481      ? JSON.parse(json)
1482      : json;
1483}
1484
1485
1486function timezoneToOffset(timezone, fallback) {
1487  var requestedTimezoneOffset = Date.parse('Jan 01, 1970 00:00:00 ' + timezone) / 60000;
1488  return isNaN(requestedTimezoneOffset) ? fallback : requestedTimezoneOffset;
1489}
1490
1491
1492function addDateMinutes(date, minutes) {
1493  date = new Date(date.getTime());
1494  date.setMinutes(date.getMinutes() + minutes);
1495  return date;
1496}
1497
1498
1499function convertTimezoneToLocal(date, timezone, reverse) {
1500  reverse = reverse ? -1 : 1;
1501  var timezoneOffset = timezoneToOffset(timezone, date.getTimezoneOffset());
1502  return addDateMinutes(date, reverse * (timezoneOffset - date.getTimezoneOffset()));
1503}
1504
1505
1506/**
1507 * @returns {string} Returns the string representation of the element.
1508 */
1509function startingTag(element) {
1510  element = jqLite(element).clone();
1511  try {
1512    // turns out IE does not let you set .html() on elements which
1513    // are not allowed to have children. So we just ignore it.
1514    element.empty();
1515  } catch (e) {}
1516  var elemHtml = jqLite('<div>').append(element).html();
1517  try {
1518    return element[0].nodeType === NODE_TYPE_TEXT ? lowercase(elemHtml) :
1519        elemHtml.
1520          match(/^(<[^>]+>)/)[1].
1521          replace(/^<([\w\-]+)/, function(match, nodeName) { return '<' + lowercase(nodeName); });
1522  } catch (e) {
1523    return lowercase(elemHtml);
1524  }
1525
1526}
1527
1528
1529/////////////////////////////////////////////////
1530
1531/**
1532 * Tries to decode the URI component without throwing an exception.
1533 *
1534 * @private
1535 * @param str value potential URI component to check.
1536 * @returns {boolean} True if `value` can be decoded
1537 * with the decodeURIComponent function.
1538 */
1539function tryDecodeURIComponent(value) {
1540  try {
1541    return decodeURIComponent(value);
1542  } catch (e) {
1543    // Ignore any invalid uri component
1544  }
1545}
1546
1547
1548/**
1549 * Parses an escaped url query string into key-value pairs.
1550 * @returns {Object.<string,boolean|Array>}
1551 */
1552function parseKeyValue(/**string*/keyValue) {
1553  var obj = {};
1554  forEach((keyValue || "").split('&'), function(keyValue) {
1555    var splitPoint, key, val;
1556    if (keyValue) {
1557      key = keyValue = keyValue.replace(/\+/g,'%20');
1558      splitPoint = keyValue.indexOf('=');
1559      if (splitPoint !== -1) {
1560        key = keyValue.substring(0, splitPoint);
1561        val = keyValue.substring(splitPoint + 1);
1562      }
1563      key = tryDecodeURIComponent(key);
1564      if (isDefined(key)) {
1565        val = isDefined(val) ? tryDecodeURIComponent(val) : true;
1566        if (!hasOwnProperty.call(obj, key)) {
1567          obj[key] = val;
1568        } else if (isArray(obj[key])) {
1569          obj[key].push(val);
1570        } else {
1571          obj[key] = [obj[key],val];
1572        }
1573      }
1574    }
1575  });
1576  return obj;
1577}
1578
1579function toKeyValue(obj) {
1580  var parts = [];
1581  forEach(obj, function(value, key) {
1582    if (isArray(value)) {
1583      forEach(value, function(arrayValue) {
1584        parts.push(encodeUriQuery(key, true) +
1585                   (arrayValue === true ? '' : '=' + encodeUriQuery(arrayValue, true)));
1586      });
1587    } else {
1588    parts.push(encodeUriQuery(key, true) +
1589               (value === true ? '' : '=' + encodeUriQuery(value, true)));
1590    }
1591  });
1592  return parts.length ? parts.join('&') : '';
1593}
1594
1595
1596/**
1597 * We need our custom method because encodeURIComponent is too aggressive and doesn't follow
1598 * http://www.ietf.org/rfc/rfc3986.txt with regards to the character set (pchar) allowed in path
1599 * segments:
1600 *    segment       = *pchar
1601 *    pchar         = unreserved / pct-encoded / sub-delims / ":" / "@"
1602 *    pct-encoded   = "%" HEXDIG HEXDIG
1603 *    unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
1604 *    sub-delims    = "!" / "$" / "&" / "'" / "(" / ")"
1605 *                     / "*" / "+" / "," / ";" / "="
1606 */
1607function encodeUriSegment(val) {
1608  return encodeUriQuery(val, true).
1609             replace(/%26/gi, '&').
1610             replace(/%3D/gi, '=').
1611             replace(/%2B/gi, '+');
1612}
1613
1614
1615/**
1616 * This method is intended for encoding *key* or *value* parts of query component. We need a custom
1617 * method because encodeURIComponent is too aggressive and encodes stuff that doesn't have to be
1618 * encoded per http://tools.ietf.org/html/rfc3986:
1619 *    query       = *( pchar / "/" / "?" )
1620 *    pchar         = unreserved / pct-encoded / sub-delims / ":" / "@"
1621 *    unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
1622 *    pct-encoded   = "%" HEXDIG HEXDIG
1623 *    sub-delims    = "!" / "$" / "&" / "'" / "(" / ")"
1624 *                     / "*" / "+" / "," / ";" / "="
1625 */
1626function encodeUriQuery(val, pctEncodeSpaces) {
1627  return encodeURIComponent(val).
1628             replace(/%40/gi, '@').
1629             replace(/%3A/gi, ':').
1630             replace(/%24/g, '$').
1631             replace(/%2C/gi, ',').
1632             replace(/%3B/gi, ';').
1633             replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
1634}
1635
1636var ngAttrPrefixes = ['ng-', 'data-ng-', 'ng:', 'x-ng-'];
1637
1638function getNgAttribute(element, ngAttr) {
1639  var attr, i, ii = ngAttrPrefixes.length;
1640  for (i = 0; i < ii; ++i) {
1641    attr = ngAttrPrefixes[i] + ngAttr;
1642    if (isString(attr = element.getAttribute(attr))) {
1643      return attr;
1644    }
1645  }
1646  return null;
1647}
1648
1649/**
1650 * @ngdoc directive
1651 * @name ngApp
1652 * @module ng
1653 *
1654 * @element ANY
1655 * @param {angular.Module} ngApp an optional application
1656 *   {@link angular.module module} name to load.
1657 * @param {boolean=} ngStrictDi if this attribute is present on the app element, the injector will be
1658 *   created in "strict-di" mode. This means that the application will fail to invoke functions which
1659 *   do not use explicit function annotation (and are thus unsuitable for minification), as de
1659scribed
1660 *   in {@link guide/di the Dependency Injection guide}, and useful debugging info will assist in
1661 *   tracking down the root of these bugs.
1662 *
1663 * @description
1664 *
1665 * Use this directive to **auto-bootstrap** an AngularJS application. The `ngApp` directive
1666 * designates the **root element** of the application and is typically placed near the root element
1667 * of the page - e.g. on the `<body>` or `<html>` tags.
1668 *
1669 * Only one AngularJS application can be auto-bootstrapped per HTML document. The first `ngApp`
1670 * found in the document will be used to define the root element to auto-bootstrap as an
1671 * application. To run multiple applications in an HTML document you must manually bootstrap them using
1672 * {@link angular.bootstrap} instead. AngularJS applications cannot be nested within each other.
1673 *
1674 * You can specify an **AngularJS module** to be used as the root module for the application.  This
1675 * module will be loaded into the {@link auto.$injector} when the application is bootstrapped. It
1676 * should contain the application code needed or have dependencies on other modules that will
1677 * contain the code. See {@link angular.module} for more information.
1678 *
1679 * In the example below if the `ngApp` directive were not placed on the `html` element then the
1680 * document would not be compiled, the `AppController` would not be instantiated and the `{{ a+b }}`
1681 * would not be resolved to `3`.
1682 *
1683 * `ngApp` is the easiest, and most common way to bootstrap an application.
1684 *
1685 <example module="ngAppDemo">
1686   <file name="index.html">
1687   <div ng-controller="ngAppDemoController">
1688     I can add: {{a}} + {{b}} =  {{ a+b }}
1689   </div>
1690   </file>
1691   <file name="script.js">
1692   angular.module('ngAppDemo', []).controller('ngAppDemoController', function($scope) {
1693     $scope.a = 1;
1694     $scope.b = 2;
1695   });
1696   </file>
1697 </example>
1698 *
1699 * Using `ngStrictDi`, you would see something like this:
1700 *
1701 <example ng-app-included="true">
1702   <file name="index.html">
1703   <div ng-app="ngAppStrictDemo" ng-strict-di>
1704       <div ng-controller="GoodController1">
1705           I can add: {{a}} + {{b}} =  {{ a+b }}
1706
1707           <p>This renders because the controller does not fail to
1708              instantiate, by using explicit annotation style (see
1709              script.js for details)
1710           </p>
1711       </div>
1712
1713       <div ng-controller="GoodController2">
1714           Name: <input ng-model="name"><br />
1715           Hello, {{name}}!
1716
1717           <p>This renders because the controller does not fail to
1718              instantiate, by using explicit annotation style
1719              (see script.js for details)
1720           </p>
1721       </div>
1722
1723       <div ng-controller="BadController">
1724           I can add: {{a}} + {{b}} =  {{ a+b }}
1725
1726           <p>The controller could not be instantiated, due to relying
1727              on automatic function annotations (which are disabled in
1728              strict mode). As such, the content of this section is not
1729              interpolated, and there should be an error in your web console.
1730           </p>
1731       </div>
1732   </div>
1733   </file>
1734   <file name="script.js">
1735   angular.module('ngAppStrictDemo', [])
1736     // BadController will fail to instantiate, due to relying on automatic function annotation,
1737     // rather than an explicit annotation
1738     .controller('BadController', function($scope) {
1739       $scope.a = 1;
1740       $scope.b = 2;
1741     })
1742     // Unlike BadController, GoodController1 and GoodController2 will not fail to be instantiated,
1743     // due to using explicit annotations using the array style and $inject property, respectively.
1744     .controller('GoodController1', ['$scope', function($scope) {
1745       $scope.a = 1;
1746       $scope.b = 2;
1747     }])
1748     .controller('GoodController2', GoodController2);
1749     function GoodController2($scope) {
1750       $scope.name = "World";
1751     }
1752     GoodController2.$inject = ['$scope'];
1753   </file>
1754   <file name="style.css">
1755   div[ng-controller] {
1756       margin-bottom: 1em;
1757       -webkit-border-radius: 4px;
1758       border-radius: 4px;
1759       border: 1px solid;
1760       padding: .5em;
1761   }
1762   div[ng-controller^=Good] {
1763       border-color: #d6e9c6;
1764       background-color: #dff0d8;
1765       color: #3c763d;
1766   }
1767   div[ng-controller^=Bad] {
1768       border-color: #ebccd1;
1769       background-color: #f2dede;
1770       color: #a94442;
1771       margin-bottom: 0;
1772   }
1773   </file>
1774 </example>
1775 */
1776function angularInit(element, bootstrap) {
1777  var appElement,
1778      module,
1779      config = {};
1780
1781  // The element `element` has priority over any other element
1782  forEach(ngAttrPrefixes, function(prefix) {
1783    var name = prefix + 'app';
1784
1785    if (!appElement && element.hasAttribute && element.hasAttribute(name)) {
1786      appElement = element;
1787      module = element.getAttribute(name);
1788    }
1789  });
1790  forEach(ngAttrPrefixes, function(prefix) {
1791    var name = prefix + 'app';
1792    var candidate;
1793
1794    if (!appElement && (candidate = element.querySelector('[' + name.replace(':', '\\:') + ']'))) {
1795      appElement = candidate;
1796      module = candidate.getAttribute(name);
1797    }
1798  });
1799  if (appElement) {
1800    config.strictDi = getNgAttribute(appElement, "strict-di") !== null;
1801    bootstrap(appElement, module ? [module] : [], config);
1802  }
1803}
1804
1805/**
1806 * @ngdoc function
1807 * @name angular.bootstrap
1808 * @module ng
1809 * @description
1810 * Use this function to manually start up angular application.
1811 *
1812 * See: {@link guide/bootstrap Bootstrap}
1813 *
1814 * Note that Protractor based end-to-end tests cannot use this function to bootstrap manually.
1815 * They must use {@link ng.directive:ngApp ngApp}.
1816 *
1817 * Angular will detect if it has been loaded into the browser more than once and only allow the
1818 * first loaded script to be bootstrapped and will report a warning to the browser console for
1819 * each of the subsequent scripts. This prevents strange results in applications, where otherwise
1820 * multiple instances of Angular try to work on the DOM.
1821 *
1822 * ```html
1823 * <!doctype html>
1824 * <html>
1825 * <body>
1826 * <div ng-controller="WelcomeController">
1827 *   {{greeting}}
1828 * </div>
1829 *
1830 * <script src="angular.js"></script>
1831 * <script>
1832 *   var app = angular.module('demo', [])
1833 *   .controller('WelcomeController', function($scope) {
1834 *       $scope.greeting = 'Welcome!';
1835 *   });
1836 *   angular.bootstrap(document, ['demo']);
1837 * </script>
1838 * </body>
1839 * </html>
1840 * ```
1841 *
1842 * @param {DOMElement} element DOM element which is the root of angular application.
1843 * @param {Array<String|Function|Array>=} modules an array of modules to load into the application.
1844 *     Each item in the array should be the name of a predefined module or a (DI annotated)
1845 *     function that will be invoked by the injector as a `config` block.
1846 *     See: {@link angular.module modules}
1847 * @param {Object=} config an object for defining configuration options for the application. The
1848 *     following keys are supported:
1849 *
1850 * * `strictDi` - disable automatic function annotation for the application. This is meant to
1851 *   assist in finding bugs which break minified code. Defaults to `false`.
1852 *
1853 * @returns {auto.$injector} Returns the newly created injector for this app.
1854 */
1855function bootstrap(element, modules, config) {
1856  if (!isObject(config)) config = {};
1857  var defaultConfig = {
1858    strictDi: false
1859  };
1860  config = extend(defaultConfig, config);
1861  var doBootstrap = function() {
1862    element = jqLite(element);
1863
1864    if (element.injector()) {
1865      var tag = (element[0] === document) ? 'document' : startingTag(element);
1866      //Encode angle brackets to prevent input from being sanitized to empty string #8683
1867      throw ngMinErr(
1868          'btstrpd',
1869          "App Already Bootstrapped with this Element '{0}'",
1870          tag.replace(/</,'&lt;').replace(/>/,'&gt;'));
1871    }
1872
1873    modules = modules || [];
1874    modules.unshift(['$provide', function($provide) {
1875      $provide.value('$rootElement', element);
1876    }]);
1877
1878    if (config.debugInfoEnabled) {
1879      // Pushing so that this overrides `debugInfoEnabled` setting defined in user's `modules`.
1880      modules.push(['$compileProvider', function($compileProvider) {
1881        $compileProvider.debugInfoEnabled(true);
1882      }]);
1883    }
1884
1885    modules.unshift('ng');
1886    var injector = createInjector(modules, config.strictDi);
1887    injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector',
1888       function bootstrapApply(scope, element, compile, injector) {
1889        scope.$apply(function() {
1890          element.data('$injector', injector);
1891          compile(element)(scope);
1892        });
1893      }]
1894    );
1895    return injector;
1896  };
1897
1898  var NG_ENABLE_DEBUG_INFO = /^NG_ENABLE_DEBUG_INFO!/;
1899  var NG_DEFER_BOOTSTRAP = /^NG_DEFER_BOOTSTRAP!/;
1900
1901  if (window && NG_ENABLE_DEBUG_INFO.test(window.name)) {
1902    config.debugInfoEnabled = true;
1903    window.name = window.name.replace(NG_ENABLE_DEBUG_INFO, '');
1904  }
1905
1906  if (window && !NG_DEFER_BOOTSTRAP.test(window.name)) {
1907    return doBootstrap();
1908  }
1909
1910  window.name = window.name.replace(NG_DEFER_BOOTSTRAP, '');
1911  angular.resumeBootstrap = function(extraModules) {
1912    forEach(extraModules, function(module) {
1913      modules.push(module);
1914    });
1915    return doBootstrap();
1916  };
1917
1918  if (isFunction(angular.resumeDeferredBootstrap)) {
1919    angular.resumeDeferredBootstrap();
1920  }
1921}
1922
1923/**
1924 * @ngdoc function
1925 * @name angular.reloadWithDebugInfo
1926 * @module ng
1927 * @description
1928 * Use this function to reload the current application with debug information turned on.
1929 * This takes precedence over a call to `$compileProvider.debugInfoEnabled(false)`.
1930 *
1931 * See {@link ng.$compileProvider#debugInfoEnabled} for more.
1932 */
1933function reloadWithDebugInfo() {
1934  window.name = 'NG_ENABLE_DEBUG_INFO!' + window.name;
1935  window.location.reload();
1936}
1937
1938/**
1939 * @name angular.getTestability
1940 * @module ng
1941 * @description
1942 * Get the testability service for the instance of Angular on the given
1943 * element.
1944 * @param {DOMElement} element DOM element which is the root of angular application.
1945 */
1946function getTestability(rootElement) {
1947  var injector = angular.element(rootElement).injector();
1948  if (!injector) {
1949    throw ngMinErr('test',
1950      'no injector found for element argument to getTestability');
1951  }
1952  return injector.get('$$testability');
1953}
1954
1955var SNAKE_CASE_REGEXP = /[A-Z]/g;
1956function snake_case(name, separator) {
1957  separator = separator || '_';
1958  return name.replace(SNAKE_CASE_REGEXP, function(letter, pos) {
1959    return (pos ? separator : '') + letter.toLowerCase();
1960  });
1961}
1962
1963var bindJQueryFired = false;
vendor: 9,099 bytes, lines 1964-2253
1964var skipDestroyOnNextJQueryCleanData;
1965function bindJQuery() {
1966  var originalCleanData;
1967
1968  if (bindJQueryFired) {
1969    return;
1970  }
1971
1972  // bind to jQuery if present;
1973  var jqName = jq();
1974  jQuery = isUndefined(jqName) ? window.jQuery :   // use jQuery (if present)
1975           !jqName             ? undefined     :   // use jqLite
1976                                 window[jqName];   // use jQuery specified by `ngJq`
1977
1978  // Use jQuery if it exists with proper functionality, otherwise default to us.
1979  // Angular 1.2+ requires jQuery 1.7+ for on()/off() support.
1980  // Angular 1.3+ technically requires at least jQuery 2.1+ but it may work with older
1981  // versions. It will not work for sure with jQuery <1.7, though.
1982  if (jQuery && jQuery.fn.on) {
1983    jqLite = jQuery;
1984    extend(jQuery.fn, {
1985      scope: JQLitePrototype.scope,
1986      isolateScope: JQLitePrototype.isolateScope,
1987      controller: JQLitePrototype.controller,
1988      injector: JQLitePrototype.injector,
1989      inheritedData: JQLitePrototype.inheritedData
1990    });
1991
1992    // All nodes removed from the DOM via various jQuery APIs like .remove()
1993    // are passed through jQuery.cleanData. Monkey-patch this method to fire
1994    // the $destroy event on all removed nodes.
1995    originalCleanData = jQuery.cleanData;
1996    jQuery.cleanData = function(elems) {
1997      var events;
1998      if (!skipDestroyOnNextJQueryCleanData) {
1999        for (var i = 0, elem; (elem = elems[i]) != null; i++) {
2000          events = jQuery._data(elem, "events");
2001          if (events && events.$destroy) {
2002            jQuery(elem).triggerHandler('$destroy');
2003          }
2004        }
2005      } else {
2006        skipDestroyOnNextJQueryCleanData = false;
2007      }
2008      originalCleanData(elems);
2009    };
2010  } else {
2011    jqLite = JQLite;
2012  }
2013
2014  angular.element = jqLite;
2015
2016  // Prevent double-proxying.
2017  bindJQueryFired = true;
2018}
2019
2020/**
2021 * throw error if the argument is falsy.
2022 */
2023function assertArg(arg, name, reason) {
2024  if (!arg) {
2025    throw ngMinErr('areq', "Argument '{0}' is {1}", (name || '?'), (reason || "required"));
2026  }
2027  return arg;
2028}
2029
2030function assertArgFn(arg, name, acceptArrayAnnotation) {
2031  if (acceptArrayAnnotation && isArray(arg)) {
2032      arg = arg[arg.length - 1];
2033  }
2034
2035  assertArg(isFunction(arg), name, 'not a function, got ' +
2036      (arg && typeof arg === 'object' ? arg.constructor.name || 'Object' : typeof arg));
2037  return arg;
2038}
2039
2040/**
2041 * throw error if the name given is hasOwnProperty
2042 * @param  {String} name    the name to test
2043 * @param  {String} context the context in which the name is used, such as module or directive
2044 */
2045function assertNotHasOwnProperty(name, context) {
2046  if (name === 'hasOwnProperty') {
2047    throw ngMinErr('badname', "hasOwnProperty is not a valid {0} name", context);
2048  }
2049}
2050
2051/**
2052 * Return the value accessible from the object by path. Any undefined traversals are ignored
2053 * @param {Object} obj starting object
2054 * @param {String} path path to traverse
2055 * @param {boolean} [bindFnToScope=true]
2056 * @returns {Object} value as accessible by path
2057 */
2058//TODO(misko): this function needs to be removed
2059function getter(obj, path, bindFnToScope) {
2060  if (!path) return obj;
2061  var keys = path.split('.');
2062  var key;
2063  var lastInstance = obj;
2064  var len = keys.length;
2065
2066  for (var i = 0; i < len; i++) {
2067    key = keys[i];
2068    if (obj) {
2069      obj = (lastInstance = obj)[key];
2070    }
2071  }
2072  if (!bindFnToScope && isFunction(obj)) {
2073    return bind(lastInstance, obj);
2074  }
2075  return obj;
2076}
2077
2078/**
2079 * Return the DOM siblings between the first and last node in the given array.
2080 * @param {Array} array like object
2081 * @returns {Array} the inputted object or a jqLite collection containing the nodes
2082 */
2083function getBlockNodes(nodes) {
2084  // TODO(perf): update `nodes` instead of creating a new object?
2085  var node = nodes[0];
2086  var endNode = nodes[nodes.length - 1];
2087  var blockNodes;
2088
2089  for (var i = 1; node !== endNode && (node = node.nextSibling); i++) {
2090    if (blockNodes || nodes[i] !== node) {
2091      if (!blockNodes) {
2092        blockNodes = jqLite(slice.call(nodes, 0, i));
2093      }
2094      blockNodes.push(node);
2095    }
2096  }
2097
2098  return blockNodes || nodes;
2099}
2100
2101
2102/**
2103 * Creates a new object without a prototype. This object is useful for lookup without having to
2104 * guard against prototypically inherited properties via hasOwnProperty.
2105 *
2106 * Related micro-benchmarks:
2107 * - http://jsperf.com/object-create2
2108 * - http://jsperf.com/proto-map-lookup/2
2109 * - http://jsperf.com/for-in-vs-object-keys2
2110 *
2111 * @returns {Object}
2112 */
2113function createMap() {
2114  return Object.create(null);
2115}
2116
2117var NODE_TYPE_ELEMENT = 1;
2118var NODE_TYPE_ATTRIBUTE = 2;
2119var NODE_TYPE_TEXT = 3;
2120var NODE_TYPE_COMMENT = 8;
2121var NODE_TYPE_DOCUMENT = 9;
2122var NODE_TYPE_DOCUMENT_FRAGMENT = 11;
2123
2124/**
2125 * @ngdoc type
2126 * @name angular.Module
2127 * @module ng
2128 * @description
2129 *
2130 * Interface for configuring angular {@link angular.module modules}.
2131 */
2132
2133function setupModuleLoader(window) {
2134
2135  var $injectorMinErr = minErr('$injector');
2136  var ngMinErr = minErr('ng');
2137
2138  function ensure(obj, name, factory) {
2139    return obj[name] || (obj[name] = factory());
2140  }
2141
2142  var angular = ensure(window, 'angular', Object);
2143
2144  // We need to expose `angular.$$minErr` to modules such as `ngResource` that reference it during bootstrap
2145  angular.$$minErr = angular.$$minErr || minErr;
2146
2147  return ensure(angular, 'module', function() {
2148    /** @type {Object.<string, angular.Module>} */
2149    var modules = {};
2150
2151    /**
2152     * @ngdoc function
2153     * @name angular.module
2154     * @module ng
2155     * @description
2156     *
2157     * The `angular.module` is a global place for creating, registering and retrieving Angular
2158     * modules.
2159     * All modules (angular core or 3rd party) that should be available to an application must be
2160     * registered using this mechanism.
2161     *
2162     * Passing one argument retrieves an existing {@link angular.Module},
2163     * whereas passing more than one argument creates a new {@link angular.Module}
2164     *
2165     *
2166     * # Module
2167     *
2168     * A module is a collection of services, directives, controllers, filters, and configuration information.
2169     * `angular.module` is used to configure the {@link auto.$injector $injector}.
2170     *
2171     * ```js
2172     * // Create a new module
2173     * var myModule = angular.module('myModule', []);
2174     *
2175     * // register a new service
2176     * myModule.value('appName', 'MyCoolApp');
2177     *
2178     * // configure existing services inside initialization blocks.
2179     * myModule.config(['$locationProvider', function($locationProvider) {
2180     *   // Configure existing providers
2181     *   $locationProvider.hashPrefix('!');
2182     * }]);
2183     * ```
2184     *
2185     * Then you can create an injector and load your modules like this:
2186     *
2187     * ```js
2188     * var injector = angular.injector(['ng', 'myModule'])
2189     * ```
2190     *
2191     * However it's more likely that you'll just use
2192     * {@link ng.directive:ngApp ngApp} or
2193     * {@link angular.bootstrap} to simplify this process for you.
2194     *
2195     * @param {!string} name The name of the module to create or retrieve.
2196     * @param {!Array.<string>=} requires If specified then new module is being created. If
2197     *        unspecified then the module is being retrieved for further configuration.
2198     * @param {Function=} configFn Optional configuration function for the module. Same as
2199     *        {@link angular.Module#config Module#config()}.
2200     * @returns {module} new module with the {@link angular.Module} api.
2201     */
2202    return function module(name, requires, configFn) {
2203      var assertNotHasOwnProperty = function(name, context) {
2204        if (name === 'hasOwnProperty') {
2205          throw ngMinErr('badname', 'hasOwnProperty is not a valid {0} name', context);
2206        }
2207      };
2208
2209      assertNotHasOwnProperty(name, 'module');
2210      if (requires && modules.hasOwnProperty(name)) {
2211        modules[name] = null;
2212      }
2213      return ensure(modules, name, function() {
2214        if (!requires) {
2215          throw $injectorMinErr('nomod', "Module '{0}' is not available! You either misspelled " +
2216             "the module name or forgot to load it. If registering a module ensure that you " +
2217             "specify the dependencies as the second argument.", name);
2218        }
2219
2220        /** @type {!Array.<Array.<*>>} */
2221        var invokeQueue = [];
2222
2223        /** @type {!Array.<Function>} */
2224        var configBlocks = [];
2225
2226        /** @type {!Array.<Function>} */
2227        var runBlocks = [];
2228
2229        var config = invokeLater('$injector', 'invoke', 'push', configBlocks);
2230
2231        /** @type {angular.Module} */
2232        var moduleInstance = {
2233          // Private state
2234          _invokeQueue: invokeQueue,
2235          _configBlocks: configBlocks,
2236          _runBlocks: runBlocks,
2237
2238          /**
2239           * @ngdoc property
2240           * @name angular.Module#requires
2241           * @module ng
2242           *
2243           * @description
2244           * Holds the list of modules which the injector will load before the current module is
2245           * loaded.
2246           */
2247          requires: requires,
2248
2249          /**
2250           * @ngdoc property
2251           * @name angular.Module#name
2252           * @module ng
2253           *
2254           * @description
2255           * Name of the module.
2256           */
2257          name: name,
2258
2259
2260          /**
2261           * @ngdoc method
2262           * @name angular.Module#provider
2263           * @module ng
2264           * @param {string} name service name
2265           * @param {Function} providerType Construction function for creating new instance of the
2266           *                                service.
2267           * @description
2268           * See {@link auto.$provide#provider $provide.provider()}.
2269           */
2270          provider: invokeLaterAndSetModuleName('$provide', 'provider'),
2271
2272          /**
2273           * @ngdoc method
2274           * @name angular.Module#factory
2275           * @module ng
2276           * @param {string} name service name
2277           * @param {Function} providerFunction Function for creating new instance of the service.
2278           * @description
2279           * See {@link auto.$provide#factory $provide.factory()}.
2280           */
2281          factory: invokeLaterAndSetModuleName('$provide', 'factory'),
2282
2283          /**
2284           * @ngdoc method
2285           * @name angular.Module#service
2286           * @module ng
2287           * @param {string} name service name
2288           * @param {Function} constructor A constructor function that will be instantiated.
2289           * @description
2290           * See {@link auto.$provide#service $provide.service()}.
2291           */
2292          service: invokeLaterAndSetModuleName('$provide', 'service'),
2293
2294          /**
2295           * @ngdoc method
2296           * @name angular.Module#value
2297           * @module ng
2298           * @param {string} name service name
2299           * @param {*} object Service instance object.
2300           * @description
2301           * See {@link auto.$provide#value $provide.value()}.
2302           */
2303          value: invokeLater('$provide', 'value'),
2304
2305          /**
2306           * @ngdoc method
2307           * @name angular.Module#constant
2308           * @module ng
2309           * @param {string} name constant name
2310           * @param {*} object Constant value.
2311           * @description
2312           * Because the constant are fixed, they get applied before other provide methods.
2313           * See {@link auto.$provide#constant $provide.constant()}.
2314           */
2315          constant: invokeLater('$provide', 'constant', 'unshift'),
2316
2317           /**
2318           * @ngdoc method
2319           * @name angular.Module#decorator
2320           * @module ng
2321           * @param {string} The name of the service to decorate.
2322           * @param {Function} This function will be invoked when the service needs to be
2323           *                                    instantiated and should return the decorated service instance.
2324           * @description
2325           * See {@link auto.$provide#decorator $provide.decorator()}.
2326           */
2327          decorator: invokeLaterAndSetModuleName('$provide', 'decorator'),
2328
2329          /**
2330           * @ngdoc method
2331           * @name angular.Module#animation
2332           * @module ng
2333           * @param {string} name animation name
2334           * @param {Function} animationFactory Factory function for creating new instance of an
2335           *                                    animation.
2336           * @description
2337           *
2338           * **NOTE**: animations take effect only if the **ngAnimate** module is loaded.
2339           *
2340           *
2341           * Defines an animation hook that can be later used with
2342           * {@link $animate $animate} service and directives that use this service.
2343           *
2344           * ```js
2345           * module.animation('.animation-name', function($inject1, $inject2) {
2346           *   return {
2347           *     eventName : function(element, done) {
2348           *       //code to run the animation
2349           *       //once complete, then run done()
2350           *       return function cancellationFunction(element) {
2351           *         //code to cancel the animation
2352           *       }
2353           *     }
2354           *   }
2355           * })
2356           * ```
2357           *
2358           * See {@link ng.$animateProvider#register $animateProvider.register()} and
2359           * {@link ngAnimate ngAnimate module} for more information.
2360           */
2361          animation: invokeLaterAndSetModuleName('$animateProvider', 'register'),
2362
2363          /**
2364           * @ngdoc method
2365           * @name angular.Module#filter
2366           * @module ng
2367           * @param {string} name Filter name - this must be a valid angular expression identifier
2368           * @param {Function} filterFactory Factory function for creating new instance of filter.
2369           * @description
2370           * See {@link ng.$filterProvider#register $filterProvider.register()}.
2371           *
2372           * <div class="alert alert-warning">
2373           * **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`.
2374           * Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace
2375           * your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
2376           * (`myapp_subsection_filterx`).
2377           * </div>
2378           */
2379          filter: invokeLaterAndSetModuleName('$filterProvider', 'register'),
2380
2381          /**
2382           * @ngdoc method
2383           * @name angular.Module#controller
2384           * @module ng
2385           * @param {string|Object} name Controller name, or an object map of controllers where the
2386           *    keys are the names and the values are the constructors.
2387           * @param {Function} constructor Controller constructor function.
2388           * @description
2389           * See {@link ng.$controllerProvider#register $controllerProvider.register()}.
2390           */
2391          controller: invokeLaterAndSetModuleName('$controllerProvider', 'register'),
2392
2393          /**
2394           * @ngdoc method
2395           * @name angular.Module#directive
2396           * @module ng
2397           * @param {string|Object} name Directive name, or an object map of directives where the
2398           *    keys are the names and the values are the factories.
2399           * @param {Function} directiveFactory Factory function for creating new instance of
2400           * directives.
2401           * @description
2402           * See {@link ng.$compileProvider#directive $compileProvider.directive()}.
2403           */
2404          directive: invokeLaterAndSetModuleName('$compileProvider', 'directive'),
2405
2406          /**
2407           * @ngdoc method
2408           * @name angular.Module#config
2409           * @module ng
2410           * @param {Function} configFn Execute this function on module load. Useful for service
2411           *    configuration.
2412           * @description
2413           * Use this method to register work which needs to be performed on module loading.
2414           * For more about how to configure services, see
2415           * {@link providers#provider-recipe Provider Recipe}.
2416           */
2417          config: config,
2418
2419          /**
2420           * @ngdoc method
2421           * @name angular.Module#run
2422           * @module ng
2423           * @param {Function} initializationFn Execute this function after injector creation.
2424           *    Useful for application initialization.
2425           * @description
2426           * Use this method to register work which should be performed when the injector is done
2427           * loading all modules.
2428           */
2429          run: function(block) {
2430            runBlocks.push(block);
2431            return this;
2432          }
2433        };
2434
2435        if (configFn) {
2436          config(configFn);
2437        }
2438
2439        return moduleInstance;
2440
2441        /**
2442         * @param {string} provider
2443         * @param {string} method
2444         * @param {String=} insertMethod
2445         * @returns {angular.Module}
2446         */
2447        function invokeLater(provider, method, insertMethod, queue) {
2448          if (!queue) queue = invokeQueue;
2449          return function() {
2450            queue[insertMethod || 'push']([provider, method, arguments]);
2451            return moduleInstance;
2452          };
2453        }
2454
2455        /**
2456         * @param {string} provider
2457         * @param {string} method
2458         * @returns {angular.Module}
2459         */
2460        function invokeLaterAndSetModuleName(provider, method) {
2461          return function(recipeName, factoryFunction) {
2462            if (factoryFunction && isFunction(factoryFunction)) factoryFunction.$$moduleName = name;
2463            invokeQueue.push([provider, method, arguments]);
2464            return moduleInstance;
2465          };
2466        }
2467      });
2468    };
2469  });
2470
2471}
2472
2473/* global: toDebugString: true */
2474
2475function serializeObject(obj) {
2476  var seen = [];
2477
2478  return JSON.stringify(obj, function(key, val) {
2479    val = toJsonReplacer(key, val);
2480    if (isObject(val)) {
2481
2482      if (seen.indexOf(val) >= 0) return '...';
2483
2484      seen.push(val);
2485    }
2486    return val;
2487  });
2488}
2489
2490function toDebugString(obj) {
2491  if (typeof obj === 'function') {
2492    return obj.toString().replace(/ \{[\s\S]*$/, '');
2493  } else if (isUndefined(obj)) {
2494    return 'undefined';
2495  } else if (typeof obj !== 'string') {
2496    return serializeObject(obj);
2497  }
2498  return obj;
2499}
2500
2501/* global angularModule: true,
2502  version: true,
2503
2504  $CompileProvider,
2505
2506  htmlAnchorDirective,
2507  inputDirective,
2508  inputDirective,
2509  formDirective,
2510  scriptDirective,
2511  selectDirective,
2512  styleDirective,
2513  optionDirective,
2514  ngBindDirective,
2515  ngBindHtmlDirective,
2516  ngBindTemplateDirective,
2517  ngClassDirective,
2518  ngClassEvenDirective,
2519  ngClassOddDirective,
2520  ngCloakDirective,
2521  ngControllerDirective,
2522  ngFormDirective,
2523  ngHideDirective,
2524  ngIfDirective,
2525  ngIncludeDirective,
2526  ngIncludeFillContentDirective,
2527  ngInitDirective,
2528  ngNonBindableDirective,
2529  ngPluralizeDirective,
2530  ngRepeatDirective,
2531  ngShowDirective,
2532  ngStyleDirective,
2533  ngSwitchDirective,
2534  ngSwitchWhenDirective,
2535  ngSwitchDefaultDirective,
2536  ngOptionsDirective,
2537  ngTranscludeDirective,
2538  ngModelDirective,
2539  ngListDirective,
2540  ngChangeDirective,
2541  patternDirective,
2542  patternDirective,
2543  requiredDirective,
2544  requiredDirective,
2545  minlengthDirective,
2546  minlengthDirective,
2547  maxlengthDirective,
2548  maxlengthDirective,
2549  ngValueDirective,
2550  ngModelOptionsDirective,
2551  ngAttributeAliasDirectives,
2552  ngEventDirectives,
2553
2554  $AnchorScrollProvider,
2555  $AnimateProvider,
2556  $CoreAnimateCssProvider,
2557  $$CoreAnimateQueueProvider,
2558  $$CoreAnimateRunnerProvider,
2559  $BrowserProvider,
vendor: 4,985 bytes, lines 2560-2719
2560  $CacheFactoryProvider,
2561  $ControllerProvider,
2562  $DocumentProvider,
2563  $ExceptionHandlerProvider,
2564  $FilterProvider,
2565  $$ForceReflowProvider,
2566  $InterpolateProvider,
2567  $IntervalProvider,
2568  $$HashMapProvider,
2569  $HttpProvider,
2570  $HttpParamSerializerProvider,
2571  $HttpParamSerializerJQLikeProvider,
2572  $HttpBackendProvider,
2573  $xhrFactoryProvider,
2574  $LocationProvider,
2575  $LogProvider,
2576  $ParseProvider,
2577  $RootScopeProvider,
2578  $QProvider,
2579  $$QProvider,
2580  $$SanitizeUriProvider,
2581  $SceProvider,
2582  $SceDelegateProvider,
2583  $SnifferProvider,
2584  $TemplateCacheProvider,
2585  $TemplateRequestProvider,
2586  $$TestabilityProvider,
2587  $TimeoutProvider,
2588  $$RAFProvider,
2589  $WindowProvider,
2590  $$jqLiteProvider,
2591  $$CookieReaderProvider
2592*/
2593
2594
2595/**
2596 * @ngdoc object
2597 * @name angular.version
2598 * @module ng
2599 * @description
2600 * An object that contains information about the current AngularJS version.
2601 *
2602 * This object has the following properties:
2603 *
2604 * - `full` – `{string}` – Full version string, such as "0.9.18".
2605 * - `major` – `{number}` – Major version number, such as "0".
2606 * - `minor` – `{number}` – Minor version number, such as "9".
2607 * - `dot` – `{number}` – Dot version number, such as "18".
2608 * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat".
2609 */
2610var version = {
2611  full: '1.4.7',    // all of these placeholder strings will be replaced by grunt's
2612  major: 1,    // package task
2613  minor: 4,
2614  dot: 7,
2615  codeName: 'snapshot'
2616};
2617
2618
2619function publishExternalAPI(angular) {
2620  extend(angular, {
2621    'bootstrap': bootstrap,
2622    'copy': copy,
2623    'extend': extend,
2624    'merge': merge,
2625    'equals': equals,
2626    'element': jqLite,
2627    'forEach': forEach,
2628    'injector': createInjector,
2629    'noop': noop,
2630    'bind': bind,
2631    'toJson': toJson,
2632    'fromJson': fromJson,
2633    'identity': identity,
2634    'isUndefined': isUndefined,
2635    'isDefined': isDefined,
2636    'isString': isString,
2637    'isFunction': isFunction,
2638    'isObject': isObject,
2639    'isNumber': isNumber,
2640    'isElement': isElement,
2641    'isArray': isArray,
2642    'version': version,
2643    'isDate': isDate,
2644    'lowercase': lowercase,
2645    'uppercase': uppercase,
2646    'callbacks': {counter: 0},
2647    'getTestability': getTestability,
2648    '$$minErr': minErr,
2649    '$$csp': csp,
2650    'reloadWithDebugInfo': reloadWithDebugInfo
2651  });
2652
2653  angularModule = setupModuleLoader(window);
2654
2655  angularModule('ng', ['ngLocale'], ['$provide',
2656    function ngModule($provide) {
2657      // $$sanitizeUriProvider needs to be before $compileProvider as it is used by it.
2658      $provide.provider({
2659        $$sanitizeUri: $$SanitizeUriProvider
2660      });
2661      $provide.provider('$compile', $CompileProvider).
2662        directive({
2663            a: htmlAnchorDirective,
2664            input: inputDirective,
2665            textarea: inputDirective,
2666            form: formDirective,
2667            script: scriptDirective,
2668            select: selectDirective,
2669            style: styleDirective,
2670            option: optionDirective,
2671            ngBind: ngBindDirective,
2672            ngBindHtml: ngBindHtmlDirective,
2673            ngBindTemplate: ngBindTemplateDirective,
2674            ngClass: ngClassDirective,
2675            ngClassEven: ngClassEvenDirective,
2676            ngClassOdd: ngClassOddDirective,
2677            ngCloak: ngCloakDirective,
2678            ngController: ngControllerDirective,
2679            ngForm: ngFormDirective,
2680            ngHide: ngHideDirective,
2681            ngIf: ngIfDirective,
2682            ngInclude: ngIncludeDirective,
2683            ngInit: ngInitDirective,
2684            ngNonBindable: ngNonBindableDirective,
2685            ngPluralize: ngPluralizeDirective,
2686            ngRepeat: ngRepeatDirective,
2687            ngShow: ngShowDirective,
2688            ngStyle: ngStyleDirective,
2689            ngSwitch: ngSwitchDirective,
2690            ngSwitchWhen: ngSwitchWhenDirective,
2691            ngSwitchDefault: ngSwitchDefaultDirective,
2692            ngOptions: ngOptionsDirective,
2693            ngTransclude: ngTranscludeDirective,
2694            ngModel: ngModelDirective,
2695            ngList: ngListDirective,
2696            ngChange: ngChangeDirective,
2697            pattern: patternDirective,
2698            ngPattern: patternDirective,
2699            required: requiredDirective,
2700            ngRequired: requiredDirective,
2701            minlength: minlengthDirective,
2702            ngMinlength: minlengthDirective,
2703            maxlength: maxlengthDirective,
2704            ngMaxlength: maxlengthDirective,
2705            ngValue: ngValueDirective,
2706            ngModelOptions: ngModelOptionsDirective
2707        }).
2708        directive({
2709          ngInclude: ngIncludeFillContentDirective
2710        }).
2711        directive(ngAttributeAliasDirectives).
2712        directive(ngEventDirectives);
2713      $provide.provider({
2714        $anchorScroll: $AnchorScrollProvider,
2715        $animate: $AnimateProvider,
2716        $animateCss: $CoreAnimateCssProvider,
2717        $$animateQueue: $$CoreAnimateQueueProvider,
2718        $$AnimateRunner: $$CoreAnimateRunnerProvider,
2719        $browser: $BrowserProvider,
2720        $cacheFactory: $CacheFactoryProvider,
2721        $controller: $ControllerProvider,
2722        $document: $DocumentProvider,
2723        $exceptionHandler: $ExceptionHandlerProvider,
2724        $filter: $FilterProvider,
2725        $$forceReflow: $$ForceReflowProvider,
2726        $interpolate: $InterpolateProvider,
2727        $interval: $IntervalProvider,
2728        $http: $HttpProvider,
2729        $httpParamSerializer: $HttpParamSerializerProvider,
2730        $httpParamSerializerJQLike: $HttpParamSerializerJQLikeProvider,
2731        $httpBackend: $HttpBackendProvider,
2732        $xhrFactory: $xhrFactoryProvider,
2733        $location: $LocationProvider,
2734        $log: $LogProvider,
2735        $parse: $ParseProvider,
2736        $rootScope: $RootScopeProvider,
2737        $q: $QProvider,
2738        $$q: $$QProvider,
2739        $sce: $SceProvider,
2740        $sceDelegate: $SceDelegateProvider,
2741        $sniffer: $SnifferProvider,
2742        $templateCache: $TemplateCacheProvider,
2743        $templateRequest: $TemplateRequestProvider,
2744        $$testability: $$TestabilityProvider,
2745        $timeout: $TimeoutProvider,
2746        $window: $WindowProvider,
2747        $$rAF: $$RAFProvider,
2748        $$jqLite: $$jqLiteProvider,
2749        $$HashMap: $$HashMapProvider,
2750        $$cookieReader: $$CookieReaderProvider
2751      });
2752    }
2753  ]);
2754}
2755
2756/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2757 *     Any commits to this file should be reviewed with security in mind.  *
2758 *   Changes to this file can potentially create security vulnerabilities. *
2759 *          An approval from 2 Core members with history of modifying      *
2760 *                         this file is required.                          *
2761 *                                                                         *
2762 *  Does the change somehow allow for arbitrary javascript to be executed? *
2763 *    Or allows for someone to change the prototype of built-in objects?   *
2764 *     Or gives undesired access to variables likes document or window?    *
2765 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2766
2767/* global JQLitePrototype: true,
2768  addEventListenerFn: true,
2769  removeEventListenerFn: true,
2770  BOOLEAN_ATTR: true,
2771  ALIASED_ATTR: true,
2772*/
2773
2774//////////////////////////////////
2775//JQLite
2776//////////////////////////////////
2777
2778/**
2779 * @ngdoc function
2780 * @name angular.element
2781 * @module ng
2782 * @kind function
2783 *
2784 * @description
2785 * Wraps a raw DOM element or HTML string as a [jQuery](http://jquery.com) element.
2786 *
2787 * If jQuery is available, `angular.element` is an alias for the
2788 * [jQuery](http://api.jquery.com/jQuery/) function. If jQuery is not available, `angular.element`
2789 * delegates to Angular's built-in subset of jQuery, called "jQuery lite" or "jqLite."
2790 *
2791 * <div class="alert alert-success">jqLite is a tiny, API-compatible subset of jQuery that allows
2792 * Angular to manipulate the DOM in a cross-browser compatible way. **jqLite** implements only the most
2793 * commonly needed functionality with the goal of having a very small footprint.</div>
2794 *
2795 * To use `jQuery`, simply ensure it is loaded before the `angular.js` file.
2796 *
2797 * <div class="alert">**Note:** all element references in Angular are always wrapped with jQuery or
2798 * jqLite; they are never raw DOM references.</div>
2799 *
2800 * ## Angular's jqLite
2801 * jqLite provides only the following jQuery methods:
2802 *
2803 * - [`addClass()`](http://api.jquery.com/addClass/)
2804 * - [`after()`](http://api.jquery.com/after/)
2805 * - [`append()`](http://api.jquery.com/append/)
2806 * - [`attr()`](http://api.jquery.com/attr/) - Does not support functions as parameters
2807 * - [`bind()`](http://api.jquery.com/bind/) - Does not support namespaces, selectors or eventData
2808 * - [`children()`](http://api.jquery.com/children/) - Does not support selectors
2809 * - [`clone()`](http://api.jquery.com/clone/)
2810 * - [`contents()`](http://api.jquery.com/contents/)
2811 * - [`css()`](http://api.jquery.com/css/) - Only retrieves inline-styles, does not call `getComputedStyle()`. As a setter, does not convert numbers to strings or append 'px'.
2812 * - [`data()`](http://api.jquery.com/data/)
2813 * - [`detach()`](http://api.jquery.com/detach/)
2814 * - [`empty()`](http://api.jquery.com/empty/)
2815 * - [`eq()`](http://api.jquery.com/eq/)
2816 * - [`find()`](http://api.jquery.com/find/) - Limited to lookups by tag name
2817 * - [`hasClass()`](http://api.jquery.com/hasClass/)
2818 * - [`html()`](http://api.jquery.com/html/)
2819 * - [`next()`](http://api.jquery.com/next/) - Does not support selectors
2820 * - [`on()`](http://api.jquery.com/on/) - Does not support namespaces, selectors or eventData
2821 * - [`off()`](http://api.jquery.com/off/) - Does not support namespaces, selectors or event object as parameter
2822 * - [`one()`](http://api.jquery.com/one/) - Does not support namespaces or selectors
2823 * - [`parent()`](http://api.jquery.com/parent/) - Does not support selectors
2824 * - [`prepend()`](http://api.jquery.com/prepend/)
2825 * - [`prop()`](http://api.jquery.com/prop/)
2826 * - [`ready()`](http://api.jquery.com/ready/)
2827 * - [`remove()`](http://api.jquery.com/remove/)
2828 * - [`removeAttr()`](http://api.jquery.com/removeAttr/)
2829 * - [`removeClass()`](http://api.jquery.com/removeClass/)
2830 * - [`removeData()`](http://api.jquery.com/removeData/)
2831 * - [`replaceWith()`](http://api.jquery.com/replaceWith/)
2832 * - [`text()`](http://api.jquery.com/text/)
2833 * - [`toggleClass()`](http://api.jquery.com/toggleClass/)
2834 * - [`triggerHandler()`](http://api.jquery.com/triggerHandler/) - Passes a dummy event object to handlers.
2835 * - [`unbind()`](http://api.jquery.com/unbind/) - Does not support namespaces or event object as parameter
2836 * - [`val()`](http://api.jquery.com/val/)
2837 * - [`wrap()`](http://api.jquery.com/wrap/)
2838 *
2839 * ## jQuery/jqLite Extras
2840 * Angular also provides the following additional methods and events to both jQuery and jqLite:
2841 *
2842 * ### Events
2843 * - `$destroy` - AngularJS intercepts all jqLite/jQuery's DOM destruction a
2843pis and fires this event
2844 *    on all DOM nodes being removed.  This can be used to clean up any 3rd party bindings to the DOM
2845 *    element before it is removed.
2846 *
2847 * ### Methods
2848 * - `controller(name)` - retrieves the controller of the current element or its parent. By default
2849 *   retrieves controller associated with the `ngController` directive. If `name` is provided as
2850 *   camelCase directive name, then the controller for this directive will be retrieved (e.g.
2851 *   `'ngModel'`).
2852 * - `injector()` - retrieves the injector of the current element or its parent.
2853 * - `scope()` - retrieves the {@link ng.$rootScope.Scope scope} of the current
2854 *   element or its parent. Requires {@link guide/production#disabling-debug-data Debug Data} to
2855 *   be enabled.
2856 * - `isolateScope()` - retrieves an isolate {@link ng.$rootScope.Scope scope} if one is attached directly to the
2857 *   current element. This getter should be used only on elements that contain a directive which starts a new isolate
2858 *   scope. Calling `scope()` on this element always returns the original non-isolate scope.
2859 *   Requires {@link guide/production#disabling-debug-data Debug Data} to be enabled.
2860 * - `inheritedData()` - same as `data()`, but walks up the DOM until a value is found or the top
2861 *   parent element is reached.
2862 *
2863 * @param {string|DOMElement} element HTML string or DOMElement to be wrapped into jQuery.
2864 * @returns {Object} jQuery object.
2865 */
2866
2867JQLite.expando = 'ng339';
2868
2869var jqCache = JQLite.cache = {},
2870    jqId = 1,
2871    addEventListenerFn = function(element, type, fn) {
2872      element.addEventListener(type, fn, false);
2873    },
2874    removeEventListenerFn = function(element, type, fn) {
2875      element.removeEventListener(type, fn, false);
2876    };
2877
2878/*
2879 * !!! This is an undocumented "private" function !!!
2880 */
2881JQLite._data = function(node) {
2882  //jQuery always returns an object on cache miss
2883  return this.cache[node[this.expando]] || {};
2884};
2885
2886function jqNextId() { return ++jqId; }
2887
2888
2889var SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g;
2890var MOZ_HACK_REGEXP = /^moz([A-Z])/;
2891var MOUSE_EVENT_MAP= { mouseleave: "mouseout", mouseenter: "mouseover"};
2892var jqLiteMinErr = minErr('jqLite');
2893
2894/**
2895 * Converts snake_case to camelCase.
2896 * Also there is special case for Moz prefix starting with upper case letter.
2897 * @param name Name to normalize
2898 */
2899function camelCase(name) {
2900  return name.
2901    replace(SPECIAL_CHARS_REGEXP, function(_, separator, letter, offset) {
2902      return offset ? letter.toUpperCase() : letter;
2903    }).
2904    replace(MOZ_HACK_REGEXP, 'Moz$1');
2905}
2906
2907var SINGLE_TAG_REGEXP = /^<([\w-]+)\s*\/?>(?:<\/\1>|)$/;
2908var HTML_REGEXP = /<|&#?\w+;/;
2909var TAG_NAME_REGEXP = /<([\w:-]+)/;
2910var XHTML_TAG_REGEXP = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^>]*)\/>/gi;
2911
2912var wrapMap = {
2913  'option': [1, '<select multiple="multiple">', '</select>'],
2914
2915  'thead': [1, '<table>', '</table>'],
2916  'col': [2, '<table><colgroup>', '</colgroup></table>'],
2917  'tr': [2, '<table><tbody>', '</tbody></table>'],
2918  'td': [3, '<table><tbody><tr>', '</tr></tbody></table>'],
2919  '_default': [0, "", ""]
2920};
2921
2922wrapMap.optgroup = wrapMap.option;
2923wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
2924wrapMap.th = wrapMap.td;
2925
2926
2927function jqLiteIsTextNode(html) {
2928  return !HTML_REGEXP.test(html);
2929}
2930
2931function jqLiteAcceptsData(node) {
2932  // The window object can accept data but has no nodeType
2933  // Otherwise we are only interested in elements (1) and documents (9)
2934  var nodeType = node.nodeType;
2935  return nodeType === NODE_TYPE_ELEMENT || !nodeType || nodeType === NODE_TYPE_DOCUMENT;
2936}
2937
2938function jqLiteHasData(node) {
2939  for (var key in jqCache[node.ng339]) {
2940    return true;
2941  }
2942  return false;
2943}
2944
2945function jqLiteBuildFragment(html, context) {
2946  var tmp, tag, wrap,
2947      fragment = context.createDocumentFragment(),
2948      nodes = [], i;
2949
2950  if (jqLiteIsTextNode(html)) {
2951    // Convert non-html into a text node
2952    nodes.push(context.createTextNode(html));
2953  } else {
2954    // Convert html into DOM nodes
2955    tmp = tmp || fragment.appendChild(context.createElement("div"));
2956    tag = (TAG_NAME_REGEXP.exec(html) || ["", ""])[1].toLowerCase();
2957    wrap = wrapMap[tag] || wrapMap._default;
2958    tmp.innerHTML = wrap[1] + html.replace(XHTML_TAG_REGEXP, "<$1></$2>") + wrap[2];
2959
2960    // Descend through wrappers to the right content
2961    i = wrap[0];
2962    while (i--) {
2963      tmp = tmp.lastChild;
2964    }
2965
2966    nodes = concat(nodes, tmp.childNodes);
2967
2968    tmp = fragment.firstChild;
2969    tmp.textContent = "";
2970  }
2971
2972  // Remove wrapper from fragment
2973  fragment.textContent = "";
2974  fragment.innerHTML = ""; // Clear inner HTML
2975  forEach(nodes, function(node) {
2976    fragment.appendChild(node);
2977  });
2978
2979  return fragment;
2980}
2981
2982function jqLiteParseHTML(html, context) {
2983  context = context || document;
2984  var parsed;
2985
2986  if ((parsed = SINGLE_TAG_REGEXP.exec(html))) {
2987    return [context.createElement(parsed[1])];
2988  }
2989
2990  if ((parsed = jqLiteBuildFragment(html, context))) {
2991    return parsed.childNodes;
2992  }
2993
2994  return [];
2995}
2996
vendor: 1,065 bytes, lines 2997-3038
2997/////////////////////////////////////////////
2998function JQLite(element) {
2999  if (element instanceof JQLite) {
3000    return element;
3001  }
3002
3003  var argIsString;
3004
3005  if (isString(element)) {
3006    element = trim(element);
3007    argIsString = true;
3008  }
3009  if (!(this instanceof JQLite)) {
3010    if (argIsString && element.charAt(0) != '<') {
3011      throw jqLiteMinErr('nosel', 'Looking up elements via selectors is not supported by jqLite! See: http://docs.angularjs.org/api/angular.element');
3012    }
3013    return new JQLite(element);
3014  }
3015
3016  if (argIsString) {
3017    jqLiteAddNodes(this, jqLiteParseHTML(element));
3018  } else {
3019    jqLiteAddNodes(this, element);
3020  }
3021}
3022
3023function jqLiteClone(element) {
3024  return element.cloneNode(true);
3025}
3026
3027function jqLiteDealoc(element, onlyDescendants) {
3028  if (!onlyDescendants) jqLiteRemoveData(element);
3029
3030  if (element.querySelectorAll) {
3031    var descendants = element.querySelectorAll('*');
3032    for (var i = 0, l = descendants.length; i < l; i++) {
3033      jqLiteRemoveData(descendants[i]);
3034    }
3035  }
3036}
3037
3038function jqLiteOff(element, type, fn, unsupported) {
3039  if (isDefined(unsupported)) throw jqLiteMinErr('offargs', 'jqLite#off() does not support the `selector` argument');
3040
3041  var expandoStore = jqLiteExpandoStore(element);
3042  var events = expandoStore && expandoStore.events;
3043  var handle = expandoStore && expandoStore.handle;
3044
3045  if (!handle) return; //no listeners registered
3046
3047  if (!type) {
3048    for (type in events) {
3049      if (type !== '$destroy') {
3050        removeEventListenerFn(element, type, handle);
3051      }
3052      delete events[type];
3053    }
3054  } else {
3055    forEach(type.split(' '), function(type) {
3056      if (isDefined(fn)) {
3057        var listenerFns = events[type];
3058        arrayRemove(listenerFns || [], fn);
3059        if (listenerFns && listenerFns.length > 0) {
3060          return;
3061        }
3062      }
3063
3064      removeEventListenerFn(element, type, handle);
3065      delete events[type];
3066    });
3067  }
3068}
3069
3070function jqLiteRemoveData(element, name) {
3071  var expandoId = element.ng339;
3072  var expandoStore = expandoId && jqCache[expandoId];
3073
3074  if (expandoStore) {
3075    if (name) {
3076      delete expandoStore.data[name];
3077      return;
3078    }
3079
3080    if (expandoStore.handle) {
3081      if (expandoStore.events.$destroy) {
3082        expandoStore.handle({}, '$destroy');
3083      }
3084      jqLiteOff(element);
3085    }
3086    delete jqCache[expandoId];
3087    element.ng339 = undefined; // don't delete DOM expandos. IE and Chrome don't like it
3088  }
3089}
3090
3091
3092function jqLiteExpandoStore(element, createIfNecessary) {
3093  var expandoId = element.ng339,
3094      expandoStore = expandoId && jqCache[expandoId];
3095
3096  if (createIfNecessary && !expandoStore) {
3097    element.ng339 = expandoId = jqNextId();
3098    expandoStore = jqCache[expandoId] = {events: {}, data: {}, handle: undefined};
3099  }
3100
3101  return expandoStore;
3102}
3103
3104
3105function jqLiteData(element, key, value) {
3106  if (jqLiteAcceptsData(element)) {
3107
3108    var isSimpleSetter = isDefined(value);
3109    var isSimpleGetter = !isSimpleSetter && key && !isObject(key);
3110    var massGetter = !key;
3111    var expandoStore = jqLiteExpandoStore(element, !isSimpleGetter);
3112    var data = expandoStore && expandoStore.data;
3113
3114    if (isSimpleSetter) { // data('key', value)
3115      data[key] = value;
3116    } else {
3117      if (massGetter) {  // data()
3118        return data;
3119      } else {
3120        if (isSimpleGetter) { // data('key')
3121          // don't force creation of expandoStore if it doesn't exist yet
3122          return data && data[key];
3123        } else { // mass-setter: data({key1: val1, key2: val2})
3124          extend(data, key);
3125        }
3126      }
3127    }
3128  }
3129}
3130
3131function jqLiteHasClass(element, selector) {
3132  if (!element.getAttribute) return false;
3133  return ((" " + (element.getAttribute('class') || '') + " ").replace(/[\n\t]/g, " ").
3134      indexOf(" " + selector + " ") > -1);
3135}
3136
3137function jqLiteRemoveClass(element, cssClasses) {
3138  if (cssClasses && element.setAttribute) {
3139    forEach(cssClasses.split(' '), function(cssClass) {
3140      element.setAttribute('class', trim(
3141          (" " + (element.getAttribute('class') || '') + " ")
3142          .replace(/[\n\t]/g, " ")
3143          .replace(" " + trim(cssClass) + " ", " "))
3144      );
3145    });
3146  }
3147}
3148
3149function jqLiteAddClass(element, cssClasses) {
3150  if (cssClasses && element.setAttribute) {
3151    var existingClasses = (' ' + (element.getAttribute('class') || '') + ' ')
3152                            .replace(/[\n\t]/g, " ");
3153
3154    forEach(cssClasses.split(' '), function(cssClass) {
3155      cssClass = trim(cssClass);
3156      if (existingClasses.indexOf(' ' + cssClass + ' ') === -1) {
3157        existingClasses += cssClass + ' ';
3158      }
3159    });
3160
3161    element.setAttribute('class', trim(existingClasses));
3162  }
3163}
3164
3165
3166function jqLiteAddNodes(root, elements) {
3167  // THIS CODE IS VERY HOT. Don't make changes without benchmarking.
3168
3169  if (elements) {
3170
3171    // if a Node (the most common case)
3172    if (elements.nodeType) {
3173      root[root.length++] = elements;
3174    } else {
3175      var length = elements.length;
3176
3177      // if an Array or NodeList and not a Window
3178      if (typeof length === 'number' && elements.window !== elements) {
3179        if (length) {
3180          for (var i = 0; i < length; i++) {
3181            root[root.length++] = elements[i];
3182          }
3183        }
3184      } else {
3185        root[root.length++] = elements;
3186      }
3187    }
3188  }
3189}
3190
3191
3192function jqLiteController(element, name) {
3193  return jqLiteInheritedData(element, '$' + (name || 'ngController') + 'Controller');
3194}
3195
3196function jqLiteInheritedData(element, name, value) {
3197  // if element is the document object work with the html element instead
3198  // this makes $(document).scope() possible
3199  if (element.nodeType == NODE_TYPE_DOCUMENT) {
3200    element = element.documentElement;
3201  }
3202  var names = isArray(name) ? name : [name];
3203
3204  while (element) {
3205    for (var i = 0, ii = names.length; i < ii; i++) {
3206      if (isDefined(value = jqLite.data(element, names[i]))) return value;
3207    }
3208
3209    // If dealing with a document fragment node with a host element, and no parent, use the host
3210    // element as the parent. This enables directives within a Shadow DOM or polyfilled Shadow DOM
3211    // to lookup parent controllers.
3212    element = element.parentNode || (element.nodeType === NODE_TYPE_DOCUMENT_FRAGMENT && element.host);
3213  }
3214}
3215
3216function jqLiteEmpty(element) {
3217  jqLiteDealoc(element, true);
3218  while (element.firstChild) {
3219    element.removeChild(element.firstChild);
3220  }
3221}
3222
3223function jqLiteRemove(element, keepData) {
3224  if (!keepData) jqLiteDealoc(element);
3225  var parent = element.parentNode;
3226  if (parent) parent.removeChild(element);
3227}
3228
3229
3230function jqLiteDocumentLoaded(action, win) {
3231  win = win || window;
3232  if (win.document.readyState === 'complete') {
3233    // Force the action to be run async for consistent behaviour
3234    // from the action's point of view
3235    // i.e. it will definitely not be in a $apply
3236    win.setTimeout(action);
3237  } else {
3238    // No need to unbind this handler as load is only ever called once
3239    jqLite(win).on('load', action);
3240  }
3241}
3242
vendor: 8,923 bytes, lines 3243-3546
3243//////////////////////////////////////////
3244// Functions which are declared directly.
3245//////////////////////////////////////////
3246var JQLitePrototype = JQLite.prototype = {
3247  ready: function(fn) {
3248    var fired = false;
3249
3250    function trigger() {
3251      if (fired) return;
3252      fired = true;
3253      fn();
3254    }
3255
3256    // check if document is already loaded
3257    if (document.readyState === 'complete') {
3258      setTimeout(trigger);
3259    } else {
3260      this.on('DOMContentLoaded', trigger); // works for modern browsers and IE9
3261      // we can not use jqLite since we are not done loading and jQuery could be loaded later.
3262      // jshint -W064
3263      JQLite(window).on('load', trigger); // fallback to window.onload for others
3264      // jshint +W064
3265    }
3266  },
3267  toString: function() {
3268    var value = [];
3269    forEach(this, function(e) { value.push('' + e);});
3270    return '[' + value.join(', ') + ']';
3271  },
3272
3273  eq: function(index) {
3274      return (index >= 0) ? jqLite(this[index]) : jqLite(this[this.length + index]);
3275  },
3276
3277  length: 0,
3278  push: push,
3279  sort: [].sort,
3280  splice: [].splice
3281};
3282
3283//////////////////////////////////////////
3284// Functions iterating getter/setters.
3285// these functions return self on setter and
3286// value on get.
3287//////////////////////////////////////////
3288var BOOLEAN_ATTR = {};
3289forEach('multiple,selected,checked,disabled,readOnly,required,open'.split(','), function(value) {
3290  BOOLEAN_ATTR[lowercase(value)] = value;
3291});
3292var BOOLEAN_ELEMENTS = {};
3293forEach('input,select,option,textarea,button,form,details'.split(','), function(value) {
3294  BOOLEAN_ELEMENTS[value] = true;
3295});
3296var ALIASED_ATTR = {
3297  'ngMinlength': 'minlength',
3298  'ngMaxlength': 'maxlength',
3299  'ngMin': 'min',
3300  'ngMax': 'max',
3301  'ngPattern': 'pattern'
3302};
3303
3304function getBooleanAttrName(element, name) {
3305  // check dom last since we will most likely fail on name
3306  var booleanAttr = BOOLEAN_ATTR[name.toLowerCase()];
3307
3308  // booleanAttr is here twice to minimize DOM access
3309  return booleanAttr && BOOLEAN_ELEMENTS[nodeName_(element)] && booleanAttr;
3310}
3311
3312function getAliasedAttrName(name) {
3313  return ALIASED_ATTR[name];
3314}
3315
3316forEach({
3317  data: jqLiteData,
3318  removeData: jqLiteRemoveData,
3319  hasData: jqLiteHasData
3320}, function(fn, name) {
3321  JQLite[name] = fn;
3322});
3323
3324forEach({
3325  data: jqLiteData,
3326  inheritedData: jqLiteInheritedData,
3327
3328  scope: function(element) {
3329    // Can't use jqLiteData here directly so we stay compatible with jQuery!
3330    return jqLite.data(element, '$scope') || jqLiteInheritedData(element.parentNode || element, ['$isolateScope', '$scope']);
3331  },
3332
3333  isolateScope: function(element) {
3334    // Can't use jqLiteData here directly so we stay compatible with jQuery!
3335    return jqLite.data(element, '$isolateScope') || jqLite.data(element, '$isolateScopeNoTemplate');
3336  },
3337
3338  controller: jqLiteController,
3339
3340  injector: function(element) {
3341    return jqLiteInheritedData(element, '$injector');
3342  },
3343
3344  removeAttr: function(element, name) {
3345    element.removeAttribute(name);
3346  },
3347
3348  hasClass: jqLiteHasClass,
3349
3350  css: function(element, name, value) {
3351    name = camelCase(name);
3352
3353    if (isDefined(value)) {
3354      element.style[name] = value;
3355    } else {
3356      return element.style[name];
3357    }
3358  },
3359
3360  attr: function(element, name, value) {
3361    var nodeType = element.nodeType;
3362    if (nodeType === NODE_TYPE_TEXT || nodeType === NODE_TYPE_ATTRIBUTE || nodeType === NODE_TYPE_COMMENT) {
3363      return;
3364    }
3365    var lowercasedName = lowercase(name);
3366    if (BOOLEAN_ATTR[lowercasedName]) {
3367      if (isDefined(value)) {
3368        if (!!value) {
3369          element[name] = true;
3370          element.setAttribute(name, lowercasedName);
3371        } else {
3372          element[name] = false;
3373          element.removeAttribute(lowercasedName);
3374        }
3375      } else {
3376        return (element[name] ||
3377                 (element.attributes.getNamedItem(name) || noop).specified)
3378               ? lowercasedName
3379               : undefined;
3380      }
3381    } else if (isDefined(value)) {
3382      element.setAttribute(name, value);
3383    } else if (element.getAttribute) {
3384      // the extra argument "2" is to get the right thing for a.href in IE, see jQuery code
3385      // some elements (e.g. Document) don't have get attribute, so return undefined
3386      var ret = element.getAttribute(name, 2);
3387      // normalize non-existing attributes to undefined (as jQuery)
3388      return ret === null ? undefined : ret;
3389    }
3390  },
3391
3392  prop: function(element, name, value) {
3393    if (isDefined(value)) {
3394      element[name] = value;
3395    } else {
3396      return element[name];
3397    }
3398  },
3399
3400  text: (function() {
3401    getText.$dv = '';
3402    return getText;
3403
3404    function getText(element, value) {
3405      if (isUndefined(value)) {
3406        var nodeType = element.nodeType;
3407        return (nodeType === NODE_TYPE_ELEMENT || nodeType === NODE_TYPE_TEXT) ? element.textContent : '';
3408      }
3409      element.textContent = value;
3410    }
3411  })(),
3412
3413  val: function(element, value) {
3414    if (isUndefined(value)) {
3415      if (element.multiple && nodeName_(element) === 'select') {
3416        var result = [];
3417        forEach(element.options, function(option) {
3418          if (option.selected) {
3419            result.push(option.value || option.text);
3420          }
3421        });
3422        return result.length === 0 ? null : result;
3423      }
3424      return element.value;
3425    }
3426    element.value = value;
3427  },
3428
3429  html: function(element, value) {
3430    if (isUndefined(value)) {
3431      return element.innerHTML;
3432    }
3433    jqLiteDealoc(element, true);
3434    element.innerHTML = value;
3435  },
3436
3437  empty: jqLiteEmpty
3438}, function(fn, name) {
3439  /**
3440   * Properties: writes return selection, reads return first value
3441   */
3442  JQLite.prototype[name] = function(arg1, arg2) {
3443    var i, key;
3444    var nodeCount = this.length;
3445
3446    // jqLiteHasClass has only two arguments, but is a getter-only fn, so we need to special-case it
3447    // in a way that survives minification.
3448    // jqLiteEmpty takes no arguments but is a setter.
3449    if (fn !== jqLiteEmpty &&
3450        (isUndefined((fn.length == 2 && (fn !== jqLiteHasClass && fn !== jqLiteController)) ? arg1 : arg2))) {
3451      if (isObject(arg1)) {
3452
3453        // we are a write, but the object properties are the key/values
3454        for (i = 0; i < nodeCount; i++) {
3455          if (fn === jqLiteData) {
3456            // data() takes the whole object in jQuery
3457            fn(this[i], arg1);
3458          } else {
3459            for (key in arg1) {
3460              fn(this[i], key, arg1[key]);
3461            }
3462          }
3463        }
3464        // return self for chaining
3465        return this;
3466      } else {
3467        // we are a read, so read the first child.
3468        // TODO: do we still need this?
3469        var value = fn.$dv;
3470        // Only if we have $dv do we iterate over all, otherwise it is just the first element.
3471        var jj = (isUndefined(value)) ? Math.min(nodeCount, 1) : nodeCount;
3472        for (var j = 0; j < jj; j++) {
3473          var nodeValue = fn(this[j], arg1, arg2);
3474          value = value ? value + nodeValue : nodeValue;
3475        }
3476        return value;
3477      }
3478    } else {
3479      // we are a write, so apply to all children
3480      for (i = 0; i < nodeCount; i++) {
3481        fn(this[i], arg1, arg2);
3482      }
3483      // return self for chaining
3484      return this;
3485    }
3486  };
3487});
3488
3489function createEventHandler(element, events) {
3490  var eventHandler = function(event, type) {
3491    // jQuery specific api
3492    event.isDefaultPrevented = function() {
3493      return event.defaultPrevented;
3494    };
3495
3496    var eventFns = events[type || event.type];
3497    var eventFnsLength = eventFns ? eventFns.length : 0;
3498
3499    if (!eventFnsLength) return;
3500
3501    if (isUndefined(event.immediatePropagationStopped)) {
3502      var originalStopImmediatePropagation = event.stopImmediatePropagation;
3503      event.stopImmediatePropagation = function() {
3504        event.immediatePropagationStopped = true;
3505
3506        if (event.stopPropagation) {
3507          event.stopPropagation();
3508        }
3509
3510        if (originalStopImmediatePropagation) {
3511          originalStopImmediatePropagation.call(event);
3512        }
3513      };
3514    }
3515
3516    event.isImmediatePropagationStopped = function() {
3517      return event.immediatePropagationStopped === true;
3518    };
3519
3520    // Copy event handlers in case event handlers array is modified during execution.
3521    if ((eventFnsLength > 1)) {
3522      eventFns = shallowCopy(eventFns);
3523    }
3524
3525    for (var i = 0; i < eventFnsLength; i++) {
3526      if (!event.isImmediatePropagationStopped()) {
3527        eventFns[i].call(element, event);
3528      }
3529    }
3530  };
3531
3532  // TODO: this is a hack for angularMocks/clearDataCache that makes it possible to deregister all
3533  //       events on `element`
3534  eventHandler.elem = element;
3535  return eventHandler;
3536}
3537
3538//////////////////////////////////////////
3539// Functions iterating traversal.
3540// These functions chain results into a single
3541// selector.
3542//////////////////////////////////////////
3543forEach({
3544  removeData: jqLiteRemoveData,
3545
3546  on: function jqLiteOn(element, type, fn, unsupported) {
3547    if (isDefined(unsupported)) throw jqLiteMinErr('onargs', 'jqLite#on() does not support the `selector` or `eventData` parameters');
3548
3549    // Do not add event handlers to non-elements because they will not be cleaned up.
3550    if (!jqLiteAcceptsData(element)) {
3551      return;
3552    }
3553
3554    var expandoStore = jqLiteExpandoStore(element, true);
3555    var events = expandoStore.events;
3556    var handle = expandoStore.handle;
3557
3558    if (!handle) {
3559      handle = expandoStore.handle = createEventHandler(element, events);
3560    }
3561
3562    // http://jsperf.com/string-indexof-vs-split
3563    var types = type.indexOf(' ') >= 0 ? type.split(' ') : [type];
3564    var i = types.length;
3565
3566    while (i--) {
3567      type = types[i];
3568      var eventFns = events[type];
3569
3570      if (!eventFns) {
3571        events[type] = [];
3572
3573        if (type === 'mouseenter' || type === 'mouseleave') {
3574          // Refer to jQuery's implementation of mouseenter & mouseleave
3575          // Read about mouseenter and mouseleave:
3576          // http://www.quirksmode.org/js/events_mouse.html#link8
3577
3578          jqLiteOn(element, MOUSE_EVENT_MAP[type], function(event) {
3579            var target = this, related = event.relatedTarget;
3580            // For mousenter/leave call the handler if related is outside the target.
3581            // NB: No relatedTarget if the mouse left/entered the browser window
3582            if (!related || (related !== target && !target.contains(related))) {
3583              handle(event, type);
3584            }
3585          });
3586
3587        } else {
3588          if (type !== '$destroy') {
3589            addEventListenerFn(element, type, handle);
3590          }
3591        }
3592        eventFns = events[type];
3593      }
3594      eventFns.push(fn);
3595    }
3596  },
3597
3598  off: jqLiteOff,
3599
3600  one: function(element, type, fn) {
3601    element = jqLite(element);
3602
3603    //add the listener twice so that when it is called
3604    //you can remove the original function and still be
3605    //able to call element.off(ev, fn) normally
3606    element.on(type, function onFn() {
3607      element.off(type, fn);
3608      element.off(type, onFn);
3609    });
3610    element.on(type, fn);
3611  },
3612
3613  replaceWith: function(element, replaceNode) {
3614    var index, parent = element.parentNode;
3615    jqLiteDealoc(element);
3616    forEach(new JQLite(replaceNode), function(node) {
3617      if (index) {
3618        parent.insertBefore(node, index.nextSibling);
3619      } else {
3620        parent.replaceChild(node, element);
3621      }
3622      index = node;
3623    });
3624  },
3625
3626  children: function(element) {
3627    var children = [];
3628    forEach(element.childNodes, function(element) {
3629      if (element.nodeType === NODE_TYPE_ELEMENT) {
3630        children.push(element);
3631      }
3632    });
3633    return children;
3634  },
3635
3636  contents: function(element) {
3637    return element.contentDocument || element.childNodes || [];
3638  },
3639
3640  append: function(element, node) {
3641    var nodeType = element.nodeType;
3642    if (nodeType !== NODE_TYPE_ELEMENT && nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT) return;
3643
3644    node = new JQLite(node);
3645
3646    for (var i = 0, ii = node.length; i < ii; i++) {
3647      var child = node[i];
3648      element.appendChild(child);
3649    }
3650  },
3651
3652  prepend: function(element, node) {
3653    if (element.nodeType === NODE_TYPE_ELEMENT) {
3654      var index = element.firstChild;
3655      forEach(new JQLite(node), function(child) {
3656        element.insertBefore(child, index);
3657      });
3658    }
3659  },
3660
3661  wrap: function(element, wrapNode) {
3662    wrapNode = jqLite(wrapNode).eq(0).clone()[0];
3663    var parent = element.parentNode;
3664    if (parent) {
3665      parent.replaceChild(wrapNode, element);
3666    }
3667    wrapNode.appendChild(element);
3668  },
3669
3670  remove: jqLiteRemove,
3671
3672  detach: function(element) {
3673    jqLiteRemove(element, true);
3674  },
3675
3676  after: function(element, newElement) {
3677    var index = element, parent = element.parentNode;
3678    newElement = new JQLite(newElement);
3679
3680    for (var i = 0, ii = newElement.length; i < ii; i++) {
3681      var node = newElement[i];
3682      parent.insertBefore(node, index.nextSibling);
3683      index = node;
3684    }
3685  },
3686
3687  addClass: jqLiteAddClass,
3688  removeClass: jqLiteRemoveClass,
3689
3690  toggleClass: function(element, selector, condition) {
3691    if (selector) {
vendor: 5,441 bytes, lines 3692-3893
3692      forEach(selector.split(' '), function(className) {
3693        var classCondition = condition;
3694        if (isUndefined(classCondition)) {
3695          classCondition = !jqLiteHasClass(element, className);
3696        }
3697        (classCondition ? jqLiteAddClass : jqLiteRemoveClass)(element, className);
3698      });
3699    }
3700  },
3701
3702  parent: function(element) {
3703    var parent = element.parentNode;
3704    return parent && parent.nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT ? parent : null;
3705  },
3706
3707  next: function(element) {
3708    return element.nextElementSibling;
3709  },
3710
3711  find: function(element, selector) {
3712    if (element.getElementsByTagName) {
3713      return element.getElementsByTagName(selector);
3714    } else {
3715      return [];
3716    }
3717  },
3718
3719  clone: jqLiteClone,
3720
3721  triggerHandler: function(element, event, extraParameters) {
3722
3723    var dummyEvent, eventFnsCopy, handlerArgs;
3724    var eventName = event.type || event;
3725    var expandoStore = jqLiteExpandoStore(element);
3726    var events = expandoStore && expandoStore.events;
3727    var eventFns = events && events[eventName];
3728
3729    if (eventFns) {
3730      // Create a dummy event to pass to the handlers
3731      dummyEvent = {
3732        preventDefault: function() { this.defaultPrevented = true; },
3733        isDefaultPrevented: function() { return this.defaultPrevented === true; },
3734        stopImmediatePropagation: function() { this.immediatePropagationStopped = true; },
3735        isImmediatePropagationStopped: function() { return this.immediatePropagationStopped === true; },
3736        stopPropagation: noop,
3737        type: eventName,
3738        target: element
3739      };
3740
3741      // If a custom event was provided then extend our dummy event with it
3742      if (event.type) {
3743        dummyEvent = extend(dummyEvent, event);
3744      }
3745
3746      // Copy event handlers in case event handlers array is modified during execution.
3747      eventFnsCopy = shallowCopy(eventFns);
3748      handlerArgs = extraParameters ? [dummyEvent].concat(extraParameters) : [dummyEvent];
3749
3750      forEach(eventFnsCopy, function(fn) {
3751        if (!dummyEvent.isImmediatePropagationStopped()) {
3752          fn.apply(element, handlerArgs);
3753        }
3754      });
3755    }
3756  }
3757}, function(fn, name) {
3758  /**
3759   * chaining functions
3760   */
3761  JQLite.prototype[name] = function(arg1, arg2, arg3) {
3762    var value;
3763
3764    for (var i = 0, ii = this.length; i < ii; i++) {
3765      if (isUndefined(value)) {
3766        value = fn(this[i], arg1, arg2, arg3);
3767        if (isDefined(value)) {
3768          // any function which returns a value needs to be wrapped
3769          value = jqLite(value);
3770        }
3771      } else {
3772        jqLiteAddNodes(value, fn(this[i], arg1, arg2, arg3));
3773      }
3774    }
3775    return isDefined(value) ? value : this;
3776  };
3777
3778  // bind legacy bind/unbind to on/off
3779  JQLite.prototype.bind = JQLite.prototype.on;
3780  JQLite.prototype.unbind = JQLite.prototype.off;
3781});
3782
3783
3784// Provider for private $$jqLite service
3785function $$jqLiteProvider() {
3786  this.$get = function $$jqLite() {
3787    return extend(JQLite, {
3788      hasClass: function(node, classes) {
3789        if (node.attr) node = node[0];
3790        return jqLiteHasClass(node, classes);
3791      },
3792      addClass: function(node, classes) {
3793        if (node.attr) node = node[0];
3794        return jqLiteAddClass(node, classes);
3795      },
3796      removeClass: function(node, classes) {
3797        if (node.attr) node = node[0];
3798        return jqLiteRemoveClass(node, classes);
3799      }
3800    });
3801  };
3802}
3803
3804/**
3805 * Computes a hash of an 'obj'.
3806 * Hash of a:
3807 *  string is string
3808 *  number is number as string
3809 *  object is either result of calling $$hashKey function on the object or uniquely generated id,
3810 *         that is also assigned to the $$hashKey property of the object.
3811 *
3812 * @param obj
3813 * @returns {string} hash string such that the same input will have the same hash string.
3814 *         The resulting string key is in 'type:hashKey' format.
3815 */
3816function hashKey(obj, nextUidFn) {
3817  var key = obj && obj.$$hashKey;
3818
3819  if (key) {
3820    if (typeof key === 'function') {
3821      key = obj.$$hashKey();
3822    }
3823    return key;
3824  }
3825
3826  var objType = typeof obj;
3827  if (objType == 'function' || (objType == 'object' && obj !== null)) {
3828    key = obj.$$hashKey = objType + ':' + (nextUidFn || nextUid)();
3829  } else {
3830    key = objType + ':' + obj;
3831  }
3832
3833  return key;
3834}
3835
3836/**
3837 * HashMap which can use objects as keys
3838 */
3839function HashMap(array, isolatedUid) {
3840  if (isolatedUid) {
3841    var uid = 0;
3842    this.nextUid = function() {
3843      return ++uid;
3844    };
3845  }
3846  forEach(array, this.put, this);
3847}
3848HashMap.prototype = {
3849  /**
3850   * Store key value pair
3851   * @param key key to store can be any type
3852   * @param value value to store can be any type
3853   */
3854  put: function(key, value) {
3855    this[hashKey(key, this.nextUid)] = value;
3856  },
3857
3858  /**
3859   * @param key
3860   * @returns {Object} the value for the key
3861   */
3862  get: function(key) {
3863    return this[hashKey(key, this.nextUid)];
3864  },
3865
3866  /**
3867   * Remove the key/value pair
3868   * @param key
3869   */
3870  remove: function(key) {
3871    var value = this[key = hashKey(key, this.nextUid)];
3872    delete this[key];
3873    return value;
3874  }
3875};
3876
3877var $$HashMapProvider = [function() {
3878  this.$get = [function() {
3879    return HashMap;
3880  }];
3881}];
3882
3883/**
3884 * @ngdoc function
3885 * @module ng
3886 * @name angular.injector
3887 * @kind function
3888 *
3889 * @description
3890 * Creates an injector object that can be used for retrieving services as well as for
3891 * dependency injection (see {@link guide/di dependency injection}).
3892 *
3893 * @param {Array.<string|Function>}
3893 modules A list of module functions or their aliases. See
3894 *     {@link angular.module}. The `ng` module must be explicitly added.
3895 * @param {boolean=} [strictDi=false] Whether the injector should be in strict mode, which
3896 *     disallows argument name annotation inference.
3897 * @returns {injector} Injector object. See {@link auto.$injector $injector}.
3898 *
3899 * @example
3900 * Typical usage
3901 * ```js
3902 *   // create an injector
3903 *   var $injector = angular.injector(['ng']);
3904 *
3905 *   // use the injector to kick off your application
3906 *   // use the type inference to auto inject arguments, or use implicit injection
3907 *   $injector.invoke(function($rootScope, $compile, $document) {
3908 *     $compile($document)($rootScope);
3909 *     $rootScope.$digest();
3910 *   });
3911 * ```
3912 *
3913 * Sometimes you want to get access to the injector of a currently running Angular app
3914 * from outside Angular. Perhaps, you want to inject and compile some markup after the
3915 * application has been bootstrapped. You can do this using the extra `injector()` added
3916 * to JQuery/jqLite elements. See {@link angular.element}.
3917 *
3918 * *This is fairly rare but could be the case if a third party library is injecting the
3919 * markup.*
3920 *
3921 * In the following example a new block of HTML containing a `ng-controller`
3922 * directive is added to the end of the document body by JQuery. We then compile and link
3923 * it into the current AngularJS scope.
3924 *
3925 * ```js
3926 * var $div = $('<div ng-controller="MyCtrl">{{content.label}}</div>');
3927 * $(document.body).append($div);
3928 *
3929 * angular.element(document).injector().invoke(function($compile) {
3930 *   var scope = angular.element($div).scope();
3931 *   $compile($div)(scope);
3932 * });
3933 * ```
3934 */
3935
3936
3937/**
3938 * @ngdoc module
3939 * @name auto
3940 * @description
3941 *
3942 * Implicit module which gets automatically added to each {@link auto.$injector $injector}.
3943 */
3944
3945var FN_ARGS = /^[^\(]*\(\s*([^\)]*)\)/m;
3946var FN_ARG_SPLIT = /,/;
3947var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/;
3948var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
3949var $injectorMinErr = minErr('$injector');
3950
3951function anonFn(fn) {
3952  // For anonymous functions, showing at the very least the function signature can help in
3953  // debugging.
3954  var fnText = fn.toString().replace(STRIP_COMMENTS, ''),
3955      args = fnText.match(FN_ARGS);
3956  if (args) {
3957    return 'function(' + (args[1] || '').replace(/[\s\r\n]+/, ' ') + ')';
3958  }
3959  return 'fn';
3960}
3961
3962function annotate(fn, strictDi, name) {
3963  var $inject,
3964      fnText,
3965      argDecl,
3966      last;
3967
3968  if (typeof fn === 'function') {
3969    if (!($inject = fn.$inject)) {
3970      $inject = [];
3971      if (fn.length) {
3972        if (strictDi) {
3973          if (!isString(name) || !name) {
3974            name = fn.name || anonFn(fn);
3975          }
3976          throw $injectorMinErr('strictdi',
3977            '{0} is not using explicit annotation and cannot be invoked in strict mode', name);
3978        }
3979        fnText = fn.toString().replace(STRIP_COMMENTS, '');
3980        argDecl = fnText.match(FN_ARGS);
3981        forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg) {
3982          arg.replace(FN_ARG, function(all, underscore, name) {
3983            $inject.push(name);
3984          });
3985        });
3986      }
3987      fn.$inject = $inject;
3988    }
3989  } else if (isArray(fn)) {
3990    last = fn.length - 1;
3991    assertArgFn(fn[last], 'fn');
3992    $inject = fn.slice(0, last);
3993  } else {
3994    assertArgFn(fn, 'fn', true);
3995  }
3996  return $inject;
3997}
3998
3999///////////////////////////////////////
4000
4001/**
4002 * @ngdoc service
4003 * @name $injector
4004 *
4005 * @description
4006 *
4007 * `$injector` is used to retrieve object instances as defined by
4008 * {@link auto.$provide provider}, instantiate types, invoke methods,
4009 * and load modules.
4010 *
4011 * The following always holds true:
4012 *
4013 * ```js
4014 *   var $injector = angular.injector();
4015 *   expect($injector.get('$injector')).toBe($injector);
4016 *   expect($injector.invoke(function($injector) {
4017 *     return $injector;
4018 *   })).toBe($injector);
4019 * ```
4020 *
4021 * # Injection Function Annotation
4022 *
4023 * JavaScript does not have annotations, and annotations are needed for dependency injection. The
4024 * following are all valid ways of annotating function with injection arguments and are equivalent.
4025 *
4026 * ```js
4027 *   // inferred (only works if code not minified/obfuscated)
4028 *   $injector.invoke(function(serviceA){});
4029 *
4030 *   // annotated
4031 *   function explicit(serviceA) {};
4032 *   explicit.$inject = ['serviceA'];
4033 *   $injector.invoke(explicit);
4034 *
4035 *   // inline
4036 *   $injector.invoke(['serviceA', function(serviceA){}]);
4037 * ```
4038 *
4039 * ## Inference
4040 *
4041 * In JavaScript calling `toString()` on a function returns the function definition. The definition
4042 * can then be parsed and the function arguments can be extracted. This method of discovering
4043 * annotations is disallowed when the injector is in strict mode.
4044 * *NOTE:* This does not work with minification, and obfuscation tools since these tools change the
4045 * argument names.
4046 *
4047 * ## `$inject` Annotation
4048 * By adding an `$inject` property onto a function the injection parameters can be specified.
4049 *
4050 * ## Inline
4051 * As an array of injection names, where the last item in the array is the function to call.
4052 */
4053
4054/**
4055 * @ngdoc method
4056 * @name $injector#get
4057 *
4058 * @description
4059 * Return an instance of the service.
4060 *
4061 * @param {string} name The name of the instance to retrieve.
4062 * @param {string=} caller An optional string to provide the origin of the function call for error messages.
4063 * @return {*} The instance.
4064 */
4065
4066/**
4067 * @ngdoc method
4068 * @name $injector#invoke
4069 *
4070 * @description
4071 * Invoke the method and supply the method arguments from the `$injector`.
4072 *
4073 * @param {Function|Array.<string|Function>} fn The injectable function to invoke. Function parameters are
4074 *   injected according to the {@link guide/di $inject Annotation} rules.
4075 * @param {Object=} self The `this` for the invoked method.
4076 * @param {Object=} locals Optional object. If preset then any argument names are read from this
4077 *                         object first, before the `$injector` is consulted.
4078 * @returns {*} the value returned by the invoked `fn` function.
4079 */
4080
4081/**
4082 * @ngdoc method
4083 * @name $injector#has
4084 *
4085 * @description
4086 * Allows the user to query if the particular service exists.
4087 *
4088 * @param {string} name Name of the service to query.
4089 * @returns {boolean} `true` if injector has given service.
4090 */
4091
4092/**
4093 * @ngdoc method
4094 * @name $injector#instantiate
4095 * @description
4096 * Create a new instance of JS type. The method takes a constructor function, invokes the new
4097 * operator, and supplies all of the arguments to the constructor function as specified by the
4098 * constructor annotation.
4099 *
4100 * @param {Function} Type Annotated constructor function.
4101 * @param {Object=} locals Optional object. If preset then any argument names are read from this
4102 * object first, before the `$injector` is consulted.
4103 * @returns {Object} new instance of `Type`.
4104 */
4105
4106/**
4107 * @ngdoc method
4108 * @name $injector#annotate
4109 *
4110 * @description
4111 * Returns an array of service names which the function is requesting for injection. This API is
4112 * used by the injector to determine which services need to be injected into the function when the
4113 * function is invoked. There are three ways in which the function can be annotated with the needed
4114 * dependencies.
4115 *
4116 * # Argument names
4117 *
4118 * The simplest form is to extract the dependencies from the arguments of the function. This is done
4119 * by converting the function into a string using `toString()` method and extracting the argument
4120 * names.
4121 * ```js
4122 *   // Given
4123 *   function MyController($scope, $route) {
4124 *     // ...
4125 *   }
4126 *
4127 *   // Then
4128 *   expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
4129 * ```
4130 *
4131 * You can disallow this method by using strict injection mode.
4132 *
4133 * This method does not work with code minification / obfuscation. For this reason the following
4134 * annotation strategies are supported.
4135 *
4136 * # The `$inject` property
4137 *
4138 * If a function has an `$inject` property and its value is an array of strings, then the strings
4139 * represent names of services to be injected into the function.
4140 * ```js
4141 *   // Given
4142 *   var MyController = function(obfuscatedScope, obfuscatedRoute) {
4143 *     // ...
4144 *   }
4145 *   // Define function dependencies
4146 *   MyController['$inject'] = ['$scope', '$route'];
4147 *
4148 *   // Then
4149 *   expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
4150 * ```
4151 *
4152 * # The array notation
4153 *
4154 * It is often desirable to inline Injected functions and that's when setting the `$inject` property
4155 * is very inconvenient. In these situations using the array notation to specify the dependencies in
4156 * a way that survives minification is a better choice:
4157 *
4158 * ```js
4159 *   // We wish to write this (not minification / obfuscation safe)
4160 *   injector.invoke(function($compile, $rootScope) {
4161 *     // ...
4162 *   });
4163 *
4164 *   // We are forced to write break inlining
4165 *   var tmpFn = function(obfuscatedCompile, obfuscatedRootScope) {
4166 *     // ...
4167 *   };
4168 *   tmpFn.$inject = ['$compile', '$rootScope'];
4169 *   injector.invoke(tmpFn);
4170 *
4171 *   // To better support inline function the inline annotation is supported
4172 *   injector.invoke(['$compile', '$rootScope', function(obfCompile, obfRootScope) {
4173 *     // ...
4174 *   }]);
4175 *
4176 *   // Therefore
4177 *   expect(injector.annotate(
4178 *      ['$compile', '$rootScope', function(obfus_$compile, obfus_$rootScope) {}])
4179 *    ).toEqual(['$compile', '$rootScope']);
4180 * ```
4181 *
4182 * @param {Function|Array.<string|Function>} fn Function for which dependent service names need to
4183 * be retrieved as described above.
4184 *
4185 * @param {boolean=} [strictDi=false] Disallow argument name annotation inference.
4186 *
4187 * @returns {Array.<string>} The names of the services which the function requires.
4188 */
4189
4190
4191
4192
4193/**
4194 * @ngdoc service
4195 * @name $provide
4196 *
4197 * @description
4198 *
4199 * The {@link auto.$provide $provide} service has a number of methods for registering components
4200 * with the {@link auto.$injector $injector}. Many of these functions are also exposed on
4201 * {@link angular.Module}.
4202 *
4203 * An Angular **service** is a singleton object created by a **service factory**.  These **service
4204 * factories** are functions which, in turn, are created by a **service provider**.
4205 * The **service providers** are constructor functions. When instantiated they must contain a
4206 * property called `$get`, which holds the **service factory** function.
4207 *
4208 * When you request a service, the {@link auto.$injector $injector} is responsible for finding the
4209 * correct **service provider**, instantiating it and then calling its `$get` **service factory**
4210 * function to get the instance of the **service**.
4211 *
4212 * Often services have no configuration options and there is no need to add methods to the service
4213 * provider.  The provider will be no more than a constructor function with a `$get` property. For
4214 * these cases the {@link auto.$provide $provide} service has additional helper methods to register
4215 * services without specifying a provider.
4216 *
4217 * * {@link auto.$provide#provider provider(provider)} - registers a **service provider** with the
4218 *     {@link auto.$injector $injector}
4219 * * {@link auto.$provide#constant constant(obj)} - registers a value/object that can be accessed by
4220 *     providers and services.
4221 * * {@link auto.$provide#value value(obj)} - registers a value/object that can only be accessed by
4222 *     services, not providers.
4223 * * {@link auto.$provide#factory factory(fn)} - registers a service **factory function**, `fn`,
4224 *     that will be wrapped in a **service provider** object, whose `$get` property will contain the
4225 *     given factory function.
4226 * * {@link auto.$provide#service service(class)} - registers a **constructor function**, `class`
4227 *     that will be wrapped in a **service provider** object, whose `$get` property will instantiate
4228 *      a new object using the given constructor function.
4229 *
4230 * See the individual methods for more information and examples.
4231 */
4232
4233/**
4234 * @ngdoc method
4235 * @name $provide#provider
4236 * @description
4237 *
4238 * Register a **provider function** with the {@link auto.$injector $injector}. Provider functions
4239 * are constructor functions, whose instances are responsible for "providing" a factory for a
4240 * service.
4241 *
4242 * Service provider names start with the name of the service they provide followed by `Provider`.
4243 * For example, the {@link ng.$log $log} service has a provider called
4244 * {@link ng.$logProvider $logProvider}.
4245 *
4246 * Service provider objects can have additional methods which allow configuration of the provider
4247 * and its service. Importantly, you can configure what kind of service is created by the `$get`
4248 * method, or how that service will act. For example, the {@link ng.$logProvider $logProvider} has a
4249 * method {@link ng.$logProvider#debugEnabled debugEnabled}
4250 * which lets you specify whether the {@link ng.$log $log} service will log debug messages to the
4251 * console or not.
4252 *
4253 * @param {string} name The name of the instance. NOTE: the provider will be available under `name +
4254                        'Provider'` key.
4255 * @param {(Object|function())} provider If the provider is:
4256 *
4257 *   - `Object`: then it should have a `$get` method. The `$get` method will be invoked using
4258 *     {@link auto.$injector#invoke $injector.invoke()} when an instance needs to be created.
4259 *   - `Constructor`: a new instance of the provider will be created using
4260 *     {@link auto.$injector#instantiate $injector.instantiate()}, then treated as `object`.
4261 *
4262 * @returns {Object} registered provider instance
4263
4264 * @example
4265 *
4266 * The following example shows how to create a simple event tracking service and register it using
4267 * {@link auto.$provide#provider $provide.provider()}.
4268 *
4269 * ```js
4270 *  // Define the eventTracker provider
4271 *  function EventTrackerProvider() {
4272 *    var trackingUrl = '/track';
4273 *
4274 *    // A provider method for configuring where the tracked events should been saved
4275 *    this.setTrackingUrl = function(url) {
4276 *      trackingUrl = url;
4277 *    };
4278 *
4279 *    // The service factory function
4280 *    this.$get = ['$http', function($http) {
4281 *      var trackedEvents = {};
4282 *      return {
4283 *        // Call this to track an event
4284 *        event: function(event) {
4285 *          var count = trackedEvents[event] || 0;
4286 *          count += 1;
4287 *          trackedEvents[event] = count;
4288 *          return count;
4289 *        },
4290 *        // Call this to save the tracked events to the trackingUrl
4291 *        save: function() {
4292 *          $http.post(trackingUrl, trackedEvents);
4293 *        }
4294 *      };
4295 *    }];
4296 *  }
4297 *
4298 *  describe('eventTracker', function() {
4299 *    var postSpy;
4300 *
4301 *    beforeEach(module(function($provide) {
4302 *      // Register the eventTracker provider
4303 *      $provide.provider('eventTracker', EventTrackerProvider);
4304 *    }));
4305 *
4306 *    beforeEach(module(function(eventTrackerProvider) {
4307 *      // Configure eventTracker provider
4308 *      eventTrackerProvider.setTrackingUrl('/custom-track');
4309 *    }));
4310 *
4311 *    it('tracks events', inject(function(eventTracker) {
4312 *      expect(eventTracker.event('login')).toEqual(1);
4313 *      expect(eventTracker.event('login')).toEqual(2);
4314 *    }));
4315 *
4316 *    it('saves to the tracking url', inject(function(eventTracker, $http) {
4317 *      postSpy = spyOn($http, 'post');
4318 *      eventTracker.event('login');
4319 *      eventTracker.save();
4320 *      expect(postSpy).toHaveBeenCalled();
4321 *      expect(postSpy.mostRecentCall.args[0]).not.toEqual('/track');
4322 *      expect(postSpy.mostRecentCall.args[0]).toEqual('/custom-track');
4323 *      expect(postSpy.mostRecentCall.args[1]).toEqual({ 'login': 1 });
4324 *    }));
4325 *  });
4326 * ```
4327 */
4328
4329/**
4330 * @ngdoc method
4331 * @name $provide#factory
4332 * @description
4333 *
4334 * Register a **service factory**, which will be called to return the service instance.
4335 * This is short for registering a service where its provider consists of only a `$get` property,
4336 * which is the given service factory function.
4337 * You should use {@link auto.$provide#factory $provide.factory(getFn)} if you do not need to
4338 * configure your service in a provider.
4339 *
4340 * @param {string} name The name of the instance.
4341 * @param {Function|Array.<string|Function>} $getFn The injectable $getFn for the instance creation.
4342 *                      Internally this is a short hand for `$provide.provider(name, {$get: $getFn})`.
4343 * @returns {Object} registered provider instance
4344 *
4345 * @example
4346 * Here is an example of registering a service
4347 * ```js
4348 *   $provide.factory('ping', ['$http', function($http) {
4349 *     return function ping() {
4350 *       return $http.send('/ping');
4351 *     };
4352 *   }]);
4353 * ```
4354 * You would then inject and use this service like this:
4355 * ```js
4356 *   someModule.controller('Ctrl', ['ping', function(ping) {
4357 *     ping();
4358 *   }]);
4359 * ```
4360 */
4361
4362
4363/**
4364 * @ngdoc method
4365 * @name $provide#service
4366 * @description
4367 *
4368 * Register a **service constructor**, which will be invoked with `new` to create the service
4369 * instance.
4370 * This is short for registering a service where its provider's `$get` property is the service
4371 * constructor function that will be used to instantiate the service instance.
4372 *
4373 * You should use {@link auto.$provide#service $provide.service(class)} if you define your service
4374 * as a type/class.
4375 *
4376 * @param {string} name The name of the instance.
4377 * @param {Function|Array.<string|Function>} constructor An injectable class (constructor function)
4378 *     that will be instantiated.
4379 * @returns {Object} registered provider instance
4380 *
4381 * @example
4382 * Here is an example of registering a service using
4383 * {@link auto.$provide#service $provide.service(class)}.
4384 * ```js
4385 *   var Ping = function($http) {
4386 *     this.$http = $http;
4387 *   };
4388 *
4389 *   Ping.$inject = ['$http'];
4390 *
4391 *   Ping.prototype.send = function() {
4392 *     return this.$http.get('/ping');
4393 *   };
4394 *   $provide.service('ping', Ping);
4395 * ```
4396 * You would then inject and use this service like this:
4397 * ```js
4398 *   someModule.controller('Ctrl', ['ping', function(ping) {
4399 *     ping.send();
4400 *   }]);
4401 * ```
4402 */
4403
4404
4405/**
4406 * @ngdoc method
4407 * @name $provide#value
4408 * @description
4409 *
4410 * Register a **value service** with the {@link auto.$injector $injector}, such as a string, a
4411 * number, an array, an object or a function.  This is short for registering a service where its
4412 * provider's `$get` property is a factory function that takes no arguments and returns the **value
4413 * service**.
4414 *
4415 * Value services are similar to constant services, except that they cannot be injected into a
4416 * module configuration function (see {@link angular.Module#config}) but they can be overridden by
4417 * an Angular
4418 * {@link auto.$provide#decorator decorator}.
4419 *
4420 * @param {string} name The name of the instance.
4421 * @param {*} value The value.
4422 * @returns {Object} registered provider instance
4423 *
4424 * @example
4425 * Here are some examples of creating value services.
4426 * ```js
4427 *   $provide.value('ADMIN_USER', 'admin');
4428 *
4429 *   $provide.value('RoleLookup', { admin: 0, writer: 1, reader: 2 });
4430 *
4431 *   $provide.value('halfOf', function(value) {
4432 *     return value / 2;
4433 *   });
4434 * ```
4435 */
4436
4437
4438/**
4439 * @ngdoc method
4440 * @name $provide#constant
4441 * @description
4442 *
4443 * Register a **constant service**, such as a string, a number, an array, an object or a function,
4444 * with the {@link auto.$injector $injector}. Unlike {@link auto.$provide#value value} it can be
4445 * injected into a module configuration function (see {@link angular.Module#config}) and it cannot
4446 * be overridden by an Angular {@link auto.$provide#decorator decorator}.
4447 *
4448 * @param {string} name The name of the constant.
4449 * @param {*} value The constant value.
4450 * @returns {Object} registered instance
4451 *
4452 * @example
4453 * Here a some examples of creating constants:
4454 * ```js
4455 *   $provide.constant('SHARD_HEIGHT', 306);
4456 *
4457 *   $provide.constant('MY_COLOURS', ['red', 'blue', 'grey']);
4458 *
4459 *   $provide.constant('double', function(value) {
4460 *     return value * 2;
4461 *   });
4462 * ```
4463 */
4464
4465
4466/**
4467 * @ngdoc method
4468 * @name $provide#decorator
4469 * @description
4470 *
4471 * Register a **service decorator** with the {@link auto.$injector $injector}. A service decorator
4472 * intercepts the creation of a service, allowing it to override or modify the behaviour of the
4473 * service. The object returned by the decorator may be the original service, or a new service
4474 * object which replaces or wraps and delegates to the original service.
4475 *
4476 * @param {string} name The name of the service to decorate.
4477 * @param {Function|Array.<string|Function>} decorator This function will be invoked when the service needs to be
4478 *    instantiated and should return the decorated service instance. The function is called using
4479 *    the {@link auto.$injector#invoke injector.invoke} method and is therefore fully injectable.
4480 *    Local injection arguments:
4481 *
4482 *    * `$delegate` - The original service instance, which can be monkey patched, configured,
4483 *      decorated or delegated to.
4484 *
4485 * @example
4486 * Here we decorate the {@link ng.$log $log} service to convert warnings to errors by intercepting
4487 * calls to {@link ng.$log#error $log.warn()}.
4488 * ```js
4489 *   $provide.decorator('$log', ['$delegate', function($delegate) {
4490 *     $delegate.warn = $delegate.error;
4491 *     return $delegate;
4492 *   }]);
4493 * ```
4494 */
4495
4496
4497function createInjector(modulesToLoad, strictDi) {
4498  strictDi = (strictDi === true);
4499  var INSTANTIATING = {},
4500      providerSuffix = 'Provider',
4501      path = [],
4502      loadedModules = new HashMap([], true),
4503      providerCache = {
4504        $provide: {
4505            provider: supportObject(provider),
4506            factory: supportObject(factory),
4507            service: supportObject(service),
4508            value: supportObject(value),
4509            constant: supportObject(constant),
4510            decorator: decorator
4511          }
4512      },
4513      providerInjector = (providerCache.$injector =
4514          createInternalInjector(providerCache, function(serviceName, caller) {
4515            if (angular.isString(caller)) {
4516              path.push(caller);
4517            }
4518            throw $injectorMinErr('unpr', "Unknown provider: {0}", path.join(' <- '));
4519          })),
4520      instanceCache = {},
4521      instanceInjector = (instanceCache.$injector =
4522          createInternalInjector(instanceCache, function(serviceName, caller) {
4523            var provider = providerInjector.get(serviceName + providerSuffix, caller);
4524            return instanceInjector.invoke(provider.$get, provider, undefined, serviceName);
4525          }));
4526
4527
4528  forEach(loadModules(modulesToLoad), function(fn) { if (fn) instanceInjector.invoke(fn); });
4529
4530  return instanceInjector;
4531
4532  ////////////////////////////////////
4533  // $provider
4534  ////////////////////////////////////
4535
4536  function supportObject(delegate) {
4537    return function(key, value) {
4538      if (isObject(key)) {
4539        forEach(key, reverseParams(delegate));
4540      } else {
4541        return delegate(key, value);
4542      }
4543    };
4544  }
4545
4546  function provider(name, provider_) {
4547    assertNotHasOwnProperty(name, 'service');
4548    if (isFunction(provider_) || isArray(provider_)) {
4549      provider_ = providerInjector.instantiate(provider_);
4550    }
4551    if (!provider_.$get) {
4552      throw $injectorMinErr('pget', "Provider '{0}' must define $get factory method.", name);
4553    }
4554    return providerCache[name + providerSuffix] = provider_;
4555  }
4556
4557  function enforceReturnValue(name, factory) {
4558    return function enforcedReturnValue() {
4559      var result = instanceInjector.invoke(factory, this);
4560      if (isUndefined(result)) {
4561        throw $injectorMinErr('undef', "Provider '{0}' must return a value from $get factory method.", name);
4562      }
4563      return result;
4564    };
4565  }
4566
4567  function factory(name, factoryFn, enforce) {
4568    return provider(name, {
4569      $get: enforce !== false ? enforceReturnValue(name, factoryFn) : factoryFn
4570    });
4571  }
4572
4573  function service(name, constructor) {
4574    return factory(name, ['$injector', function($injector) {
4575      return $injector.instantiate(constructor);
4576    }]);
4577  }
4578
4579  function value(name, val) { return factory(name, valueFn(val), false); }
4580
4581  function constant(name, value) {
4582    assertNotHasOwnProperty(name, 'constant');
4583    providerCache[name] = value;
4584    instanceCache[name] = value;
4585  }
4586
4587  function decorator(serviceName, decorFn) {
4588    var origProvider = providerInjector.get(serviceName + providerSuffix),
4589        orig$get = origProvider.$get;
4590
4591    origProvider.$get = function() {
4592      var origInstance = instanceInjector.invoke(orig$get, origProvider);
4593      return instanceInjector.invoke(decorFn, null, {$delegate: origInstance});
4594    };
4595  }
4596
4597  ////////////////////////////////////
4598  // Module Loading
4599  ////////////////////////////////////
4600  function loadModules(modulesToLoad) {
4601    assertArg(isUndefined(modulesToLoad) || isArray(modulesToLoad), 'modulesToLoad', 'not an array');
4602    var runBlocks = [], moduleFn;
vendor: 5,290 bytes, lines 4603-4760
4603    forEach(modulesToLoad, function(module) {
4604      if (loadedModules.get(module)) return;
4605      loadedModules.put(module, true);
4606
4607      function runInvokeQueue(queue) {
4608        var i, ii;
4609        for (i = 0, ii = queue.length; i < ii; i++) {
4610          var invokeArgs = queue[i],
4611              provider = providerInjector.get(invokeArgs[0]);
4612
4613          provider[invokeArgs[1]].apply(provider, invokeArgs[2]);
4614        }
4615      }
4616
4617      try {
4618        if (isString(module)) {
4619          moduleFn = angularModule(module);
4620          runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks);
4621          runInvokeQueue(moduleFn._invokeQueue);
4622          runInvokeQueue(moduleFn._configBlocks);
4623        } else if (isFunction(module)) {
4624            runBlocks.push(providerInjector.invoke(module));
4625        } else if (isArray(module)) {
4626            runBlocks.push(providerInjector.invoke(module));
4627        } else {
4628          assertArgFn(module, 'module');
4629        }
4630      } catch (e) {
4631        if (isArray(module)) {
4632          module = module[module.length - 1];
4633        }
4634        if (e.message && e.stack && e.stack.indexOf(e.message) == -1) {
4635          // Safari & FF's stack traces don't contain error.message content
4636          // unlike those of Chrome and IE
4637          // So if stack doesn't contain message, we create a new string that contains both.
4638          // Since error.stack is read-only in Safari, I'm overriding e and not e.stack here.
4639          /* jshint -W022 */
4640          e = e.message + '\n' + e.stack;
4641        }
4642        throw $injectorMinErr('modulerr', "Failed to instantiate module {0} due to:\n{1}",
4643                  module, e.stack || e.message || e);
4644      }
4645    });
4646    return runBlocks;
4647  }
4648
4649  ////////////////////////////////////
4650  // internal Injector
4651  ////////////////////////////////////
4652
4653  function createInternalInjector(cache, factory) {
4654
4655    function getService(serviceName, caller) {
4656      if (cache.hasOwnProperty(serviceName)) {
4657        if (cache[serviceName] === INSTANTIATING) {
4658          throw $injectorMinErr('cdep', 'Circular dependency found: {0}',
4659                    serviceName + ' <- ' + path.join(' <- '));
4660        }
4661        return cache[serviceName];
4662      } else {
4663        try {
4664          path.unshift(serviceName);
4665          cache[serviceName] = INSTANTIATING;
4666          return cache[serviceName] = factory(serviceName, caller);
4667        } catch (err) {
4668          if (cache[serviceName] === INSTANTIATING) {
4669            delete cache[serviceName];
4670          }
4671          throw err;
4672        } finally {
4673          path.shift();
4674        }
4675      }
4676    }
4677
4678    function invoke(fn, self, locals, serviceName) {
4679      if (typeof locals === 'string') {
4680        serviceName = locals;
4681        locals = null;
4682      }
4683
4684      var args = [],
4685          $inject = createInjector.$$annotate(fn, strictDi, serviceName),
4686          length, i,
4687          key;
4688
4689      for (i = 0, length = $inject.length; i < length; i++) {
4690        key = $inject[i];
4691        if (typeof key !== 'string') {
4692          throw $injectorMinErr('itkn',
4693                  'Incorrect injection token! Expected service name as string, got {0}', key);
4694        }
4695        args.push(
4696          locals && locals.hasOwnProperty(key)
4697          ? locals[key]
4698          : getService(key, serviceName)
4699        );
4700      }
4701      if (isArray(fn)) {
4702        fn = fn[length];
4703      }
4704
4705      // http://jsperf.com/angularjs-invoke-apply-vs-switch
4706      // #5388
4707      return fn.apply(self, args);
4708    }
4709
4710    function instantiate(Type, locals, serviceName) {
4711      // Check if Type is annotated and use just the given function at n-1 as parameter
4712      // e.g. someModule.factory('greeter', ['$window', function(renamed$window) {}]);
4713      // Object creation: http://jsperf.com/create-constructor/2
4714      var instance = Object.create((isArray(Type) ? Type[Type.length - 1] : Type).prototype || null);
4715      var returnedValue = invoke(Type, instance, locals, serviceName);
4716
4717      return isObject(returnedValue) || isFunction(returnedValue) ? returnedValue : instance;
4718    }
4719
4720    return {
4721      invoke: invoke,
4722      instantiate: instantiate,
4723      get: getService,
4724      annotate: createInjector.$$annotate,
4725      has: function(name) {
4726        return providerCache.hasOwnProperty(name + providerSuffix) || cache.hasOwnProperty(name);
4727      }
4728    };
4729  }
4730}
4731
4732createInjector.$$annotate = annotate;
4733
4734/**
4735 * @ngdoc provider
4736 * @name $anchorScrollProvider
4737 *
4738 * @description
4739 * Use `$anchorScrollProvider` to disable automatic scrolling whenever
4740 * {@link ng.$location#hash $location.hash()} changes.
4741 */
4742function $AnchorScrollProvider() {
4743
4744  var autoScrollingEnabled = true;
4745
4746  /**
4747   * @ngdoc method
4748   * @name $anchorScrollProvider#disableAutoScrolling
4749   *
4750   * @description
4751   * By default, {@link ng.$anchorScroll $anchorScroll()} will automatically detect changes to
4752   * {@link ng.$location#hash $location.hash()} and scroll to the element matching the new hash.<br />
4753   * Use this method to disable automatic scrolling.
4754   *
4755   * If automatic scrolling is disabled, one must explicitly call
4756   * {@link ng.$anchorScroll $anchorScroll()} in order to scroll to the element related to the
4757   * current hash.
4758   */
4759  this.disableAutoScrolling = function() {
4760    autoScrollingEnabled = false;
4761  };
4762
4763  /**
4764   * @ngdoc service
4765   * @name $anchorScroll
4766   * @kind function
4767   * @requires $window
4768   * @requires $location
4769   * @requires $rootScope
4770   *
4771   * @description
4772   * When called, it scrolls to the element related to the specified `hash` or (if omitted) to the
4773   * current value of {@link ng.$location#hash $location.hash()}, according to the rules specified
4774   * in the
4775   * [HTML5 spec](http://dev.w3.org/html5/spec/Overview.html#the-indicated-part-of-the-document).
4776   *
4777   * It also watches the {@link ng.$location#hash $location.hash()} and automatically scrolls to
4778   * match any anchor whenever it changes. This can be disabled by calling
4779   * {@link ng.$anchorScrollProvider#disableAutoScrolling $anchorScrollProvider.disableAutoScrolling()}.
4780   *
4781   * Additionally, you can use its {@link ng.$anchorScroll#yOffset yOffset} property to specify a
4782   * vertical scroll-offset (either fixed or dynamic).
4783   *
4784   * @param {string=} hash The hash specifying the element to scroll to. If omitted, the value of
4785   *                       {@link ng.$location#hash $location.hash()} will be used.
4786   *
4787   * @property {(number|function|jqLite)} yOffset
4788   * If set, specifies a vertical scroll-offset. This is often useful when there are fixed
4789   * positioned elements at the top of the page, such as navbars, headers etc.
4790   *
4791   * `yOffset` can be specified in various ways:
4792   * - **number**: A fixed number of pixels to be used as offset.<br /><br />
4793   * - **function**: A getter function called everytime `$anchorScroll()` is executed. Must return
4794   *   a number representing the offset (in pixels).<br /><br />
4795   * - **jqLite**: A jqLite/jQuery element to be used for specifying the offset. The distance from
4796   *   the top of the page to the element's bottom will be used as offset.<br />
4797   *   **Note**: The element will be taken into account only as long as its `position` is set to
4798   *   `fixed`. This option is useful, when dealing with responsive navbars/headers that adjust
4799   *   their height and/or positioning according to the viewport's size.
4800   *
4801   * <br />
4802   * <div class="alert alert-warning">
4803   * In order for `yOffset` to work properly, scrolling should take place on the document's root and
4804   * not some child element.
4805   * </div>
4806   *
4807   * @example
4808     <example module="anchorScrollExample">
4809       <file name="index.html">
4810         <div id="scrollArea" ng-controller="ScrollController">
4811           <a ng-click="gotoBottom()">Go to bottom</a>
4812           <a id="bottom"></a> You're at the bottom!
4813         </div>
4814       </file>
4815       <file name="script.js">
4816         angular.module('anchorScrollExample', [])
4817           .controller('ScrollController', ['$scope', '$location', '$anchorScroll',
4818             function ($scope, $location, $anchorScroll) {
4819               $scope.gotoBottom = function() {
4820                 // set the location.hash to the id of
4821                 // the element you wish to scroll to.
4822                 $location.hash('bottom');
4823
4824                 // call $anchorScroll()
4825                 $anchorScroll();
4826               };
4827             }]);
4828       </file>
4829       <file name="style.css">
4830         #scrollArea {
4831           height: 280px;
4832           overflow: auto;
4833         }
4834
4835         #bottom {
4836           display: block;
4837           margin-top: 2000px;
4838         }
4839       </file>
4840     </example>
4841   *
4842   * <hr />
4843   * The example below illustrates the use of a vertical scroll-offset (specified as a fixed value).
4844   * See {@link ng.$anchorScroll#yOffset $anchorScroll.yOffset} for more details.
4845   *
4846   * @example
4847     <example module="anchorScrollOffsetExample">
4848       <file name="index.html">
4849         <div class="fixed-header" ng-controller="headerCtrl">
4850           <a href="" ng-click="gotoAnchor(x)" ng-repeat="x in [1,2,3,4,5]">
4851             Go to anchor {{x}}
4852           </a>
4853         </div>
4854         <div id="anchor{{x}}" class="anchor" ng-repeat="x in [1,2,3,4,5]">
4855           Anchor {{x}} of 5
4856         </div>
4857       </file>
4858       <file name="script.js">
4859         angular.module('anchorScrollOffsetExample', [])
4860           .run(['$anchorScroll', function($anchorScroll) {
4861             $anchorScroll.yOffset = 50;   // always scroll by 50 extra pixels
4862           }])
4863           .controller('headerCtrl', ['$anchorScroll', '$location', '$scope',
4864             function ($anchorScroll, $location, $scope) {
4865               $scope.gotoAnchor = function(x) {
4866                 var newHash = 'anchor' + x;
4867                 if ($location.hash() !== newHash) {
4868                   // set the $location.hash to `newHash` and
4869                   // $anchorScroll will automatically scroll to it
4870                   $location.hash('anchor' + x);
4871                 } else {
4872                   // call $anchorScroll() explicitly,
4873                   // since $location.hash hasn't changed
4874                   $anchorScroll();
4875                 }
4876               };
4877             }
4878           ]);
4879       </file>
4880       <file name="style.css">
4881         body {
4882           padding-top: 50px;
4883         }
4884
4885         .anchor {
4886           border: 2px dashed DarkOrchid;
4887           padding: 10px 10px 200px 10px;
4888         }
4889
4890         .fixed-header {
4891           background-color: rgba(0, 0, 0, 0.2);
4892           height: 50px;
4893           position: fixed;
4894           top: 0; left: 0; right: 0;
4895         }
4896
4897         .fixed-header > a {
4898           display: inline-block;
4899           margin: 5px 15px;
4900         }
4901       </file>
4902     </example>
4903   */
4904  this.$get = ['$window', '$location', '$rootScope', function($window, $location, $rootScope) {
4905    var document = $window.document;
4906
4907    // Helper function to get first anchor from a NodeList
4908    // (using `Array#some()` instead of `angular#forEach()` since it's more performant
4909    //  and working in all supported browsers.)
4910    function getFirstAnchor(list) {
4911      var result = null;
4912      Array.prototype.some.call(list, function(element) {
4913        if (nodeName_(element) === 'a') {
4914          result = element;
4915          return true;
4916        }
4917      });
4918      return result;
4919    }
4920
4921    function getYOffset() {
4922
4923      var offset = scroll.yOffset;
4924
4925      if (isFunction(offset)) {
4926        offset = offset();
4927      } else if (isElement(offset)) {
4928        var elem = offset[0];
4929        var style = $window.getComputedStyle(elem);
4930        if (style.position !== 'fixed') {
4931          offset = 0;
4932        } else {
4933          offset = elem.getBoundingClientRect().bottom;
4934        }
4935      } else if (!isNumber(offset)) {
4936        offset = 0;
4937      }
4938
4939      return offset;
4940    }
4941
4942    function scrollTo(elem) {
4943      if (elem) {
4944        elem.scrollIntoView();
4945
4946        var offset = getYOffset();
4947
4948        if (offset) {
4949          // `offset` is the number of pixels we should scroll UP in order to align `elem` properly.
4950          // This is true ONLY if the call to `elem.scrollIntoView()` initially aligns `elem` at the
4951          // top of the viewport.
4952          //
4953          // IF the number of pixels from the top of `elem` to the end of the page's content is less
4954          // than the height of the viewport, then `elem.scrollIntoView()` will align the `elem` some
4955          // way down the page.
4956          //
4957          // This is often the case for elements near the bottom of the page.
4958          //
4959          // In such cases we do not need to scroll the whole `offset` up, just the difference between
4960          // the top of the element and the offset, which is enough to align the top of `elem` at the
4961          // desired position.
4962          var elemTop = elem.getBoundingClientRect().top;
4963          $window.scrollBy(0, elemTop - offset);
4964        }
4965      } else {
4966        $window.scrollTo(0, 0);
4967      }
4968    }
4969
4970    function scroll(hash) {
4971      hash = isString(hash) ? hash : $location.hash();
4972      var elm;
4973
4974      // empty hash, scroll to the top of the page
4975      if (!hash) scrollTo(null);
4976
4977      // element with given id
4978      else if ((elm = document.getElementById(hash))) scrollTo(elm);
4979
4980      // first anchor with given name :-D
4981      else if ((elm = getFirstAnchor(document.getElementsByName(hash)))) scrollTo(elm);
4982
4983      // no element and hash == 'top', scroll to the top of the page
4984      else if (hash === 'top') scrollTo(null);
4985    }
4986
4987    // does not scroll when user clicks on anchor link that is currently on
4988    // (no url change, no $location.hash() change), browser native does scroll
4989    if (autoScrollingEnabled) {
4990      $rootScope.$watch(function autoScrollWatch() {return $location.hash();},
4991        function autoScrollWatchAction(newVal, oldVal) {
4992          // skip the initial scroll if $location.hash is empty
4993          if (newVal === oldVal && newVal === '') return;
4994
4995          jqLiteDocumentLoaded(function() {
4996            $rootScope.$evalAsync(scroll);
4997          });
4998        });
4999    }
5000
5001    return scroll;
5002  }];
5003}
5004
5005var $animateMinErr = minErr('$animate');
5006var ELEMENT_NODE = 1;
5007var NG_ANIMATE_CLASSNAME = 'ng-animate';
5008
5009function mergeClasses(a,b) {
5010  if (!a && !b) return '';
5011  if (!a) return b;
5012  if (!b) return a;
5013  if (isArray(a)) a = a.join(' ');
5014  if (isArray(b)) b = b.join(' ');
5015  return a + ' ' + b;
5016}
5017
5018function extractElementNode(element) {
5019  for (var i = 0; i < element.length; i++) {
5020    var elm = element[i];
5021    if (elm.nodeType === ELEMENT_NODE) {
5022      return elm;
5023    }
5024  }
5025}
5026
5027function splitClasses(classes) {
5028  if (isString(classes)) {
5029    classes = classes.split(' ');
5030  }
5031
5032  // Use createMap() to prevent class assumptions involving property names in
5033  // Object.prototype
5034  var obj = createMap();
5035  forEach(classes, function(klass) {
5036    // sometimes the split leaves empty string values
5037    // incase extra spaces were applied to the options
5038    if (klass.length) {
5039      obj[klass] = true;
5040    }
5041  });
5042  return obj;
5043}
5044
5045// if any other type of options value besides an Object value is
5046// passed into the $animate.method() animation then this helper code
5047// will be run which will ignore it. While this patch is not the
5048// greatest solution to this, a lot of existing plugins depend on
5049// $animate to either call the callback (< 1.2) or return a promise
5050// that can be changed. This helper function ensures that the options
5051// are wiped clean incase a callback function is provided.
5052function prepareAnimateOptions(options) {
5053  return isObject(options)
5054      ? options
5055      : {};
5056}
5057
5058var $$CoreAnimateRunnerProvider = function() {
5059  this.$get = ['$q', '$$rAF', function($q, $$rAF) {
5060    function AnimateRunner() {}
5061    AnimateRunner.all = noop;
5062    AnimateRunner.chain = noop;
5063    AnimateRunner.prototype = {
5064      end: noop,
5065      cancel: noop,
5066      resume: noop,
5067      pause: noop,
5068      complete: noop,
5069      then: function(pass, fail) {
5070        return $q(function(resolve) {
5071          $$rAF(function() {
5072            resolve();
5073          });
5074        }).then(pass, fail);
5075      }
5076    };
5077    return AnimateRunner;
5078  }];
5079};
5080
5081// this is prefixed with Core since it conflicts with
5082// the animateQueueProvider defined in ngAnimate/animateQueue.js
5083var $$CoreAnimateQueueProvider = function() {
5084  var postDigestQueue = new HashMap();
5085  var postDigestElements = [];
5086
5087  this.$get = ['$$AnimateRunner', '$rootScope',
5088       function($$AnimateRunner,   $rootScope) {
5089    return {
5090      enabled: noop,
5091      on: noop,
5092      off: noop,
5093      pin: noop,
5094
5095      push: function(element, event, options, domOperation) {
5096        domOperation        && domOperation();
5097
5098        options = options || {};
5099        options.from        && element.css(options.from);
5100        options.to          && element.css(options.to);
5101
5102        if (options.addClass || options.removeClass) {
5103          addRemoveClassesPostDigest(element, options.addClass, options.removeClass);
5104        }
5105
5106        return new $$AnimateRunner(); // jshint ignore:line
5107      }
5108    };
5109
5110
5111    function updateData(data, classes, value) {
5112      var changed = false;
vendor: 6,104 bytes, lines 5113-5271
5113      if (classes) {
5114        classes = isString(classes) ? classes.split(' ') :
5115                  isArray(classes) ? classes : [];
5116        forEach(classes, function(className) {
5117          if (className) {
5118            changed = true;
5119            data[className] = value;
5120          }
5121        });
5122      }
5123      return changed;
5124    }
5125
5126    function handleCSSClassChanges() {
5127      forEach(postDigestElements, function(element) {
5128        var data = postDigestQueue.get(element);
5129        if (data) {
5130          var existing = splitClasses(element.attr('class'));
5131          var toAdd = '';
5132          var toRemove = '';
5133          forEach(data, function(status, className) {
5134            var hasClass = !!existing[className];
5135            if (status !== hasClass) {
5136              if (status) {
5137                toAdd += (toAdd.length ? ' ' : '') + className;
5138              } else {
5139                toRemove += (toRemove.length ? ' ' : '') + className;
5140              }
5141            }
5142          });
5143
5144          forEach(element, function(elm) {
5145            toAdd    && jqLiteAddClass(elm, toAdd);
5146            toRemove && jqLiteRemoveClass(elm, toRemove);
5147          });
5148          postDigestQueue.remove(element);
5149        }
5150      });
5151      postDigestElements.length = 0;
5152    }
5153
5154
5155    function addRemoveClassesPostDigest(element, add, remove) {
5156      var data = postDigestQueue.get(element) || {};
5157
5158      var classesAdded = updateData(data, add, true);
5159      var classesRemoved = updateData(data, remove, false);
5160
5161      if (classesAdded || classesRemoved) {
5162
5163        postDigestQueue.put(element, data);
5164        postDigestElements.push(element);
5165
5166        if (postDigestElements.length === 1) {
5167          $rootScope.$$postDigest(handleCSSClassChanges);
5168        }
5169      }
5170    }
5171  }];
5172};
5173
5174/**
5175 * @ngdoc provider
5176 * @name $animateProvider
5177 *
5178 * @description
5179 * Default implementation of $animate that doesn't perform any animations, instead just
5180 * synchronously performs DOM updates and resolves the returned runner promise.
5181 *
5182 * In order to enable animations the `ngAnimate` module has to be loaded.
5183 *
5184 * To see the functional implementation check out `src/ngAnimate/animate.js`.
5185 */
5186var $AnimateProvider = ['$provide', function($provide) {
5187  var provider = this;
5188
5189  this.$$registeredAnimations = Object.create(null);
5190
5191   /**
5192   * @ngdoc method
5193   * @name $animateProvider#register
5194   *
5195   * @description
5196   * Registers a new injectable animation factory function. The factory function produces the
5197   * animation object which contains callback functions for each event that is expected to be
5198   * animated.
5199   *
5200   *   * `eventFn`: `function(element, ... , doneFunction, options)`
5201   *   The element to animate, the `doneFunction` and the options fed into the animation. Depending
5202   *   on the type of animation additional arguments will be injected into the animation function. The
5203   *   list below explains the function signatures for the different animation methods:
5204   *
5205   *   - setClass: function(element, addedClasses, removedClasses, doneFunction, options)
5206   *   - addClass: function(element, addedClasses, doneFunction, options)
5207   *   - removeClass: function(element, removedClasses, doneFunction, options)
5208   *   - enter, leave, move: function(element, doneFunction, options)
5209   *   - animate: function(element, fromStyles, toStyles, doneFunction, options)
5210   *
5211   *   Make sure to trigger the `doneFunction` once the animation is fully complete.
5212   *
5213   * ```js
5214   *   return {
5215   *     //enter, leave, move signature
5216   *     eventFn : function(element, done, options) {
5217   *       //code to run the animation
5218   *       //once complete, then run done()
5219   *       return function endFunction(wasCancelled) {
5220   *         //code to cancel the animation
5221   *       }
5222   *     }
5223   *   }
5224   * ```
5225   *
5226   * @param {string} name The name of the animation (this is what the class-based CSS value will be compared to).
5227   * @param {Function} factory The factory function that will be executed to return the animation
5228   *                           object.
5229   */
5230  this.register = function(name, factory) {
5231    if (name && name.charAt(0) !== '.') {
5232      throw $animateMinErr('notcsel', "Expecting class selector starting with '.' got '{0}'.", name);
5233    }
5234
5235    var key = name + '-animation';
5236    provider.$$registeredAnimations[name.substr(1)] = key;
5237    $provide.factory(key, factory);
5238  };
5239
5240  /**
5241   * @ngdoc method
5242   * @name $animateProvider#classNameFilter
5243   *
5244   * @description
5245   * Sets and/or returns the CSS class regular expression that is checked when performing
5246   * an animation. Upon bootstrap the classNameFilter value is not set at all and will
5247   * therefore enable $animate to attempt to perform an animation on any element that is triggered.
5248   * When setting the `classNameFilter` value, animations will only be performed on elements
5249   * that successfully match the filter expression. This in turn can boost performance
5250   * for low-powered devices as well as applications containing a lot of structural operations.
5251   * @param {RegExp=} expression The className expression which will be checked against all animations
5252   * @return {RegExp} The current CSS className expression value. If null then there is no expression value
5253   */
5254  this.classNameFilter = function(expression) {
5255    if (arguments.length === 1) {
5256      this.$$classNameFilter = (expression instanceof RegExp) ? expression : null;
5257      if (this.$$classNameFilter) {
5258        var reservedRegex = new RegExp("(\\s+|\\/)" + NG_ANIMATE_CLASSNAME + "(\\s+|\\/)");
5259        if (reservedRegex.test(this.$$classNameFilter.toString())) {
5260          throw $animateMinErr('nongcls','$animateProvider.classNameFilter(regex) prohibits accepting a regex value which matches/contains the "{0}" CSS class.', NG_ANIMATE_CLASSNAME);
5261
5262        }
5263      }
5264    }
5265    return this.$$classNameFilter;
5266  };
5267
5268  this.$get = ['$$animateQueue', function($$animateQueue) {
5269    function domInsert(element, parentElement, afterElement) {
5270      // if for some reason the previous element was removed
5271      // from the dom sometime before this code runs then let's
5272      // just stick to using the parent element as the anchor
5273      if (afterElement) {
5274        var afterNode = extractElementNode(afterElement);
5275        if (afterNode && !afterNode.parentNode && !afterNode.previousElementSibling) {
5276          afterElement = null;
5277        }
5278      }
5279      afterElement ? afterElement.after(element) : parentElement.prepend(element);
5280    }
5281
5282    /**
5283     * @ngdoc service
5284     * @name $animate
5285     * @description The $animate service exposes a series of DOM utility methods that provide support
5286     * for animation hooks. The default behavior is the application of DOM operations, however,
5287     * when an animation is detected (and animations are enabled), $animate will do the heavy lifting
5288     * to ensure that animation runs with the triggered DOM operation.
5289     *
5290     * By default $animate doesn't trigger an animations. This is because the `ngAnimate` module isn't
5291     * included and only when it is active then the animation hooks that `$animate` triggers will be
5292     * functional. Once active then all structural `ng-` directives will trigger animations as they perform
5293     * their DOM-related operations (enter, leave and move). Other directives such as `ngClass`,
5294     * `ngShow`, `ngHide` and `ngMessages` also provide support for animations.
5295     *
5296     * It is recommended that the`$animate` service is always used when executing DOM-related procedures within directives.
5297     *
5298     * To learn more about enabling animation support, click here to visit the
5299     * {@link ngAnimate ngAnimate module page}.
5300     */
5301    return {
5302      // we don't call it directly since non-existant arguments may
5303      // be interpreted as null within the sub enabled function
5304
5305      /**
5306       *
5307       * @ngdoc method
5308       * @name $animate#on
5309       * @kind function
5310       * @description Sets up an event listener to fire whenever the animation event (enter, leave, move, etc...)
5311       *    has fired on the given element or among any of its children. Once the listener is fired, the provided callback
5312       *    is fired with the following params:
5313       *
5314       * ```js
5315       * $animate.on('enter', container,
5316       *    function callback(element, phase) {
5317       *      // cool we detected an enter animation within the container
5318       *    }
5319       * );
5320       * ```
5321       *
5322       * @param {string} event the animation event that will be captured (e.g. enter, leave, move, addClass, removeClass, etc...)
5323       * @param {DOMElement} container the container element that will capture each of the animation events that are fired on itself
5324       *     as well as among its children
5325       * @param {Function} callback the callback function that will be fired when the listener is triggered
5326       *
5327       * The arguments present in the callback function are:
5328       * * `element` - The captured DOM element that the animation was fired on.
5329       * * `phase` - The phase of the animation. The two possible phases are **start** (when the animation starts) and **close** (when it ends).
5330       */
5331      on: $$animateQueue.on,
5332
5333      /**
5334       *
5335       * @ngdoc method
5336       * @name $animate#off
5337       * @kind function
5338       * @description Deregisters an event listener based on the event which has been associated with the provided element. This method
5339       * can be used in three different ways depending on the arguments:
5340       *
5341       * ```js
5342       * // remove all the animation event listeners listening for `enter`
5343       * $animate.off('enter');
5344       *
5345       * // remove all the animation event listeners listening for `enter` on the given element and its children
5346       * $animate.off('enter', container);
5347       *
5348       * // remove the event listener function provided by `listenerFn` that is set
5349       * // to listen for `enter` on the given `element` as well as its children
5350       * $animate.off('enter', container, callback);
5351       * ```
5352       *
5353       * @param {string} event the animation event (e.g. enter, leave, move, addClass, removeClass, etc...)
5354       * @param {DOMElement=} container the container element the event listener was placed on
5355       * @param {Function=} callback the callback function that was registered as the listener
5356       */
5357      off: $$animateQueue.off,
5358
5359      /**
5360       * @ngdoc method
5361       * @name $animate#pin
5362       * @kind function
5363       * @description Associates the provided element with a host parent element to allow the element to be animated even if it exists
5364       *    outside of the DOM structure of the Angular application. By doing so, any animation triggered via `$animate` can be issued on the
5365       *    element despite being outside the realm of the application or within another application. Say for example if the application
5366       *    was bootstrapped on an element that is somewhere inside of the `<body>` tag, but we wanted to allow for an element to be situated
5367       *    as a direct child of `document.body`, then this can be achieved by pinning the element via `$animate.pin(element)`. Keep in mind
5368       *    that calling `$animate.pin(element, parentElement)` will not actually insert into the DOM anywhere; it will just create the association.
5369       *
5370       *    Note that this feature is only active when the `ngAnimate` module is used.
5371       *
5372       * @param {DOMElement} element the external element that will be pinned
5373       * @param {DOMElement} parentElement the host parent element that will be associated with the external element
5374       */
5375      pin: $$animateQueue.pin,
5376
5377      /**
5378       *
5379       * @ngdoc method
5380       * @name $animate#enabled
5381       * @kind function
5382       * @description Used to get and set whether animations are enabled or not on the entire application or on an element and its children. This
5383       * function can be called in four ways:
5384       *
5385       * ```js
5386       * // returns true or false
5387       * $animate.enabled();
5388       *
5389       * // changes the enabled state for all animations
5390       * $animate.enabled(false);
5391       * $animate.enabled(true);
5392       *
5393       * // returns true or false if animations are enabled for an element
5394       * $animate.enabled(element);
5395       *
5396       * // changes the enabled state for an element and its children
5397       * $animate.enabled(element, true);
5398       * $animate.enabled(element, false);
5399       * ```
5400       *
5401       * @param {DOMElement=} element the element that will be considered for checking/setting the enabled state
5402       * @param {boolean=} enabled whether or not the animations will be enabled for the element
5403       *
5404       * @return {boolean} whether or not animations are enabled
5405       */
5406      enabled: $$animateQueue.enabled,
5407
5408      /**
5409       * @ngdoc method
5410       * @name $animate#cancel
5411       * @kind function
5412       * @description Cancels the provided animation.
5413       *
5414       * @param {Promise} animationPromise The animation promise that is returned when an animation is started.
5415       */
5416      cancel: function(runner) {
5417        runner.end && runner.end();
5418      },
5419
5420      /**
5421       *
5422       * @ngdoc method
5423       * @name $animate#enter
5424       * @kind function
5425       * @description Inserts the element into the DOM either after the `after` element (if provided) or
5426       *   as the first child within the `parent` element and then triggers an animation.
5427       *   A promise is returned that will be resolved during the next digest once the animation
5428       *   has completed.
5429       *
5430       * @param {DOMElement} element the element which will be inserted into the DOM
5431       * @param {DOMElement} parent the parent element which will append the element as
5432       *   a child (so long as the after element is not present)
5433       * @param {DOMElement=} after the sibling element after which the element will be appended
5434       * @param {object=} options an optional collection of options/styles that will be applied to the element
5435       *
5436       * @return {Promise} the animation callback promise
5437       */
5438      enter: function(element, parent, after, options) {
5439        parent = parent && jqLite(parent);
5440        after = after && jqLite(after);
5441        parent = parent || after.parent();
5442        domInsert(element, parent, after);
5443        return $$animateQueue.push(element, 'enter', prepareAnimateOptions(options));
5444      },
5445
5446      /**
5447       *
5448       * @ngdoc method
5449       * @name $animate#move
5450       * @kind function
5451       * @description Inserts (moves) the element into its new position in the DOM either after
5452       *   the `after` element (if provided) or as the first child within the `parent` element
5453       *   and then triggers an animation. A promise is returned that will be resolved
5454       *   during the next digest once the animation has completed.
5455       *
5456       * @param {DOMElement} element the element which will be moved into the new DOM position
5457       * @param {DOMElement} parent the parent element which will append the element as
5458       *   a child (so long as the after element is not present)
5459       * @param {DOMElement=} after the sibling element after which the element will be appended
5460       * @param {object=} options an optional collection of options/styles that will be applied to the element
5461       *
5462       * @return {Promise} the animation callback promise
5463       */
5464      move: function(element, parent, after, options) {
5465        parent = parent && jqLite(parent);
5466        after = after && jqLite(after);
5467        parent = parent || after.parent();
5468        domInsert(element, parent, after);
5469        return $$animateQueue.push(element, 'move', prepareAnimateOptions(options));
5470      },
5471
5472      /**
5473       * @ngdoc method
5474       * @name $animate#leave
5475       * @kind function
5476       * @description Triggers an animation and then removes the element from the DOM.
5477       * When the function is called a promise is returned that will be resolved during the next
5478       * digest once the animation has completed.
5479       *
5480       * @param {DOMElement} element the element which will be removed from the DOM
5481       * @param {object=} options an optional collection of options/styles that will be applied to the element
5482       *
5483       * @return {Promise} the animation callback promise
5484       */
5485      leave: function(element, options) {
5486        return $$animateQueue.push(element, 'leave', prepareAnimateOptions(options), function() {
5487          element.remove();
5488        });
5489      },
5490
5491      /**
5492       * @ngdoc method
5493       * @name $animate#addClass
5494       * @kind function
5495       *
5496       * @description Triggers an addClass animation surrounding the addition of the provided CSS class(es). Upon
5497       *   execution, the addClass operation will only be handled after the next digest and it will not trigger an
5498       *   animation if element already contains the CSS class or if the class is removed at a later step.
5499       *   Note that class-based animations are treated differently compared to structural animations
5500       *   (like enter, move and leave) since the CSS classes may be added/removed at different points
5501       *   depending if CSS or JavaScript animations are used.
5502       *
5503       * @param {DOMElement} element the element which the CSS classes will be applied to
5504       * @param {string} className the CSS class(es) that will be added (multiple classes are separated via spaces)
5505       * @param {object=} options an optional collection of options/styles that will be applied to the element
5506       *
5507       * @return {Promise} the animation callback promise
5508       */
5509      addClass: function(element, className, options) {
5510        options = prepareAnimateOptions(options);
5511        options.addClass = mergeClasses(options.addclass, className);
5512        return $$animateQueue.push(element, 'addClass', options);
5513      },
5514
5515      /**
5516       * @ngdoc method
5517       * @name $animate#removeClass
5518       * @kind function
5519       *
5520       * @description Triggers a removeClass animation surrounding the removal of the provided CSS class(es). Upon
5521       *   execution, the removeClass operation will only be handled after the next digest and it will not trigger an
5522       *   animation if element does not contain the CSS class or if the class is added at a later step.
5523       *   Note that class-based animations are treated differently compared to structural animations
5524       *   (like enter, move and leave) since the CSS classes may be added/removed at different points
5525       *   depending if CSS or JavaScript animations are used.
5526       *
5527       * @param {DOMElement} element the element which the CSS classes will be applied to
5528       * @param {string} className the CSS class(es) that will be removed (multiple classes are separated via spaces)
5529       * @param {object=} options an optional collection of options/styles that will be applied to the element
5530       *
5531       * @return {Promise} the animation callback promise
5532       */
5533      removeClass: function(element, className, options) {
5534        options = prepareAnimateOptions(options);
5535        options.removeClass = mergeClasses(options.removeClass, className);
5536        return $$animateQueue.push(element, 'removeClass', options);
5537      },
5538
5539      /**
5540       * @ngdoc method
5541       * @name $animate#setClass
5542       * @kind function
5543       *
5544       * @description Performs both the addition and removal of a CSS classes on an element and (during the process)
5545       *    triggers an animation surrounding the class addition/removal. Much like `$animate.addClass` and
5546       *    `$animate.removeClass`, `setClass` will only evaluate the classes being added/removed once a digest has
5547       *    passed. Note that class-based animations are treated differently compared to structural animations
5548       *    (like enter, move and leave) since the CSS classes may be added/removed at different points
5549       *    depending if CSS or JavaScript animations are used.
5550       *
5551       * @param {DOMElement} element the element which the CSS classes will be applied to
5552       * @param {string} add the CSS class(es) that will be added (multiple classes are separated via spaces)
5553       * @param {string} remove the CSS class(es) that will be removed (multiple classes are separated via spaces)
5554       * @param {object=} options an optional collection of options/styles that will be applied to the element
5555       *
5556       * @return {Promise} the animation callback promise
5557       */
5558      setClass: function(element, add, remove, options) {
5559        options = prepareAnimateOptions(options);
5560        options.addClass = mergeClasses(options.addClass, add);
5561        options.removeClass = mergeClasses(options.removeClass, remove);
5562        return $$animateQueue.push(element, 'setClass', options);
5563      },
5564
5565      /**
5566       * @ngdoc method
5567       * @name $animate#animate
5568       * @kind function
5569       *
5570       * @description Performs an inline animation on the element which applies the provided to and from CSS styles to the element.
5571       * If any detected CSS transition, keyframe or JavaScript matches the provided className value then the animation will take
5572       * on the provided styles. For example, if a transition animation is set for the given className then the provided from and
5573       * to styles will be applied alongside the given transition. If a JavaScript animation is detected then the provided styles
5574       * will be given in as function paramters into the `animate` method (or as apart of the `options` parameter).
5575       *
5576       * @param {DOMElement} element the element which the CSS styles will be applied to
5577       * @param {object} from the from (starting) CSS styles that will be applied to the element and across the animation.
5578       * @param {object} to the to (destination) CSS styles that will be applied to the element and across the animation.
5579       * @param {string=} className an optional CSS class that will be applied to the element for the duration of the animation. If
5580       *    this value is left as empty then a CSS class of `ng-inline-animate` will be applied to the element.
5581       *    (Note that if no animation is detected then this value will not be appplied to the element.)
5582       * @param {object=} options an optional collection of options/styles that will be applied to the element
5583       *
5584       * @return {Promise} the animation callback promise
5585       */
5586      animate: function(element, from, to, className, options) {
5587        options = prepareAnimateOptions(options);
5588        options.from = options.from ? extend(options.from, from) : from;
5589        options.to   = options.to   ? extend(options.to, to)     : to;
5590
5591        className = className || 'ng-inline-animate';
5592        options.tempClasses = mergeClasses(options.tempClasses, className);
5593        return $$animateQueue.push(element, 'animate', options);
5594      }
5595    };
5596  }];
5597}];
5598
5599/**
5600 * @ngdoc service
5601 * @name $animateCss
5602 * @kind object
5603 *
5604 * @description
5605 * This is the core version of `$animateCss`. By default, only when the `ngAnimate` is included,
5606 * then the `$animateCss` service will actually perform animations.
5607 *
5608 * Click here {@link ngAnimate.$animateCss to read the documentation for $animateCss}.
5609 */
5610var $CoreAnimateCssProvider = function() {
5611  this.$get = ['$$rAF', '$q', function($$rAF, $q) {
5612
5613    var RAFPromise = function() {};
5614    RAFPromise.prototype = {
5615      done: function(cancel) {
5616        this.defer && this.defer[cancel === true ? 'reject' : 'resolve']();
5617      },
5618      end: function() {
5619        this.done();
5620      },
5621      cancel: function() {
5622        this.done(true);
5623      },
5624      getPromise: function() {
5625        if (!this.defer) {
5626          this.defer = $q.defer();
5627        }
5628        return this.defer.promise;
5629      },
5630      then: function(f1,f2) {
5631        return this.getPromise().then(f1,f2);
5632      },
5633      'catch': function(f1) {
5634        return this.getPromise()['catch'](f1);
5635      },
5636      'finally': function(f1) {
5637        return this.getPromise()['finally'](f1);
5638      }
5639    };
5640
5641    return function(element, options) {
5642      // there is no point in applying the styles since
5643      // there is no animation that goes on at all in
5644      // this version of $animateCss.
5645      if (options.cleanupStyles) {
5646        options.from = options.to = null;
5647      }
5648
5649      if (options.from) {
5650        element.css(options.from);
5651        options.from = null;
5652      }
5653
5654      var closed, runner = new RAFPromise();
5655      return {
5656        start: run,
5657        end: run
5658      };
5659
5660      function run() {
5661        $$rAF(function() {
5662          close();
5663          if (!closed) {
5664            runner.done();
5665          }
5666          closed = true;
5667        });
5668        return runner;
5669      }
5670
5671      function close() {
5672        if (options.addClass) {
5673          element.addClass(options.addClass);
5674          options.addClass = null;
5675        }
5676        if (options.removeClass) {
5677          element.removeClass(options.removeClass);
5678          options.removeClass = null;
5679        }
5680        if (options.to) {
5681          element.css(options.to);
5682          options.to = null;
5683        }
5684      }
5685    };
5686  }];
5687};
5688
5689/* global stripHash: true */
5690
5691/**
5692 * ! This is a private undocumented service !
5693 *
5694 * @name $browser
5695 * @requires $log
5696 * @description
5697 * This object has two goals:
5698 *
5699 * - hide all the global state in the browser caused by the window object
5700 * - abstract away all the browser specific features and inconsistencies
5701 *
5702 * For tests we provide {@link ngMock.$browser mock implementation} of the `$browser`
5703 * service, which can be used for convenient testing of the application without the interaction with
5704 * the real browser apis.
5705 */
5706/**
5707 * @param {object} window The global window object.
5708 * @param {object} document jQuery wrapped document.
5709 * @param {object} $log window.console or an object with the same interface.
5710 * @param {object} $sniffer $sniffer service
5711 */
5712function Browser(window, document, $log, $sniffer) {
5713  var self = this,
5714      rawDocument = document[0],
5715      location = window.location,
5716      history = window.history,
5717      setTimeout = window.setTimeout,
5718      clearTimeout = window.clearTimeout,
5719      pendingDeferIds = {};
5720
5721  self.isMock = false;
5722
5723  var outstandingRequestCount = 0;
5724  var outstandingRequestCallbacks = [];
5725
5726  // TODO(vojta): remove this temporary api
5727  self.$$completeOutstandingRequest = completeOutstandingRequest;
5728  self.$$incOutstandingRequestCount = function() { outstandingRequestCount++; };
5729
5730  /**
5731   * Executes the `fn` function(supports currying) and decrements the `outstandingRequestCallbacks`
5732   * counter. If the counter reaches 0, all the `outstandingRequestCallbacks` are executed.
5733   */
5734  function completeOutstandingRequest(fn) {
5735    try {
5736      fn.apply(null, sliceArgs(arguments, 1));
5737    } finally {
5738      outstandingRequestCount--;
5739      if (outstandingRequestCount === 0) {
5740        while (outstandingRequestCallbacks.length) {
5741          try {
5742            outstandingRequestCallbacks.pop()();
5743          } catch (e) {
5744            $log.error(e);
5745          }
5746        }
5747      }
5748    }
5749  }
5750
5751  function getHash(url) {
5752    var index = url.indexOf('#');
5753    return index === -1 ? '' : url.substr(index);
5754  }
5755
5756  /**
5757   * @private
5758   * Note: this method is used only by scenario runner
5759   * TODO(vojta): prefix this method with $$ ?
5760   * @param {function()} callback Function that will be called when no outstanding request
5761   */
5762  self.notifyWhenNoOutstandingRequests = function(callback) {
5763    if (outstandingRequestCount === 0) {
5764      callback();
5765    } else {
5766      outstandingRequestCallbacks.push(callback);
5767    }
5768  };
5769
5770  //////////////////////////////////////////////////////////////
5771  // URL API
5772  //////////////////////////////////////////////////////////////
5773
5774  var cachedState, lastHistoryState,
5775      lastBrowserUrl = location.href,
5776      baseElement = document.find('base'),
5777      pendingLocation = null;
5778
5779  cacheState();
5780  lastHistoryState = cachedState;
5781
5782  /**
5783   * @name $browser#url
5784   *
5785   * @description
5786   * GETTER:
5787   * Without any argument, this method just returns current value of location.href.
5788   *
5789   * SETTER:
5790   * With at least one argument, this method sets url to new value.
5791   * If html5 history api supported, pushState/replaceState is used, otherwise
5792   * location.href/location.replace is used.
5793   * Returns its own instance to allow chaining
5794   *
5795   * NOTE: this api is intended for use only by the $location service. Please use the
5796   * {@link ng.$location $location service} to change url.
5797   *
5798   * @param {string} url New url (when used as setter)
5799   * @param {boolean=} replace Should new url replace current history record?
5800   * @param {object=} state object to use with pushState/replaceState
5801   */
5802  self.url = function(url, replace, state) {
5803    // In modern browsers `history.state` is `null` by default; treating it separately
5804    // from `undefined` would cause `$browser.url('/foo')` to change `history.state`
5805    // to undefined via `pushState`. Instead, let's change `undefined` to `null` here.
5806    if (isUndefined(state)) {
5807      state = null;
5808    }
5809
5810    // Android Browser BFCache causes location, history reference to become stale.
5811    if (location !== window.location) location = window.location;
5812    if (history !== window.history) history = window.history;
5813
5814    // setter
5815    if (url) {
5816      var sameState = lastHistoryState === state;
5817
5818      // Don't change anything if previous and current URLs and states match. This also prevents
5819      // IE<10 from getting into redirect loop when in LocationHashbangInHtml5Url mode.
5820      // See https://github.com/angular/angular.js/commit/ffb2701
5821      if (lastBrowserUrl === url && (!$sniffer.history || sameState)) {
5822        return self;
5823      }
5824      var sameBase = lastBrowserUrl && stripHash(lastBrowserUrl) === stripHash(url);
5825      lastBrowserUrl = url;
5826      lastHistoryState = state;
5827      // Don't use history API if only the hash changed
5828      // due to a bug in IE10/IE11 which leads
5829      // to not firing a `hashchange` nor `popstate` event
5830      // in some cases (see #9143).
5831      if ($sniffer.history && (!sameBase || !sameState)) {
5832        history[replace ? 'replaceState' : 'pushState'](state, '', url);
5833        cacheState();
5834        // Do the assignment again so that those two variables are referentially identical.
5835        lastHistoryState = cachedState;
5836      } else {
5837        if (!sameBase || pendingLocation) {
5838          pendingLocation = url;
5839        }
5840        if (replace) {
5841          location.replace(url);
5842        } else if (!sameBase) {
5843          location.href = url;
5844        } else {
5845          location.hash = getHash(url);
5846        }
5847        if (location.href !== url) {
5848          pendingLocation = url;
5849        }
5850      }
5851      return self;
5852    // getter
5853    } else {
5854      // - pendingLocation is needed as browsers don't allow to read out
5855      //   the new location.href if a reload happened or if there is a bug like in iOS 9 (see
5856      //   https://openradar.appspot.com/22186109).
5857      // - the replacement is a workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=407172
5858      return pendingLocation || location.href.replace(/%27/g,"'");
5859    }
5860  };
5861
5862  /**
5863   * @name $browser#state
5864   *
5865   * @description
5866   * This method is a getter.
5867   *
5868   * Return history.state or null if history.state is undefined.
5869   *
5870   * @returns {object} state
5871   */
5872  self.state = function() {
5873    return cachedState;
5874  };
5875
5876  var urlChangeListeners = [],
5877      urlChangeInit = false;
5878
5879  function cacheStateAndFireUrlChange() {
5880    pendingLocation = null;
5881    cacheState();
5882    fireUrlChange();
5883  }
5884
5885  function getCurrentState() {
5886    try {
5887      return history.state;
5888    } catch (e) {
5889      // MSIE can reportedly throw when there is no state (UNCONFIRMED).
5890    }
5891  }
5892
5893  // This variable should be used *only* inside the cacheState function.
5894  var lastCachedState = null;
5895  function cacheState() {
5896    // This should be the only place in $browser where `history.state` is read.
5897    cachedState = getCurrentState();
5898    cachedState = isUndefined(cachedState) ? null : cachedState;
5899
5900    // Prevent callbacks fo fire twice if both hashchange & popstate were fired.
5901    if (equals(cachedState, lastCachedState)) {
5902      cachedState = lastCachedState;
5903    }
5904    lastCachedState = cachedState;
5905  }
5906
5907  function fireUrlChange() {
5908    if (lastBrowserUrl === self.url() && lastHistoryState === cachedState) {
5909      return;
5910    }
5911
5912    lastBrowserUrl = self.url();
5913    lastHistoryState = cachedState;
5914    forEach(urlChangeListeners, function(listener) {
5915      listener(self.url(), cachedState);
5916    });
5917  }
5918
5919  /**
5920   * @name $browser#onUrlChange
5921   *
5922   * @description
5923   * Register callback function that will be called, when url changes.
5924   *
5925   * It's only called when the url is changed from outside of angular:
5926   * - user types different url into address bar
5927   * - user clicks on history (forward/back) button
5928   * - user clicks on a link
5929   *
5930   * It's not called when url is changed by $browser.url() method
5931   *
5932   * The listener gets called with new url as parameter.
5933   *
5934   * NOTE: this api is intended for use only by the $location service. Please use the
5935   * {@link ng.$location $location service} to monitor url changes in angular apps.
5936   *
5937   * @param {function(string)} listener Listener function to be called when url changes.
5938   * @return {function(string)} Returns the registered listener fn - handy if the fn is anonymous.
5939   */
5940  self.onUrlChange = function(callback) {
5941    // TODO(vojta): refactor to use node's syntax for events
5942    if (!urlChangeInit) {
5943      // We listen on both (hashchange/popstate) when available, as some browsers (e.g. Opera)
5944      // don't fire popstate when user change the address bar and don't fire hashchange when url
5945      // changed by push/replaceState
5946
5947      // html5 history api - popstate event
5948      if ($sniffer.history) jqLite(window).on('popstate', cacheStateAndFireUrlChange);
5949      // hashchange event
5950      jqLite(window).on('hashchange', cacheStateAndFireUrlChange);
5951
5952      urlChangeInit = true;
5953    }
5954
5955    urlChangeListeners.push(callback);
5956    return callback;
5957  };
5958
5959  /**
5960   * @private
5961   * Remove popstate and hashchange handler from window.
5962   *
5963   * NOTE: this api is intended for use only by $rootScope.
5964   */
5965  self.$$applicationDestroyed = function() {
5966    jqLite(window).off('hashchange popstate', cacheStateAndFireUrlChange);
5967  };
5968
5969  /**
5970   * Checks whether the url has changed outside of Angular.
5971   * Needs to be exported to be able to check for changes that have been done in sync,
5972   * as hashchange/popstate events fire in async.
5973   */
5974  self.$$checkUrlChange = fireUrlChange;
5975
5976  //////////////////////////////////////////////////////////////
5977  // Misc API
5978  //////////////////////////////////////////////////////////////
5979
5980  /**
5981   * @name $browser#baseHref
5982   *
5983   * @description
5984   * Returns current <base href>
5985   * (always relative - without domain)
5986   *
5987   * @returns {string} The current base href
5988   */
5989  self.baseHref = function() {
5990    var href = baseElement.attr('href');
5991    return href ? href.replace(/^(https?\:)?\/\/[^\/]*/, '') : '';
5992  };
5993
5994  /**
5995   * @name $browser#defer
5996   * @param {function()} fn A function, who's execution should be deferred.
5997   * @param {number=} [delay=0] of milliseconds to defer the function execution.
5998   * @returns {*} DeferId that can be used to cancel the task via `$browser.defer.cancel()`.
5999   *
6000   * @description
6001   * Executes a fn asynchronously via `setTimeout(fn, delay)`.
6002   *
6003   * Unlike when calling `setTimeout` directly, in test this function is mocked and instead of using
6004   * `setTimeout` in tests, the fns are queued in an array, which can be programmatically flushed
6005   * via `$browser.defer.flush()`.
6006   *
6007   */
6008  self.defer = function(fn, delay) {
6009    var timeoutId;
6010    outstandingRequestCount++;
6011    timeoutId = setTimeout(function() {
6012      delete pendingDeferIds[timeoutId];
6013      completeOutstandingRequest(fn);
6014    }, delay || 0);
6015    pendingDeferIds[timeoutId] = true;
6016    return timeoutId;
6017  };
6018
6019
6020  /**
6021   * @name $browser#defer.cancel
6022   *
6023   * @description
6024   * Cancels a deferred task identified with `deferId`.
6025   *
6026   * @param {*} deferId Token returned by the `$browser.defer` function.
6027   * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully
6028   *                    canceled.
6029   */
6030  self.defer.cancel = function(deferId) {
6031    if (pendingDeferIds[deferId]) {
6032      delete pendingDeferIds[deferId];
6033      clearTimeout(deferId);
6034      completeOutstandingRequest(noop);
6035      return true;
6036    }
6037    return false;
6038  };
6039
6040}
6041
6042function $BrowserProvider() {
6043  this.$get = ['$window', '$log', '$sniffer', '$document',
6044      function($window, $log, $sniffer, $document) {
6045        return new Browser($window, $document, $log, $sniffer);
6046      }];
6047}
6048
6049/**
6050 * @ngdoc service
6051 * @name $cacheFactory
6052 *
6053 * @description
6054 * Factory that constructs {@link $cacheFactory.Cache Cache} objects and gives access to
6055 * them.
6056 *
6057 * ```js
6058 *
6059 *  var cache = $cacheFactory('cacheId');
6060 *  expect($cacheFactory.get('cacheId')).toBe(cache);
6061 *  expect($cacheFactory.get('noSuchCacheId')).not.toBeDefined();
6062 *
6063 *  cache.put("key", "value");
6064 *  cache.put("another key", "another value");
6065 *
6066 *  // We've specified no options on creation
6067 *  expect(cache.info()).toEqual({id: 'cacheId', size: 2});
6068 *
6069 * ```
6070 *
6071 *
6072 * @param {string} cacheId Name or id of the newly created cache.
6073 * @param {object=} options Options object that specifies the cache behavior. Properties:
6074 *
6075 *   - `{number=}` `capacity` — turns the cache into LRU cache.
6076 *
6077 * @returns {object} Newly created cache object with the following set of methods:
6078 *
6079 * - `{object}` `info()` — Returns id, size, and options of cache.
6080 * - `{{*}}` `put({string} key, {*} value)` — Puts a new key-value pair into the cache and returns
6081 *   it.
6082 * - `{{*}}` `get({string} key)` — Returns cached value for `key` or undefined for cache miss.
6083 * - `{void}` `remove({string} key)` — Removes a key-value pair from the cache.
6084 * - `{void}` `removeAll()` — Removes all cached values.
6085 * - `{void}` `destroy()` — Removes references to this cache from $cacheFactory.
6086 *
6087 * @example
6088   <example module="cacheExampleApp">
6089     <file name="index.html">
6090       <div ng-controller="CacheController">
6091         <input ng-model="newCacheKey" placeholder="Key">
6092         <input ng-model="newCacheValue" placeholder="Value">
6093         <button ng-click="put(newCacheKey, newCacheValue)">Cache</button>
6094
6095         <p ng-if="keys.length">Cached Values</p>
6096         <div ng-repeat="key in keys">
6097           <span ng-bind="key"></span>
6098           <span>: </span>
6099           <b ng-bind="cache.get(key)"></b>
6100         </div>
6101
6102         <p>Cache Info</p>
6103         <div ng-repeat="(key, value) in cache.info()">
6104           <span ng-bind="key"></span>
6105           <span>: </span>
6106           <b ng-bind="value"></b>
6107         </div>
6108       </div>
6109     </file>
6110     <file name="script.js">
6111       angular.module('cacheExampleApp', []).
6112         controller('CacheController', ['$scope', '$cacheFactory', function($scope, $cacheFactory) {
6113           $scope.keys = [];
6114           $scope.cache = $cacheFactory('cacheId');
6115           $scope.put = function(key, value) {
6116             if (angular.isUndefined($scope.cache.get(key))) {
6117               $scope.keys.push(key);
6118             }
6119             $scope.cache.put(key, angular.isUndefined(value) ? null : value);
6120           };
6121         }]);
6122     </file>
6123     <file name="style.css">
6124       p {
6125         margin: 10px 0 3px;
6126       }
6127     </file>
6128   </example>
6129 */
6130function $CacheFactoryProvider() {
6131
6132  this.$get = function() {
6133    var caches = {};
6134
6135    function cacheFactory(cacheId, options) {
6136      if (cacheId in caches) {
6137        throw minErr('$cacheFactory')('iid', "CacheId '{0}' is already taken!", cacheId);
6138      }
6139
6140      var size = 0,
6141          stats = extend({}, options, {id: cacheId}),
6142          data = {},
6143          capacity = (options && options.capacity) || Number.MAX_VALUE,
6144          lruHash = {},
6145          freshEnd = null,
6146          staleEnd = null;
6147
6148      /**
6149       * @ngdoc type
6150       * @name $cacheFactory.Cache
6151       *
6152       * @description
6153       * A cache object used to store and retrieve data, primarily used by
6154       * {@link $http $http} and the {@link ng.directive:script script} directive to cache
6155       * templates and other data.
6156       *
6157       * ```js
6158       *  angular.module('superCache')
6159       *    .factory('superCache', ['$cacheFactory', function($cacheFactory) {
6160       *      return $cacheFactory('super-cache');
6161       *    }]);
6162       * ```
6163       *
6164       * Example test:
6165       *
6166       * ```js
6167       *  it('should behave like a cache', inject(function(superCache) {
6168       *    superCache.put('key', 'value');
6169       *    superCache.put('another key', 'another value');
6170       *
6171       *    expect(superCache.info()).toEqual({
6172       *      id: 'super-cache',
6173       *      size: 2
6174       *    });
6175       *
6176       *    superCache.remove('another key');
6177       *    expect(superCache.get('another key')).toBeUndefined();
6178       *
6179       *    superCache.removeAll();
6180       *    expect(superCache.info()).toEqual({
6181       *      id: 'super-cache',
6182       *      size: 0
6183       *    });
6184       *  }));
6185       * ```
6186       */
6187      return caches[cacheId] = {
6188
6189        /**
6190         * @ngdoc method
6191         * @name $cacheFactory.Cache#put
6192         * @kind function
6193         *
6194         * @description
6195         * Inserts a named entry into the {@link $cacheFactory.Cache Cache} object to be
6196         * retrieved later, and incrementing the size of the cache if the key was not already
6197         * present in the cache. If behaving like an LRU cache, it will also remove stale
6198         * entries from the set.
6199         *
6200         * It will not insert undefined values into the cache.
6201         *
6202         * @param {string} key the key under which the cached data is stored.
6203         * @param {*} value the value to store alongside the key. If it is undefined, the key
6204         *    will not be stored.
6205         * @returns {*} the value stored.
6206         */
6207        put: function(key, value) {
6208          if (isUndefined(value)) return;
6209          if (capacity < Number.MAX_VALUE) {
6210            var lruEntry = lruHash[key] || (lruHash[key] = {key: key});
6211
6212            refresh(lruEntry);
6213          }
6214
6215          if (!(key in data)) size++;
6216          data[key] = value;
6217
6218          if (size > capacity) {
6219            this.remove(staleEnd.key);
6220          }
6221
6222          return value;
6223        },
6224
6225        /**
6226         * @ngdoc method
6227         * @name $cacheFactory.Cache#get
6228         * @kind function
6229         *
6230         * @description
6231         * Retrieves named data stored in the {@link $cacheFactory.Cache Cache} object.
6232         *
6233         * @param {string} key the key of the data to be retrieved
6234         * @returns {*} the value stored.
6235         */
6236        get: function(key) {
6237          if (capacity < Number.MAX_VALUE) {
6238            var lruEntry = lruHash[key];
6239
6240            if (!lruEntry) return;
6241
6242            refresh(lruEntry);
6243          }
6244
6245          return data[key];
6246        },
6247
6248
6249        /**
6250         * @ngdoc method
6251         * @name $cacheFactory.Cache#remove
6252         * @kind function
6253         *
6254         * @description
6255         * Removes an entry from the {@link $cacheFactory.Cache Cache} object.
6256         *
6257         * @param {string} key the key of the entry to be removed
6258         */
6259        remove: function(key) {
6260          if (capacity < Number.MAX_VALUE) {
6261            var lruEntry = lruHash[key];
6262
6263            if (!lruEntry) return;
6264
6265            if (lruEntry == freshEnd) freshEnd = lruEntry.p;
6266            if (lruEntry == staleEnd) staleEnd = lruEntry.n;
6267            link(lruEntry.n,lruEntry.p);
6268
6269            delete lruHash[key];
6270          }
6271
6272          delete data[key];
6273          size--;
6274        },
6275
6276
6277        /**
6278         * @ngdoc method
6279         * @name $cacheFactory.Cache#removeAll
6280         * @kind function
6281         *
6282         * @description
6283         * Clears the cache object of any entries.
6284         */
6285        removeAll: function() {
6286          data = {};
6287          size = 0;
6288          lruHash = {};
6289          freshEnd = staleEnd = null;
6290        },
6291
6292
6293        /**
6294         * @ngdoc method
6295         * @name $cacheFactory.Cache#destroy
6296         * @kind function
6297         *
6298         * @description
6299         * Destroys the {@link $cacheFactory.Cache Cache} object entirely,
6300         * removing it from the {@link $cacheFactory $cacheFactory} set.
6301         */
6302        destroy: function() {
6303          data = null;
6304          stats = null;
6305          lruHash = null;
6306          delete caches[cacheId];
6307        },
6308
6309
6310        /**
6311         * @ngdoc method
6312         * @name $cacheFactory.Cache#info
6313         * @kind function
6314         *
6315         * @description
6316         * Retrieve information regarding a particular {@link $cacheFactory.Cache Cache}.
6317         *
6318         * @returns {object} an object with the following properties:
6319         *   <ul>
6320         *     <li>**id**: the id of the cache instance</li>
6321         *     <li>**size**: the number of entries kept in the cache instance</li>
6322         *     <li>**...**: any additional properties from the options object when creating the
6323         *       cache.</li>
6324         *   </ul>
6325         */
6326        info: function() {
6327          return extend({}, stats, {size: size});
6328        }
6329      };
6330
6331
6332      /**
6333       * makes the `entry` the freshEnd of the LRU linked list
6334       */
6335      function refresh(entry) {
6336        if (entry != freshEnd) {
6337          if (!staleEnd) {
6338            staleEnd = entry;
6339          } else if (staleEnd == entry) {
6340            staleEnd = entry.n;
6341          }
6342
6343          link(entry.n, entry.p);
6344          link(entry, freshEnd);
6345          freshEnd = entry;
6346          freshEnd.n = null;
6347        }
6348      }
6349
6350
6351      /**
6352       * bidirectionally links two entries of the LRU linked list
6353       */
6354      function link(nextEntry, prevEntry) {
6355        if (nextEntry != prevEntry) {
6356          if (nextEntry) nextEntry.p = prevEntry; //p stands for previous, 'prev' didn't minify
6357          if (prevEntry) prevEntry.n = nextEntry; //n stands for next, 'next' didn't minify
6358        }
6359      }
6360    }
6361
6362
6363  /**
6364   * @ngdoc method
6365   * @name $cacheFactory#info
6366   *
6367   * @description
6368   * Get information about all the caches that have been created
6369   *
6370   * @returns {Object} - key-value map of `cacheId` to the result of calling `cache#info`
6371   */
6372    cacheFactory.info = function() {
6373      var info = {};
6374      forEach(caches, function(cache, cacheId) {
6375        info[cacheId] = cache.info();
6376      });
6377      return info;
6378    };
6379
6380
6381  /**
6382   * @ngdoc method
6383   * @name $cacheFactory#get
6384   *
6385   * @description
6386   * Get access to a cache object by the `cacheId` used when it was created.
6387   *
6388   * @param {string} cacheId Name or id of a cache to access.
6389   * @returns {object} Cache object identified by the cacheId or undefined if no such cache.
6390   */
6391    cacheFactory.get = function(cacheId) {
6392      return caches[cacheId];
6393    };
6394
6395
6396    return cacheFactory;
6397  };
6398}
6399
6400/**
6401 * @ngdoc service
6402 * @name $templateCache
6403 *
6404 * @description
6405 * The first time a template is used, it is loaded in the template cache for quick retrieval. You
6406 * can load templates directly into the cache in a `script` tag, or by consuming the
6407 * `$templateCache` service directly.
6408 *
6409 * Adding via the `script` tag:
6410 *
6411 * ```html
6412 *   <script type="text/ng-template" id="templateId.html">
6413 *     <p>This is the content of the template</p>
6414 *   </script>
6415 * ```
6416 *
6417 * **Note:** the `script` tag containing the template does not need to be included in the `head` of
6418 * the document, but it must be a descendent of the {@link ng.$rootElement $rootElement} (IE,
6419 * element with ng-app attribute), otherwise the template will be ignored.
6420 *
6421 * Adding via the `$templateCache` service:
6422 *
6423 * ```js
6424 * var myApp = angular.module('myApp', []);
6425 * myApp.run(function($templateCache) {
6426 *   $templateCache.put('templateId.html', 'This is the content of the template');
6427 * });
6428 * ```
6429 *
6430 * To retrieve the template later, simply use it in your HTML:
6431 * ```html
6432 * <div ng-include=" 'templateId.html' "></div>
6433 * ```
6434 *
6435 * or get it via Javascript:
6436 * ```js
6437 * $templateCache.get('templateId.html')
6438 * ```
6439 *
6440 * See {@link ng.$cacheFactory $cacheFactory}.
6441 *
6442 */
6443function $TemplateCacheProvider() {
6444  this.$get = ['$cacheFactory', function($cacheFactory) {
6445    return $cacheFactory('templates');
6446  }];
6447}
6448
6449/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
6450 *     Any commits to this file should be reviewed with security in mind.  *
6451 *   Changes to this file can potentially create security vulnerabilities. *
6452 *          An approval from 2 Core members with history of modifying      *
6453 *                         this file is required.                          *
6454 *                                                                         *
6455 *  Does the change somehow allow for arbitrary javascript to be executed? *
6456 *    Or allows for someone to change the prototype of built-in objects?   *
6457 *     Or gives undesired access to variables likes document or window?    *
6458 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
6459
6460/* ! VARIABLE/FUNCTION NAMING CONVENTIONS THAT APPLY TO THIS FILE!
6461 *
6462 * DOM-related variables:
6463 *
6464 * - "node" - DOM Node
6465 * - "element" - DOM Element or Node
6466 * - "$node" or "$element" - jqLite-wrapped node or element
6467 *
6468 *
6469 * Compiler related stuff:
6470 *
6471 * - "linkFn" - linking fn of a single directive
6472 * - "nodeLinkFn" - function that aggregates all linking fns for a particular node
6473 * - "childLinkFn" -  function that aggregates all linking fns for child nodes of a particular node
6474 * - "compositeLinkFn" - function that aggregates all linking fns for a compilation root (nodeList)
6475 */
6476
6477
6478/**
6479 * @ngdoc service
6480 * @name $compile
6481 * @kind function
6482 *
6483 * @description
6484 * Compiles an HTML string or DOM into a template and produces a template function, which
6485 * can then be used to link {@link ng.$rootScope.Scope `scope`} and the template together.
6486 *
6487 * The compilation is a process of walking the DOM tree and matching DOM elements to
6488 * {@link ng.$compileProvider#directive directives}.
6489 *
6490 * <div class="alert alert-warning">
6491 * **Note:** This document is an in-depth reference of all directive options.
6492 * For a gentle introduction to directives with examples of common use cases,
6493 * see the {@link guide/directive directive guide}.
6494 * </div>
6495 *
6496 * ## Comprehensive Directive API
6497 *
6498 * There are many different options for a directive.
6499 *
6500 * The difference resides in the return value of the factory function.
6501 * You can either return a "Directive Definition Object" (see below) that defines the directive properties,
6502 * or just the `postLink` function (all other properties will have the default values).
6503 *
6504 * <div class="alert alert-success">
6505 * **Best Practice:** It's recommended to use the "directive definition object" form.
6506 * </div>
6507 *
6508 * Here's an example directive declared with a Directive Definition Object:
6509 *
6510 * ```js
6511 *   var myModule = angular.module(...);
6512 *
6513 *   myModule.directive('directiveName', function factory(injectables) {
6514 *     var directiveDefinitionObject = {
6515 *       priority: 0,
6516 *       template: '<div></div>
6516', // or // function(tElement, tAttrs) { ... },
6517 *       // or
6518 *       // templateUrl: 'directive.html', // or // function(tElement, tAttrs) { ... },
6519 *       transclude: false,
6520 *       restrict: 'A',
6521 *       templateNamespace: 'html',
6522 *       scope: false,
6523 *       controller: function($scope, $element, $attrs, $transclude, otherInjectables) { ... },
6524 *       controllerAs: 'stringIdentifier',
6525 *       bindToController: false,
6526 *       require: 'siblingDirectiveName', // or // ['^parentDirectiveName', '?optionalDirectiveName', '?^optionalParent'],
6527 *       compile: function compile(tElement, tAttrs, transclude) {
6528 *         return {
6529 *           pre: function preLink(scope, iElement, iAttrs, controller) { ... },
6530 *           post: function postLink(scope, iElement, iAttrs, controller) { ... }
6531 *         }
6532 *         // or
6533 *         // return function postLink( ... ) { ... }
6534 *       },
6535 *       // or
6536 *       // link: {
6537 *       //  pre: function preLink(scope, iElement, iAttrs, controller) { ... },
6538 *       //  post: function postLink(scope, iElement, iAttrs, controller) { ... }
6539 *       // }
6540 *       // or
6541 *       // link: function postLink( ... ) { ... }
6542 *     };
6543 *     return directiveDefinitionObject;
6544 *   });
6545 * ```
6546 *
6547 * <div class="alert alert-warning">
6548 * **Note:** Any unspecified options will use the default value. You can see the default values below.
6549 * </div>
6550 *
6551 * Therefore the above can be simplified as:
6552 *
6553 * ```js
6554 *   var myModule = angular.module(...);
6555 *
6556 *   myModule.directive('directiveName', function factory(injectables) {
6557 *     var directiveDefinitionObject = {
6558 *       link: function postLink(scope, iElement, iAttrs) { ... }
6559 *     };
6560 *     return directiveDefinitionObject;
6561 *     // or
6562 *     // return function postLink(scope, iElement, iAttrs) { ... }
6563 *   });
6564 * ```
6565 *
6566 *
6567 *
6568 * ### Directive Definition Object
6569 *
6570 * The directive definition object provides instructions to the {@link ng.$compile
6571 * compiler}. The attributes are:
6572 *
6573 * #### `multiElement`
6574 * When this property is set to true, the HTML compiler will collect DOM nodes between
6575 * nodes with the attributes `directive-name-start` and `directive-name-end`, and group them
6576 * together as the directive elements. It is recommended that this feature be used on directives
6577 * which are not strictly behavioural (such as {@link ngClick}), and which
6578 * do not manipulate or replace child nodes (such as {@link ngInclude}).
6579 *
6580 * #### `priority`
6581 * When there are multiple directives defined on a single DOM element, sometimes it
6582 * is necessary to specify the order in which the directives are applied. The `priority` is used
6583 * to sort the directives before their `compile` functions get called. Priority is defined as a
6584 * number. Directives with greater numerical `priority` are compiled first. Pre-link functions
6585 * are also run in priority order, but post-link functions are run in reverse order. The order
6586 * of directives with the same priority is undefined. The default priority is `0`.
6587 *
6588 * #### `terminal`
6589 * If set to true then the current `priority` will be the last set of directives
6590 * which will execute (any directives at the current priority will still execute
6591 * as the order of execution on same `priority` is undefined). Note that expressions
6592 * and other directives used in the directive's template will also be excluded from execution.
6593 *
6594 * #### `scope`
6595 * The scope property can be `true`, an object or a falsy value:
6596 *
6597 * * **falsy:** No scope will be created for the directive. The directive will use its parent's scope.
6598 *
6599 * * **`true`:** A new child scope that prototypically inherits from its parent will be created for
6600 * the directive's element. If multiple directives on the same element request a new scope,
6601 * only one new scope is created. The new scope rule does not apply for the root of the template
6602 * since the root of the template always gets a new scope.
6603 *
6604 * * **`{...}` (an object hash):** A new "isolate" scope is created for the directive's element. The
6605 * 'isolate' scope differs from normal scope in that it does not prototypically inherit from its parent
6606 * scope. This is useful when creating reusable components, which should not accidentally read or modify
6607 * data in the parent scope.
6608 *
6609 * The 'isolate' scope object hash defines a set of local scope properties derived from attributes on the
6610 * directive's element. These local properties are useful for aliasing values for templates. The keys in
6611 * the object hash map to the name of the property on the isolate scope; the values define how the property
6612 * is bound to the parent scope, via matching attributes on the directive's element:
6613 *
6614 * * `@` or `@attr` - bind a local scope property to the value of DOM attribute. The result is
6615 *   always a string since DOM attributes are strings. If no `attr` name is specified  then the
6616 *   attribute name is assumed to be the same as the local name.
6617 *   Given `<widget my-attr="hello {{name}}">` and widget definition
6618 *   of `scope: { localName:'@myAttr' }`, then widget scope property `localName` will reflect
6619 *   the interpolated value of `hello {{name}}`. As the `name` attribute changes so will the
6620 *   `localName` property on the widget scope. The `name` is read from the parent scope (not
6621 *   component scope).
6622 *
6623 * * `=` or `=attr` - set up bi-directional binding between a local scope property and the
6624 *   parent scope property of name defined via the value of the `attr` attribute. If no `attr`
6625 *   name is specified then the attribute name is assumed to be the same as the local name.
6626 *   Given `<widget my-attr="parentModel">` and widget definition of
6627 *   `scope: { localModel:'=myAttr' }`, then widget scope property `localModel` will reflect the
6628 *   value of `parentModel` on the parent scope. Any changes to `parentModel` will be reflected
6629 *   in `localModel` and any changes in `localModel` will reflect in `parentModel`. If the parent
6630 *   scope property doesn't exist, it will throw a NON_ASSIGNABLE_MODEL_EXPRESSION exception. You
6631 *   can avoid this behavior using `=?` or `=?attr` in order to flag the property as optional. If
6632 *   you want to shallow watch for changes (i.e. $watchCollection instead of $watch) you can use
6633 *   `=*` or `=*attr` (`=*?` or `=*?attr` if the property is optional).
6634 *
6635 * * `&` or `&attr` - provides a way to execute an expression in the context of the parent scope.
6636 *   If no `attr` name is specified then the attribute name is assumed to be the same as the
6637 *   local name. Given `<widget my-attr="count = count + value">` and widget definition of
6638 *   `scope: { localFn:'&myAttr' }`, then isolate scope property `localFn` will point to
6639 *   a function wrapper for the `count = count + value` expression. Often it's desirable to
6640 *   pass data from the isolated scope via an expression to the parent scope, this can be
6641 *   done by passing a map of local variable names and values into the expression wrapper fn.
6642 *   For example, if the expression is `increment(amount)` then we can specify the amount value
6643 *   by calling the `localFn` as `localFn({amount: 22})`.
6644 *
6645 * In general it's possible to apply more than one directive to one element, but there might be limitations
6646 * depending on the type of scope required by the directives. The following points will help explain these limitations.
6647 * For simplicity only two directives are taken into account, but it is also applicable for several directives:
6648 *
6649 * * **no scope** + **no scope** => Two directives which don't require their own scope will use their parent's scope
6650 * * **child scope** + **no scope** =>  Both directives will share one single child scope
6651 * * **child scope** + **child scope** =>  Both directives will share one single child scope
6652 * * **isolated scope** + **no scope** =>  The isolated directive will use it's own created isolated scope. The other directive will use
6653 * its parent's scope
6654 * * **isolated scope** + **child scope** =>  **Won't work!** Only one scope can be related to one element. Therefore these directives cannot
6655 * be applied to the same element.
6656 * * **isolated scope** + **isolated scope**  =>  **Won't work!** Only one scope can be related to one element. Therefore these directives
6657 * cannot be applied to the same element.
6658 *
6659 *
6660 * #### `bindToController`
6661 * When an isolate scope is used for a component (see above), and `controllerAs` is used, `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 * #### `controller`
6666 * Controller constructor function. The controller is instantiated before the
6667 * pre-linking phase and can be accessed by other directives (see
6668 * `require` attribute). This allows the directives to communicate with each other and augment
6669 * each other's behavior. The controller is injectable (and supports bracket notation) with the following locals:
6670 *
6671 * * `$scope` - Current scope associated with the element
6672 * * `$element` - Current element
6673 * * `$attrs` - Current attributes object for the element
6674 * * `$transclude` - A transclude linking function pre-bound to the correct transclusion scope:
6675 *   `function([scope], cloneLinkingFn, futureParentElement)`.
6676 *    * `scope`: optional argument to override the scope.
6677 *    * `cloneLinkingFn`: optional argument to create clones of the original transcluded content.
6678 *    * `futureParentElement`:
6679 *        * defines the parent to which the `cloneLinkingFn` will add the cloned elements.
6680 *        * default: `$element.parent()` resp. `$element` for `transclude:'element'` resp. `transclude:true`.
6681 *        * only needed for transcludes that are allowed to contain non html elements (e.g. SVG elements)
6682 *          and when the `cloneLinkinFn` is passed,
6683 *          as those elements need to created and cloned in a special way when they are defined outside their
6684 *          usual containers (e.g. like `<svg>`).
6685 *        * See also the `directive.templateNamespace` property.
6686 *
6687 *
6688 * #### `require`
6689 * Require another directive and inject its controller as the fourth argument to the linking function. The
6690 * `require` takes a string name (or array of strings) of the directive(s) to pass in. If an array is used, the
6691 * injected argument will be an array in corresponding order. If no such directive can be
6692 * found, or if the directive does not have a controller, then an error is raised (unless no link function
6693 * is specified, in which case error checking is skipped). The name can be prefixed with:
6694 *
6695 * * (no prefix) - Locate the required controller on the current element. Throw an error if not found.
6696 * * `?` - Attempt to locate the required controller or pass `null` to the `link` fn if not found.
6697 * * `^` - Locate the required controller by searching the element and its parents. Throw an error if not found.
6698 * * `^^` - Locate the required controller by searching the element's parents. Throw an error if not found.
6699 * * `?^` - Attempt to locate the required controller by searching the element and its parents or pass
6700 *   `null` to the `link` fn if not found.
6701 * * `?^^` - Attempt to locate the required controller by searching the element's parents, or pass
6702 *   `null` to the `link` fn if not found.
6703 *
6704 *
6705 * #### `controllerAs`
6706 * Identifier name for a reference to the controller in the directive's scope.
6707 * This allows the controller to be referenced from the directive template. This is especially
6708 * useful when a directive is used as component, i.e. with an `isolate` scope. It's also possible
6709 * to use it in a directive without an `isolate` / `new` scope, but you need to be aware that the
6710 * `controllerAs` reference might overwrite a property that already exists on the parent scope.
6711 *
6712 *
6713 * #### `restrict`
6714 * String of subset of `EACM` which restricts the directive to a specific directive
6715 * declaration style. If omitted, the defaults (elements and attributes) are used.
6716 *
6717 * * `E` - Element name (default): `<my-directive></my-directive>`
6718 * * `A` - Attribute (default): `<div my-directive="exp"></div>`
6719 * * `C` - Class: `<div class="my-directive: exp;"></div>`
6720 * * `M` - Comment: `<!-- directive: my-directive exp -->`
6721 *
6722 *
6723 * #### `templateNamespace`
6724 * String representing the document type used by the markup in the template.
6725 * AngularJS needs this information as those elements need to be created and cloned
6726 * in a special way when they are defined outside their usual containers like `<svg>` and `<math>`.
6727 *
6728 * * `html` - All root nodes in the template are HTML. Root nodes may also be
6729 *   top-level elements such as `<svg>` or `<math>`.
6730 * * `svg` - The root nodes in the template are SVG elements (excluding `<math>`).
6731 * * `math` - The root nodes in the template are MathML elements (excluding `<svg>`).
6732 *
6733 * If no `templateNamespace` is specified, then the namespace is considered to be `html`.
6734 *
6735 * #### `template`
6736 * HTML markup that may:
6737 * * Replace the contents of the directive's element (default).
6738 * * Replace the directive's element itself (if `replace` is true - DEPRECATED).
6739 * * Wrap the contents of the directive's element (if `transclude` is true).
6740 *
6741 * Value may be:
6742 *
6743 * * A string. For example `<div red-on-hover>{{delete_str}}</div>`.
6744 * * A function which takes two arguments `tElement` and `tAttrs` (described in the `compile`
6745 *   function api below) and returns a string value.
6746 *
6747 *
6748 * #### `templateUrl`
6749 * This is similar to `template` but the template is loaded from the specified URL, asynchronously.
6750 *
6751 * Because template loading is asynchronous the compiler will suspend compilation of directives on that element
6752 * for later when the template has been resolved.  In the meantime it will continue to compile and link
6753 * sibling and parent elements as though this element had not contained any directives.
6754 *
6755 * The compiler does not suspend the entire compilation to wait for templates to be loaded because this
6756 * would result in the whole app "stalling" until all templates are loaded asynchronously - even in the
6757 * case when only one deeply nested directive has `templateUrl`.
6758 *
6759 * Template loading is asynchronous even if the template has been preloaded into the {@link $templateCache}
6760 *
6761 * You can specify `templateUrl` as a string representing the URL or as a function which takes two
6762 * arguments `tElement` and `tAttrs` (described in the `compile` function api below) and returns
6763 * a string value representing the url.  In either case, the template URL is passed through {@link
6764 * $sce#getTrustedResourceUrl $sce.getTrustedResourceUrl}.
6765 *
6766 *
6767 * #### `replace` ([*DEPRECATED*!], will be removed in next major release - i.e. v2.0)
6768 * specify what the template should replace. Defaults to `false`.
6769 *
6770 * * `true` - the template will replace the directive's element.
6771 * * `false` - the template will replace the contents of the directive's element.
6772 *
6773 * The replacement process migrates all of the attributes / classes from the old element to the new
6774 * one. See the {@link guide/directive#template-expanding-directive
6775 * Directives Guide} for an example.
6776 *
6777 * There are very few scenarios where element replacement is required for the application function,
6778 * the main one being reusable custom components that are used within SVG contexts
6779 * (because SVG doesn't work with custom elements in the DOM tree).
6780 *
6781 * #### `transclude`
6782 * Extract the contents of the element where the directive appears and make it available to the directive.
6783 * The contents are compiled and provided to the directive as a **transclusion function**. See the
6784 * {@link $compile#transclusion Transclusion} section below.
6785 *
6786 * There are two kinds of transclusion depending upon whether you want to transclude just the contents of the
6787 * directive's element or the entire element:
6788 *
6789 * * `true` - transclude the content (i.e. the child nodes) of the directive's element.
6790 * * `'element'` - transclude the whole of the directive's element including any directives on this
6791 *   element that defined at a lower priority than this directive. When used, the `template`
6792 *   property is ignored.
6793 *
6794 *
6795 * #### `compile`
6796 *
6797 * ```js
6798 *   function compile(tElement, tAttrs, transclude) { ... }
6799 * ```
6800 *
6801 * The compile function deals with transforming the template DOM. Since most directives do not do
6802 * template transformation, it is not used often. The compile function takes the following arguments:
6803 *
6804 *   * `tElement` - template element - The element where the directive has been declared. It is
6805 *     safe to do template transformation on the element and child elements only.
6806 *
6807 *   * `tAttrs` - template attributes - Normalized list of attributes declared on this element shared
6808 *     between all directive compile functions.
6809 *
6810 *   * `transclude` -  [*DEPRECATED*!] A transclude linking function: `function(scope, cloneLinkingFn)`
6811 *
6812 * <div class="alert alert-warning">
6813 * **Note:** The template instance and the link instance may be different objects if the template has
6814 * been cloned. For this reason it is **not** safe to do anything other than DOM transformations that
6815 * apply to all cloned DOM nodes within the compile function. Specifically, DOM listener registration
6816 * should be done in a linking function rather than in a compile function.
6817 * </div>
6818
6819 * <div class="alert alert-warning">
6820 * **Note:** The compile function cannot handle directives that recursively use themselves in their
6821 * own templates or compile functions. Compiling these directives results in an infinite loop and a
6822 * stack overflow errors.
6823 *
6824 * This can be avoided by manually using $compile in the postLink function to imperatively compile
6825 * a directive's template instead of relying on automatic template compilation via `template` or
6826 * `templateUrl` declaration or manual compilation inside the compile function.
6827 * </div>
6828 *
6829 * <div class="alert alert-danger">
6830 * **Note:** The `transclude` function that is passed to the compile function is deprecated, as it
6831 *   e.g. does not know about the right outer scope. Please use the transclude function that is passed
6832 *   to the link function instead.
6833 * </div>
6834
6835 * A compile function can have a return value which can be either a function or an object.
6836 *
6837 * * returning a (post-link) function - is equivalent to registering the linking function via the
6838 *   `link` property of the config object when the compile function is empty.
6839 *
6840 * * returning an object with function(s) registered via `pre` and `post` properties - allows you to
6841 *   control when a linking function should be called during the linking phase. See info about
6842 *   pre-linking and post-linking functions below.
6843 *
6844 *
6845 * #### `link`
6846 * This property is used only if the `compile` property is not defined.
6847 *
6848 * ```js
6849 *   function link(scope, iElement, iAttrs, controller, transcludeFn) { ... }
6850 * ```
6851 *
6852 * The link function is responsible for registering DOM listeners as well as updating the DOM. It is
6853 * executed after the template has been cloned. This is where most of the directive logic will be
6854 * put.
6855 *
6856 *   * `scope` - {@link ng.$rootScope.Scope Scope} - The scope to be used by the
6857 *     directive for registering {@link ng.$rootScope.Scope#$watch w
6857atches}.
6858 *
6859 *   * `iElement` - instance element - The element where the directive is to be used. It is safe to
6860 *     manipulate the children of the element only in `postLink` function since the children have
6861 *     already been linked.
6862 *
6863 *   * `iAttrs` - instance attributes - Normalized list of attributes declared on this element shared
6864 *     between all directive linking functions.
6865 *
6866 *   * `controller` - the directive's required controller instance(s) - Instances are shared
6867 *     among all directives, which allows the directives to use the controllers as a communication
6868 *     channel. The exact value depends on the directive's `require` property:
6869 *       * no controller(s) required: the directive's own controller, or `undefined` if it doesn't have one
6870 *       * `string`: the controller instance
6871 *       * `array`: array of controller instances
6872 *
6873 *     If a required controller cannot be found, and it is optional, the instance is `null`,
6874 *     otherwise the {@link error:$compile:ctreq Missing Required Controller} error is thrown.
6875 *
6876 *     Note that you can also require the directive's own controller - it will be made available like
6877 *     any other controller.
6878 *
6879 *   * `transcludeFn` - A transclude linking function pre-bound to the correct transclusion scope.
6880 *     This is the same as the `$transclude`
6881 *     parameter of directive controllers, see there for details.
6882 *     `function([scope], cloneLinkingFn, futureParentElement)`.
6883 *
6884 * #### Pre-linking function
6885 *
6886 * Executed before the child elements are linked. Not safe to do DOM transformation since the
6887 * compiler linking function will fail to locate the correct elements for linking.
6888 *
6889 * #### Post-linking function
6890 *
6891 * Executed after the child elements are linked.
6892 *
6893 * Note that child elements that contain `templateUrl` directives will not have been compiled
6894 * and linked since they are waiting for their template to load asynchronously and their own
6895 * compilation and linking has been suspended until that occurs.
6896 *
6897 * It is safe to do DOM transformation in the post-linking function on elements that are not waiting
6898 * for their async templates to be resolved.
6899 *
6900 *
6901 * ### Transclusion
6902 *
6903 * Transclusion is the process of extracting a collection of DOM elements from one part of the DOM and
6904 * copying them to another part of the DOM, while maintaining their connection to the original AngularJS
6905 * scope from where they were taken.
6906 *
6907 * Transclusion is used (often with {@link ngTransclude}) to insert the
6908 * original contents of a directive's element into a specified place in the template of the directive.
6909 * The benefit of transclusion, over simply moving the DOM elements manually, is that the transcluded
6910 * content has access to the properties on the scope from which it was taken, even if the directive
6911 * has isolated scope.
6912 * See the {@link guide/directive#creating-a-directive-that-wraps-other-elements Directives Guide}.
6913 *
6914 * This makes it possible for the widget to have private state for its template, while the transcluded
6915 * content has access to its originating scope.
6916 *
6917 * <div class="alert alert-warning">
6918 * **Note:** When testing an element transclude directive you must not place the directive at the root of the
6919 * DOM fragment that is being compiled. See {@link guide/unit-testing#testing-transclusion-directives
6920 * Testing Transclusion Directives}.
6921 * </div>
6922 *
6923 * #### Transclusion Functions
6924 *
6925 * When a directive requests transclusion, the compiler extracts its contents and provides a **transclusion
6926 * function** to the directive's `link` function and `controller`. This transclusion function is a special
6927 * **linking function** that will return the compiled contents linked to a new transclusion scope.
6928 *
6929 * <div class="alert alert-info">
6930 * If you are just using {@link ngTransclude} then you don't need to worry about this function, since
6931 * ngTransclude will deal with it for us.
6932 * </div>
6933 *
6934 * If you want to manually control the insertion and removal of the transcluded content in your directive
6935 * then you must use this transclude function. When you call a transclude function it returns a a jqLite/JQuery
6936 * object that contains the compiled DOM, which is linked to the correct transclusion scope.
6937 *
6938 * When you call a transclusion function you can pass in a **clone attach function**. This function accepts
6939 * two parameters, `function(clone, scope) { ... }`, where the `clone` is a fresh compiled copy of your transcluded
6940 * content and the `scope` is the newly created transclusion scope, to which the clone is bound.
6941 *
6942 * <div class="alert alert-info">
6943 * **Best Practice**: Always provide a `cloneFn` (clone attach function) when you call a translude function
6944 * since you then get a fresh clone of the original DOM and also have access to the new transclusion scope.
6945 * </div>
6946 *
6947 * It is normal practice to attach your transcluded content (`clone`) to the DOM inside your **clone
6948 * attach function**:
6949 *
6950 * ```js
6951 * var transcludedContent, transclusionScope;
6952 *
6953 * $transclude(function(clone, scope) {
6954 *   element.append(clone);
6955 *   transcludedContent = clone;
6956 *   transclusionScope = scope;
6957 * });
6958 * ```
6959 *
6960 * Later, if you want to remove the transcluded content from your DOM then you should also destroy the
6961 * associated transclusion scope:
6962 *
6963 * ```js
6964 * transcludedContent.remove();
6965 * transclusionScope.$destroy();
6966 * ```
6967 *
6968 * <div class="alert alert-info">
6969 * **Best Practice**: if you intend to add and remove transcluded content manually in your directive
6970 * (by calling the transclude function to get the DOM and calling `element.remove()` to remove it),
6971 * then you are also responsible for calling `$destroy` on the transclusion scope.
6972 * </div>
6973 *
6974 * The built-in DOM manipulation directives, such as {@link ngIf}, {@link ngSwitch} and {@link ngRepeat}
6975 * automatically destroy their transluded clones as necessary so you do not need to worry about this if
6976 * you are simply using {@link ngTransclude} to inject the transclusion into your directive.
6977 *
6978 *
6979 * #### Transclusion Scopes
6980 *
6981 * When you call a transclude function it returns a DOM fragment that is pre-bound to a **transclusion
6982 * scope**. This scope is special, in that it is a child of the directive's scope (and so gets destroyed
6983 * when the directive's scope gets destroyed) but it inherits the properties of the scope from which it
6984 * was taken.
6985 *
6986 * For example consider a directive that uses transclusion and isolated scope. The DOM hierarchy might look
6987 * like this:
6988 *
6989 * ```html
6990 * <div ng-app>
6991 *   <div isolate>
6992 *     <div transclusion>
6993 *     </div>
6994 *   </div>
6995 * </div>
6996 * ```
6997 *
6998 * The `$parent` scope hierarchy will look like this:
6999 *
7000 * ```
7001 * - $rootScope
7002 *   - isolate
7003 *     - transclusion
7004 * ```
7005 *
7006 * but the scopes will inherit prototypically from different scopes to their `$parent`.
7007 *
7008 * ```
7009 * - $rootScope
7010 *   - transclusion
7011 * - isolate
7012 * ```
7013 *
7014 *
7015 * ### Attributes
7016 *
7017 * The {@link ng.$compile.directive.Attributes Attributes} object - passed as a parameter in the
7018 * `link()` or `compile()` functions. It has a variety of uses.
7019 *
7020 * accessing *Normalized attribute names:*
7021 * Directives like 'ngBind' can be expressed in many ways: 'ng:bind', `data-ng-bind`, or 'x-ng-bind'.
7022 * the attributes object allows for normalized access to
7023 *   the attributes.
7024 *
7025 * * *Directive inter-communication:* All directives share the same instance of the attributes
7026 *   object which allows the directives to use the attributes object as inter directive
7027 *   communication.
7028 *
7029 * * *Supports interpolation:* Interpolation attributes are assigned to the attribute object
7030 *   allowing other directives to read the interpolated value.
7031 *
7032 * * *Observing interpolated attributes:* Use `$observe` to observe the value changes of attributes
7033 *   that contain interpolation (e.g. `src="{{bar}}"`). Not only is this very efficient but it's also
7034 *   the only way to easily get the actual value because during the linking phase the interpolation
7035 *   hasn't been evaluated yet and so the value is at this time set to `undefined`.
7036 *
7037 * ```js
7038 * function linkingFn(scope, elm, attrs, ctrl) {
7039 *   // get the attribute value
7040 *   console.log(attrs.ngModel);
7041 *
7042 *   // change the attribute
7043 *   attrs.$set('ngModel', 'new value');
7044 *
7045 *   // observe changes to interpolated attribute
7046 *   attrs.$observe('ngModel', function(value) {
7047 *     console.log('ngModel has changed value to ' + value);
7048 *   });
7049 * }
7050 * ```
7051 *
7052 * ## Example
7053 *
7054 * <div class="alert alert-warning">
7055 * **Note**: Typically directives are registered with `module.directive`. The example below is
7056 * to illustrate how `$compile` works.
7057 * </div>
7058 *
7059 <example module="compileExample">
7060   <file name="index.html">
7061    <script>
7062      angular.module('compileExample', [], function($compileProvider) {
7063        // configure new 'compile' directive by passing a directive
7064        // factory function. The factory function injects the '$compile'
7065        $compileProvider.directive('compile', function($compile) {
7066          // directive factory creates a link function
7067          return function(scope, element, attrs) {
7068            scope.$watch(
7069              function(scope) {
7070                 // watch the 'compile' expression for changes
7071                return scope.$eval(attrs.compile);
7072              },
7073              function(value) {
7074                // when the 'compile' expression changes
7075                // assign it into the current DOM
7076                element.html(value);
7077
7078                // compile the new DOM and link it to the current
7079                // scope.
7080                // NOTE: we only compile .childNodes so that
7081                // we don't get into infinite loop compiling ourselves
7082                $compile(element.contents())(scope);
7083              }
7084            );
7085          };
7086        });
7087      })
7088      .controller('GreeterController', ['$scope', function($scope) {
7089        $scope.name = 'Angular';
7090        $scope.html = 'Hello {{name}}';
7091      }]);
7092    </script>
7093    <div ng-controller="GreeterController">
7094      <input ng-model="name"> <br/>
7095      <textarea ng-model="html"></textarea> <br/>
7096      <div compile="html"></div>
7097    </div>
7098   </file>
7099   <file name="protractor.js" type="protractor">
7100     it('should auto compile', function() {
7101       var textarea = $('textarea');
7102       var output = $('div[compile]');
7103       // The initial state reads 'Hello Angular'.
7104       expect(output.getText()).toBe('Hello Angular');
7105       textarea.clear();
7106       textarea.sendKeys('{{name}}!');
7107       expect(output.getText()).toBe('Angular!');
7108     });
7109   </file>
7110 </example>
7111
7112 *
7113 *
7114 * @param {string|DOMElement} element Element or HTML string to compile into a template function.
7115 * @param {function(angular.Scope, cloneAttachFn=)} transclude function available to directives - DEPRECATED.
7116 *
7117 * <div class="alert alert-danger">
7118 * **Note:** Passing a `transclude` function to the $compile function is deprecated, as it
7119 *   e.g. will not use the right outer scope. Please pass the transclude function as a
7120 *   `parentBoundTranscludeFn` to the link function instead.
7121 * </div>
7122 *
7123 * @param {number} maxPriority only apply directives lower than given priority (Only effects the
7124 *                 root element(s), not their children)
7125 * @returns {function(scope, cloneAttachFn=, options=)} a link function which is used to bind template
7126 * (a DOM element/tree) to a scope. Where:
7127 *
7128 *  * `scope` - A {@link ng.$rootScope.Scope Scope} to bind to.
7129 *  * `cloneAttachFn` - If `cloneAttachFn` is provided, then the link function will clone the
7130 *  `template` and call the `cloneAttachFn` function allowing the caller to attach the
7131 *  cloned elements to the DOM document at the appropriate place. The `cloneAttachFn` is
7132 *  called as: <br/> `cloneAttachFn(clonedElement, scope)` where:
7133 *
7134 *      * `clonedElement` - is a clone of the original `element` passed into the compiler.
7135 *      * `scope` - is the current scope with which the linking function is working with.
7136 *
7137 *  * `options` - An optional object hash with linking options. If `options` is provided, then the following
7138 *  keys may be used to control linking behavior:
7139 *
7140 *      * `parentBoundTranscludeFn` - the transclude function made available to
7141 *        directives; if given, it will be passed through to the link functions of
7142 *        directives found in `element` during compilation.
7143 *      * `transcludeControllers` - an object hash with keys that map controller names
7144 *        to controller instances; if given, it will make the controllers
7145 *        available to directives.
7146 *      * `futureParentElement` - defines the parent to which the `cloneAttachFn` will add
7147 *        the cloned elements; only needed for transcludes that are allowed to contain non html
7148 *        elements (e.g. SVG elements). See also the directive.controller property.
7149 *
7150 * Calling the linking function returns the element of the template. It is either the original
7151 * element passed in, or the clone of the element if the `cloneAttachFn` is provided.
7152 *
7153 * After linking the view is not updated until after a call to $digest which typically is done by
7154 * Angular automatically.
7155 *
7156 * If you need access to the bound view, there are two ways to do it:
7157 *
7158 * - If you are not asking the linking function to clone the template, create the DOM element(s)
7159 *   before you send them to the compiler and keep this reference around.
7160 *   ```js
7161 *     var element = $compile('<p>{{total}}</p>')(scope);
7162 *   ```
7163 *
7164 * - if on the other hand, you need the element to be cloned, the view reference from the original
7165 *   example would not point to the clone, but rather to the original template that was cloned. In
7166 *   this case, you can access the clone via the cloneAttachFn:
7167 *   ```js
7168 *     var templateElement = angular.element('<p>{{total}}</p>'),
7169 *         scope = ....;
7170 *
7171 *     var clonedElement = $compile(templateElement)(scope, function(clonedElement, scope) {
7172 *       //attach the clone to DOM document at the right place
7173 *     });
7174 *
7175 *     //now we have reference to the cloned DOM via `clonedElement`
7176 *   ```
7177 *
7178 *
7179 * For information on how the compiler works, see the
7180 * {@link guide/compiler Angular HTML Compiler} section of the Developer Guide.
7181 */
7182
7183var $compileMinErr = minErr('$compile');
7184
7185/**
7186 * @ngdoc provider
7187 * @name $compileProvider
7188 *
7189 * @description
7190 */
7191$CompileProvider.$inject = ['$provide', '$$sanitizeUriProvider'];
7192function $CompileProvider($provide, $$sanitizeUriProvider) {
7193  var hasDirectives = {},
7194      Suffix = 'Directive',
7195      COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\w\-]+)\s+(.*)$/,
7196      CLASS_DIRECTIVE_REGEXP = /(([\w\-]+)(?:\:([^;]+))?;?)/,
7197      ALL_OR_NOTHING_ATTRS = makeMap('ngSrc,ngSrcset,src,srcset'),
7198      REQUIRE_PREFIX_REGEXP = /^(?:(\^\^?)?(\?)?(\^\^?)?)?/;
7199
7200  // Ref: http://developers.whatwg.org/webappapis.html#event-handler-idl-attributes
7201  // The assumption is that future DOM event attribute names will begin with
7202  // 'on' and be composed of only English letters.
7203  var EVENT_HANDLER_ATTR_REGEXP = /^(on[a-z]+|formaction)$/;
7204
7205  function parseIsolateBindings(scope, directiveName, isController) {
7206    var LOCAL_REGEXP = /^\s*([@&]|=(\*?))(\??)\s*(\w*)\s*$/;
7207
7208    var bindings = {};
7209
7210    forEach(scope, function(definition, scopeName) {
7211      var match = definition.match(LOCAL_REGEXP);
7212
7213      if (!match) {
7214        throw $compileMinErr('iscp',
7215            "Invalid {3} for directive '{0}'." +
7216            " Definition: {... {1}: '{2}' ...}",
7217            directiveName, scopeName, definition,
7218            (isController ? "controller bindings definition" :
7219            "isolate scope definition"));
7220      }
7221
7222      bindings[scopeName] = {
7223        mode: match[1][0],
7224        collection: match[2] === '*',
7225        optional: match[3] === '?',
7226        attrName: match[4] || scopeName
7227      };
7228    });
7229
7230    return bindings;
7231  }
7232
7233  function parseDirectiveBindings(directive, directiveName) {
7234    var bindings = {
7235      isolateScope: null,
7236      bindToController: null
7237    };
7238    if (isObject(directive.scope)) {
7239      if (directive.bindToController === true) {
7240        bindings.bindToController = parseIsolateBindings(directive.scope,
7241                                                         directiveName, true);
7242        bindings.isolateScope = {};
7243      } else {
7244        bindings.isolateScope = parseIsolateBindings(directive.scope,
7245                                                     directiveName, false);
7246      }
7247    }
7248    if (isObject(directive.bindToController)) {
7249      bindings.bindToController =
7250          parseIsolateBindings(directive.bindToController, directiveName, true);
7251    }
7252    if (isObject(bindings.bindToController)) {
7253      var controller = directive.controller;
7254      var controllerAs = directive.controllerAs;
7255      if (!controller) {
7256        // There is no controller, there may or may not be a controllerAs property
7257        throw $compileMinErr('noctrl',
7258              "Cannot bind to controller without directive '{0}'s controller.",
7259              directiveName);
7260      } else if (!identifierForController(controller, controllerAs)) {
7261        // There is a controller, but no identifier or controllerAs property
7262        throw $compileMinErr('noident',
7263              "Cannot bind to controller without identifier for directive '{0}'.",
7264              directiveName);
7265      }
7266    }
7267    return bindings;
7268  }
7269
7270  function assertValidDirectiveName(name) {
7271    var letter = name.charAt(0);
7272    if (!letter || letter !== lowercase(letter)) {
7273      throw $compileMinErr('baddir', "Directive name '{0}
vendor: 4,630 bytes, lines 7273-7385
7273' is invalid. The first character must be a lowercase letter", name);
7274    }
7275    if (name !== name.trim()) {
7276      throw $compileMinErr('baddir',
7277            "Directive name '{0}' is invalid. The name should not contain leading or trailing whitespaces",
7278            name);
7279    }
7280  }
7281
7282  /**
7283   * @ngdoc method
7284   * @name $compileProvider#directive
7285   * @kind function
7286   *
7287   * @description
7288   * Register a new directive with the compiler.
7289   *
7290   * @param {string|Object} name Name of the directive in camel-case (i.e. <code>ngBind</code> which
7291   *    will match as <code>ng-bind</code>), or an object map of directives where the keys are the
7292   *    names and the values are the factories.
7293   * @param {Function|Array} directiveFactory An injectable directive factory function. See
7294   *    {@link guide/directive} for more info.
7295   * @returns {ng.$compileProvider} Self for chaining.
7296   */
7297   this.directive = function registerDirective(name, directiveFactory) {
7298    assertNotHasOwnProperty(name, 'directive');
7299    if (isString(name)) {
7300      assertValidDirectiveName(name);
7301      assertArg(directiveFactory, 'directiveFactory');
7302      if (!hasDirectives.hasOwnProperty(name)) {
7303        hasDirectives[name] = [];
7304        $provide.factory(name + Suffix, ['$injector', '$exceptionHandler',
7305          function($injector, $exceptionHandler) {
7306            var directives = [];
7307            forEach(hasDirectives[name], function(directiveFactory, index) {
7308              try {
7309                var directive = $injector.invoke(directiveFactory);
7310                if (isFunction(directive)) {
7311                  directive = { compile: valueFn(directive) };
7312                } else if (!directive.compile && directive.link) {
7313                  directive.compile = valueFn(directive.link);
7314                }
7315                directive.priority = directive.priority || 0;
7316                directive.index = index;
7317                directive.name = directive.name || name;
7318                directive.require = directive.require || (directive.controller && directive.name);
7319                directive.restrict = directive.restrict || 'EA';
7320                var bindings = directive.$$bindings =
7321                    parseDirectiveBindings(directive, directive.name);
7322                if (isObject(bindings.isolateScope)) {
7323                  directive.$$isolateBindings = bindings.isolateScope;
7324                }
7325                directive.$$moduleName = directiveFactory.$$moduleName;
7326                directives.push(directive);
7327              } catch (e) {
7328                $exceptionHandler(e);
7329              }
7330            });
7331            return directives;
7332          }]);
7333      }
7334      hasDirectives[name].push(directiveFactory);
7335    } else {
7336      forEach(name, reverseParams(registerDirective));
7337    }
7338    return this;
7339  };
7340
7341
7342  /**
7343   * @ngdoc method
7344   * @name $compileProvider#aHrefSanitizationWhitelist
7345   * @kind function
7346   *
7347   * @description
7348   * Retrieves or overrides the default regular expression that is used for whitelisting of safe
7349   * urls during a[href] sanitization.
7350   *
7351   * The sanitization is a security measure aimed at preventing XSS attacks via html links.
7352   *
7353   * Any url about to be assigned to a[href] via data-binding is first normalized and turned into
7354   * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist`
7355   * regular expression. If a match is found, the original url is written into the dom. Otherwise,
7356   * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
7357   *
7358   * @param {RegExp=} regexp New regexp to whitelist urls with.
7359   * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
7360   *    chaining otherwise.
7361   */
7362  this.aHrefSanitizationWhitelist = function(regexp) {
7363    if (isDefined(regexp)) {
7364      $$sanitizeUriProvider.aHrefSanitizationWhitelist(regexp);
7365      return this;
7366    } else {
7367      return $$sanitizeUriProvider.aHrefSanitizationWhitelist();
7368    }
7369  };
7370
7371
7372  /**
7373   * @ngdoc method
7374   * @name $compileProvider#imgSrcSanitizationWhitelist
7375   * @kind function
7376   *
7377   * @description
7378   * Retrieves or overrides the default regular expression that is used for whitelisting of safe
7379   * urls during img[src] sanitization.
7380   *
7381   * The sanitization is a security measure aimed at prevent XSS attacks via html links.
7382   *
7383   * Any url about to be assigned to img[src] via data-binding is first normalized and turned into
7384   * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist`
7385   * regular expression. If a match is found, the original url is 
7385written into the dom. Otherwise,
7386   * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
7387   *
7388   * @param {RegExp=} regexp New regexp to whitelist urls with.
7389   * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
7390   *    chaining otherwise.
7391   */
7392  this.imgSrcSanitizationWhitelist = function(regexp) {
7393    if (isDefined(regexp)) {
7394      $$sanitizeUriProvider.imgSrcSanitizationWhitelist(regexp);
7395      return this;
7396    } else {
7397      return $$sanitizeUriProvider.imgSrcSanitizationWhitelist();
7398    }
7399  };
7400
7401  /**
7402   * @ngdoc method
7403   * @name  $compileProvider#debugInfoEnabled
7404   *
7405   * @param {boolean=} enabled update the debugInfoEnabled state if provided, otherwise just return the
7406   * current debugInfoEnabled state
7407   * @returns {*} current value if used as getter or itself (chaining) if used as setter
7408   *
7409   * @kind function
7410   *
7411   * @description
7412   * Call this method to enable/disable various debug runtime information in the compiler such as adding
7413   * binding information and a reference to the current scope on to DOM elements.
7414   * If enabled, the compiler will add the following to DOM elements that have been bound to the scope
7415   * * `ng-binding` CSS class
7416   * * `$binding` data property containing an array of the binding expressions
7417   *
7418   * You may want to disable this in production for a significant performance boost. See
7419   * {@link guide/production#disabling-debug-data Disabling Debug Data} for more.
7420   *
7421   * The default value is true.
7422   */
7423  var debugInfoEnabled = true;
7424  this.debugInfoEnabled = function(enabled) {
7425    if (isDefined(enabled)) {
7426      debugInfoEnabled = enabled;
7427      return this;
7428    }
7429    return debugInfoEnabled;
7430  };
7431
7432  this.$get = [
7433            '$injector', '$interpolate', '$exceptionHandler', '$templateRequest', '$parse',
7434            '$controller', '$rootScope', '$document', '$sce', '$animate', '$$sanitizeUri',
7435    function($injector,   $interpolate,   $exceptionHandler,   $templateRequest,   $parse,
7436             $controller,   $rootScope,   $document,   $sce,   $animate,   $$sanitizeUri) {
7437
7438    var Attributes = function(element, attributesToCopy) {
7439      if (attributesToCopy) {
7440        var keys = Object.keys(attributesToCopy);
7441        var i, l, key;
7442
7443        for (i = 0, l = keys.length; i < l; i++) {
7444          key = keys[i];
7445          this[key] = attributesToCopy[key];
7446        }
7447      } else {
7448        this.$attr = {};
7449      }
7450
7451      this.$$element = element;
7452    };
7453
7454    Attributes.prototype = {
7455      /**
7456       * @ngdoc method
7457       * @name $compile.directive.Attributes#$normalize
7458       * @kind function
7459       *
7460       * @description
7461       * Converts an attribute name (e.g. dash/colon/underscore-delimited string, optionally prefixed with `x-` or
7462       * `data-`) to its normalized, camelCase form.
7463       *
7464       * Also there is special case for Moz prefix starting with upper case letter.
7465       *
7466       * For further information check out the guide on {@link guide/directive#matching-directives Matching Directives}
7467       *
7468       * @param {string} name Name to normalize
7469       */
7470      $normalize: directiveNormalize,
7471
7472
7473      /**
7474       * @ngdoc method
7475       * @name $compile.directive.Attributes#$addClass
7476       * @kind function
7477       *
7478       * @description
7479       * Adds the CSS class value specified by the classVal parameter to the element. If animations
7480       * are enabled then an animation will be triggered for the class addition.
7481       *
7482       * @param {string} classVal The className value that will be added to the element
7483       */
7484      $addClass: function(classVal) {
7485        if (classVal && classVal.length > 0) {
7486          $animate.addClass(this.$$element, classVal);
7487        }
7488      },
7489
7490      /**
7491       * @ngdoc method
7492       * @name $compile.directive.Attributes#$removeClass
7493       * @kind function
7494       *
7495       * @description
7496       * Removes the CSS class value specified by the classVal parameter from the element. If
7497       * animations are enabled then an animation will be triggered for the class removal.
7498       *
7499       * @param {string} classVal The className value that will be removed from the element
7500       */
7501      $removeClass: function(classVal) {
7502        if (classVal && classVal.length > 0) {
7503          $animate.removeClass(this.$$element, classVal);
7504        }
7505      },
7506
7507      /**
7508       * @ngdoc method
7509       * @name $compile.directive.Attributes#$updateClass
7510       * @kind function
7511       *
7512       * @description
7513       * Adds and removes the appropriate CSS class values to the element based on the difference
7514       * between the new and old CSS class values (specified as newClasses and oldClasses).
7515       *
7516       * @param {string} newClasses The current CSS className value
7517       * @param {string} oldClasses The former CSS className value
7518       */
7519      $updateClass: function(newClasses, oldClasses) {
7520        var toAdd = tokenDifference(newClasses, oldClasses);
7521        if (toAdd && toAdd.length) {
7522          $animate.addClass(this.$$element, toAdd);
7523        }
7524
7525        var toRemove = tokenDifference(oldClasses, newClasses);
7526        if (toRemove && toRemove.length) {
7527          $animate.removeClass(this.$$element, toRemove);
7528        }
7529      },
7530
7531      /**
7532       * Set a normalized attribute on the element in a way such that all directives
7533       * can share the attribute. This function properly handles boolean attributes.
7534       * @param {string} key Normalized key. (ie ngAttribute)
7535       * @param {string|boolean} value The value to set. If `null` attribute will be deleted.
7536       * @param {boolean=} writeAttr If false, does not write the value to DOM element attribute.
7537       *     Defaults to true.
7538       * @param {string=} attrName Optional none normalized name. Defaults to key.
7539       */
7540      $set: function(key, value, writeAttr, attrName) {
7541        // TODO: decide whether or not to throw an error if "class"
7542        //is set through this function since it may cause $updateClass to
7543        //become unstable.
7544
7545        var node = this.$$element[0],
7546            booleanKey = getBooleanAttrName(node, key),
7547            aliasedKey = getAliasedAttrName(key),
7548            observer = key,
7549            nodeName;
7550
7551        if (booleanKey) {
7552          this.$$element.prop(key, value);
7553          attrName = booleanKey;
7554        } else if (aliasedKey) {
7555          this[aliasedKey] = value;
7556          observer = aliasedKey;
7557        }
7558
7559        this[key] = value;
7560
7561        // translate normalized key to actual key
7562        if (attrName) {
7563          this.$attr[key] = attrName;
7564        } else {
7565          attrName = this.$attr[key];
7566          if (!attrName) {
7567            this.$attr[key] = attrName = snake_case(key, '-');
7568          }
7569        }
7570
7571        nodeName = nodeName_(this.$$element);
7572
7573        if ((nodeName === 'a' && key === 'href') ||
7574            (nodeName === 'img' && key === 'src')) {
7575          // sanitize a[href] and img[src] values
7576          this[key] = value = $$sanitizeUri(value, key === 'src');
7577        } else if (nodeName === 'img' && key === 'srcset') {
7578          // sanitize img[srcset] values
7579          var result = "";
7580
7581          // first check if there are spaces because it's not the same pattern
7582          var trimmedSrcset = trim(value);
7583          //                (   999x   ,|   999w   ,|   ,|,   )
7584          var srcPattern = /(\s+\d+x\s*,|\s+\d+w\s*,|\s+,|,\s+)/;
7585          var pattern = /\s/.test(trimmedSrcset) ? srcPattern : /(,)/;
7586
7587          // split srcset into tuple of uri and descriptor except for the last item
7588          var rawUris = trimmedSrcset.split(pattern);
7589
7590          // for each tuples
7591          var nbrUrisWith2parts = Math.floor(rawUris.length / 2);
7592          for (var i = 0; i < nbrUrisWith2parts; i++) {
7593            var innerIdx = i * 2;
7594            // sanitize the uri
7595            result += $$sanitizeUri(trim(rawUris[innerIdx]), true);
7596            // add the descriptor
7597            result += (" " + trim(rawUris[innerIdx + 1]));
7598          }
7599
7600          // split the last item into uri and descriptor
7601          var lastTuple = trim(rawUris[i * 2]).split(/\s/);
7602
7603          // sanitize the last uri
7604          result += $$sanitizeUri(trim(lastTuple[0]), true);
7605
7606          // and add the last descriptor if any
7607          if (lastTuple.length === 2) {
7608            result += (" " + trim(lastTuple[1]));
7609          }
7610          this[key] = value = result;
7611        }
7612
7613        if (writeAttr !== false) {
7614          if (value === null || isUndefined(value)) {
7615            this.$$element.removeAttr(attrName);
7616          } else {
7617            this.$$element.attr(attrName, value);
7618          }
7619        }
7620
7621        // fire observers
7622        var $$observers = this.$$observers;
vendor: 13,821 bytes, lines 7623-7963
7623        $$observers && forEach($$observers[observer], function(fn) {
7624          try {
7625            fn(value);
7626          } catch (e) {
7627            $exceptionHandler(e);
7628          }
7629        });
7630      },
7631
7632
7633      /**
7634       * @ngdoc method
7635       * @name $compile.directive.Attributes#$observe
7636       * @kind function
7637       *
7638       * @description
7639       * Observes an interpolated attribute.
7640       *
7641       * The observer function will be invoked once during the next `$digest` following
7642       * compilation. The observer is then invoked whenever the interpolated value
7643       * changes.
7644       *
7645       * @param {string} key Normalized key. (ie ngAttribute) .
7646       * @param {function(interpolatedValue)} fn Function that will be called whenever
7647                the interpolated value of the attribute changes.
7648       *        See the {@link guide/directive#text-and-attribute-bindings Directives} guide for more info.
7649       * @returns {function()} Returns a deregistration function for this observer.
7650       */
7651      $observe: function(key, fn) {
7652        var attrs = this,
7653            $$observers = (attrs.$$observers || (attrs.$$observers = createMap())),
7654            listeners = ($$observers[key] || ($$observers[key] = []));
7655
7656        listeners.push(fn);
7657        $rootScope.$evalAsync(function() {
7658          if (!listeners.$$inter && attrs.hasOwnProperty(key) && !isUndefined(attrs[key])) {
7659            // no one registered attribute interpolation function, so lets call it manually
7660            fn(attrs[key]);
7661          }
7662        });
7663
7664        return function() {
7665          arrayRemove(listeners, fn);
7666        };
7667      }
7668    };
7669
7670
7671    function safeAddClass($element, className) {
7672      try {
7673        $element.addClass(className);
7674      } catch (e) {
7675        // ignore, since it means that we are trying to set class on
7676        // SVG element, where class name is read-only.
7677      }
7678    }
7679
7680
7681    var startSymbol = $interpolate.startSymbol(),
7682        endSymbol = $interpolate.endSymbol(),
7683        denormalizeTemplate = (startSymbol == '{{' || endSymbol  == '}}')
7684            ? identity
7685            : function denormalizeTemplate(template) {
7686              return template.replace(/\{\{/g, startSymbol).replace(/}}/g, endSymbol);
7687        },
7688        NG_ATTR_BINDING = /^ngAttr[A-Z]/;
7689
7690    compile.$$addBindingInfo = debugInfoEnabled ? function $$addBindingInfo($element, binding) {
7691      var bindings = $element.data('$binding') || [];
7692
7693      if (isArray(binding)) {
7694        bindings = bindings.concat(binding);
7695      } else {
7696        bindings.push(binding);
7697      }
7698
7699      $element.data('$binding', bindings);
7700    } : noop;
7701
7702    compile.$$addBindingClass = debugInfoEnabled ? function $$addBindingClass($element) {
7703      safeAddClass($element, 'ng-binding');
7704    } : noop;
7705
7706    compile.$$addScopeInfo = debugInfoEnabled ? function $$addScopeInfo($element, scope, isolated, noTemplate) {
7707      var dataName = isolated ? (noTemplate ? '$isolateScopeNoTemplate' : '$isolateScope') : '$scope';
7708      $element.data(dataName, scope);
7709    } : noop;
7710
7711    compile.$$addScopeClass = debugInfoEnabled ? function $$addScopeClass($element, isolated) {
7712      safeAddClass($element, isolated ? 'ng-isolate-scope' : 'ng-scope');
7713    } : noop;
7714
7715    return compile;
7716
7717    //================================
7718
7719    function compile($compileNodes, transcludeFn, maxPriority, ignoreDirective,
7720                        previousCompileContext) {
7721      if (!($compileNodes instanceof jqLite)) {
7722        // jquery always rewraps, whereas we need to preserve the original selector so that we can
7723        // modify it.
7724        $compileNodes = jqLite($compileNodes);
7725      }
7726      // We can not compile top level text elements since text nodes can be merged and we will
7727      // not be able to attach scope data to them, so we will wrap them in <span>
7728      forEach($compileNodes, function(node, index) {
7729        if (node.nodeType == NODE_TYPE_TEXT && node.nodeValue.match(/\S+/) /* non-empty */ ) {
7730          $compileNodes[index] = jqLite(node).wrap('<span></span>').parent()[0];
7731        }
7732      });
7733      var compositeLinkFn =
7734              compileNodes($compileNodes, transcludeFn, $compileNodes,
7735                           maxPriority, ignoreDirective, previousCompileContext);
7736      compile.$$addScopeClass($compileNodes);
7737      var namespace = null;
7738      return function publicLinkFn(scope, cloneConnectFn, options) {
7739        assertArg(scope, 'scope');
7740
7741        options = options || {};
7742        var parentBoundTranscludeFn = options.parentBoundTranscludeFn,
7743          transcludeControllers = options.transcludeControllers,
7744          futureParentElement = options.futureParentElement;
7745
7746        // When `parentBoundTranscludeFn` is passed, it is a
7747        // `controllersBoundTransclude` function (it was previously passed
7748        // as `transclude` to directive.link) so we must unwrap it to get
7749        // its `boundTranscludeFn`
7750        if (parentBoundTranscludeFn && parentBoundTranscludeFn.$$boundTransclude) {
7751          parentBoundTranscludeFn = parentBoundTranscludeFn.$$boundTransclude;
7752        }
7753
7754        if (!namespace) {
7755          namespace = detectNamespaceForChildElements(futureParentElement);
7756        }
7757        var $linkNode;
7758        if (namespace !== 'html') {
7759          // When using a directive with replace:true and templateUrl the $compileNodes
7760          // (or a child element inside of them)
7761          // might change, so we need to recreate the namespace adapted compileNodes
7762          // for call to the link function.
7763          // Note: This will already clone the nodes...
7764          $linkNode = jqLite(
7765            wrapTemplate(namespace, jqLite('<div>').append($compileNodes).html())
7766          );
7767        } else if (cloneConnectFn) {
7768          // important!!: we must call our jqLite.clone() since the jQuery one is trying to be smart
7769          // and sometimes changes the structure of the DOM.
7770          $linkNode = JQLitePrototype.clone.call($compileNodes);
7771        } else {
7772          $linkNode = $compileNodes;
7773        }
7774
7775        if (transcludeControllers) {
7776          for (var controllerName in transcludeControllers) {
7777            $linkNode.data('$' + controllerName + 'Controller', transcludeControllers[controllerName].instance);
7778          }
7779        }
7780
7781        compile.$$addScopeInfo($linkNode, scope);
7782
7783        if (cloneConnectFn) cloneConnectFn($linkNode, scope);
7784        if (compositeLinkFn) compositeLinkFn(scope, $linkNode, $linkNode, parentBoundTranscludeFn);
7785        return $linkNode;
7786      };
7787    }
7788
7789    function detectNamespaceForChildElements(parentElement) {
7790      // TODO: Make this detect MathML as well...
7791      var node = parentElement && parentElement[0];
7792      if (!node) {
7793        return 'html';
7794      } else {
7795        return nodeName_(node) !== 'foreignobject' && node.toString().match(/SVG/) ? 'svg' : 'html';
7796      }
7797    }
7798
7799    /**
7800     * Compile function matches each node in nodeList against the directives. Once all directives
7801     * for a particular node are collected their compile functions are executed. The compile
7802     * functions return values - the linking functions - are combined into a composite linking
7803     * function, which is the a linking function for the node.
7804     *
7805     * @param {NodeList} nodeList an array of nodes or NodeList to compile
7806     * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the
7807     *        scope argument is auto-generated to the new child of the transcluded parent scope.
7808     * @param {DOMElement=} $rootElement If the nodeList is the root of the compilation tree then
7809     *        the rootElement must be set the jqLite collection of the compile root. This is
7810     *        needed so that the jqLite collection items can be replaced with widgets.
7811     * @param {number=} maxPriority Max directive priority.
7812     * @returns {Function} A composite linking function of all of the matched directives or null.
7813     */
7814    function compileNodes(nodeList, transcludeFn, $rootElement, maxPriority, ignoreDirective,
7815                            previousCompileContext) {
7816      var linkFns = [],
7817          attrs, directives, nodeLinkFn, childNodes, childLinkFn, linkFnFound, nodeLinkFnFound;
7818
7819      for (var i = 0; i < nodeList.length; i++) {
7820        attrs = new Attributes();
7821
7822        // we must always refer to nodeList[i] since the nodes can be replaced underneath us.
7823        directives = collectDirectives(nodeList[i], [], attrs, i === 0 ? maxPriority : undefined,
7824                                        ignoreDirective);
7825
7826        nodeLinkFn = (directives.length)
7827            ? applyDirectivesToNode(directives, nodeList[i], attrs, transcludeFn, $rootElement,
7828                                      null, [], [], previousCompileContext)
7829            : null;
7830
7831        if (nodeLinkFn && nodeLinkFn.scope) {
7832          compile.$$addScopeClass(attrs.$$element);
7833        }
7834
7835        childLinkFn = (nodeLinkFn && nodeLinkFn.terminal ||
7836                      !(childNodes = nodeList[i].childNodes) ||
7837                      !childNodes.length)
7838            ? null
7839            : compileNodes(childNodes,
7840                 nodeLinkFn ? (
7841                  (nodeLinkFn.transcludeOnThisElement || !nodeLinkFn.templateOnThisElement)
7842                     && nodeLinkFn.transclude) : transcludeFn);
7843
7844        if (nodeLinkFn || childLinkFn) {
7845          linkFns.push(i, nodeLinkFn, childLinkFn);
7846          linkFnFound = true;
7847          nodeLinkFnFound = nodeLinkFnFound || nodeLinkFn;
7848        }
7849
7850        //use the previous context only for the first element in the virtual group
7851        previousCompileContext = null;
7852      }
7853
7854      // return a linking function if we have found anything, null otherwise
7855      return linkFnFound ? compositeLinkFn : null;
7856
7857      function compositeLinkFn(scope, nodeList, $rootElement, parentBoundTranscludeFn) {
7858        var nodeLinkFn, childLinkFn, node, childScope, i, ii, idx, childBoundTranscludeFn;
7859        var stableNodeList;
7860
7861
7862        if (nodeLinkFnFound) {
7863          // copy nodeList so that if a nodeLinkFn removes or adds an element at this DOM level our
7864          // offsets don't get screwed up
7865          var nodeListLength = nodeList.length;
7866          stableNodeList = new Array(nodeListLength);
7867
7868          // create a sparse array by only copying the elements which have a linkFn
7869          for (i = 0; i < linkFns.length; i+=3) {
7870            idx = linkFns[i];
7871            stableNodeList[idx] = nodeList[idx];
7872          }
7873        } else {
7874          stableNodeList = nodeList;
7875        }
7876
7877        for (i = 0, ii = linkFns.length; i < ii;) {
7878          node = stableNodeList[linkFns[i++]];
7879          nodeLinkFn = linkFns[i++];
7880          childLinkFn = linkFns[i++];
7881
7882          if (nodeLinkFn) {
7883            if (nodeLinkFn.scope) {
7884              childScope = scope.$new();
7885              compile.$$addScopeInfo(jqLite(node), childScope);
7886              var destroyBindings = nodeLinkFn.$$destroyBindings;
7887              if (destroyBindings) {
7888                nodeLinkFn.$$destroyBindings = null;
7889                childScope.$on('$destroyed', destroyBindings);
7890              }
7891            } else {
7892              childScope = scope;
7893            }
7894
7895            if (nodeLinkFn.transcludeOnThisElement) {
7896              childBoundTranscludeFn = createBoundTranscludeFn(
7897                  scope, nodeLinkFn.transclude, parentBoundTranscludeFn);
7898
7899            } else if (!nodeLinkFn.templateOnThisElement && parentBoundTranscludeFn) {
7900              childBoundTranscludeFn = parentBoundTranscludeFn;
7901
7902            } else if (!parentBoundTranscludeFn && transcludeFn) {
7903              childBoundTranscludeFn = createBoundTranscludeFn(scope, transcludeFn);
7904
7905            } else {
7906              childBoundTranscludeFn = null;
7907            }
7908
7909            nodeLinkFn(childLinkFn, childScope, node, $rootElement, childBoundTranscludeFn,
7910                       nodeLinkFn);
7911
7912          } else if (childLinkFn) {
7913            childLinkFn(scope, node.childNodes, undefined, parentBoundTranscludeFn);
7914          }
7915        }
7916      }
7917    }
7918
7919    function createBoundTranscludeFn(scope, transcludeFn, previousBoundTranscludeFn) {
7920
7921      var boundTranscludeFn = function(transcludedScope, cloneFn, controllers, futureParentElement, containingScope) {
7922
7923        if (!transcludedScope) {
7924          transcludedScope = scope.$new(false, containingScope);
7925          transcludedScope.$$transcluded = true;
7926        }
7927
7928        return transcludeFn(transcludedScope, cloneFn, {
7929          parentBoundTranscludeFn: previousBoundTranscludeFn,
7930          transcludeControllers: controllers,
7931          futureParentElement: futureParentElement
7932        });
7933      };
7934
7935      return boundTranscludeFn;
7936    }
7937
7938    /**
7939     * Looks for directives on the given node and adds them to the directive collection which is
7940     * sorted.
7941     *
7942     * @param node Node to search.
7943     * @param directives An array to which the directives are added to. This array is sorted before
7944     *        the function returns.
7945     * @param attrs The shared attrs object which is used to populate the normalized attributes.
7946     * @param {number=} maxPriority Max directive priority.
7947     */
7948    function collectDirectives(node, directives, attrs, maxPriority, ignoreDirective) {
7949      var nodeType = node.nodeType,
7950          attrsMap = attrs.$attr,
7951          match,
7952          className;
7953
7954      switch (nodeType) {
7955        case NODE_TYPE_ELEMENT: /* Element */
7956          // use the node name: <directive>
7957          addDirective(directives,
7958              directiveNormalize(nodeName_(node)), 'E', maxPriority, ignoreDirective);
7959
7960          // iterate over the attributes
7961          for (var attr, name, nName, ngAttrName, value, isNgAttr, nAttrs = node.attributes,
7962                   j = 0, jj = nAttrs && nAttrs.length; j < jj; j++) {
7963            var attrStartName = false;
7964            var attrEndName = false;
7965
7966            attr = nAttrs[j];
7967            name = attr.name;
7968            value = trim(attr.value);
7969
7970            // support ngAttr attribute binding
7971            ngAttrName = directiveNormalize(name);
7972            if (isNgAttr = NG_ATTR_BINDING.test(ngAttrName)) {
7973              name = name.replace(PREFIX_REGEXP, '')
7974                .substr(8).replace(/_(.)/g, function(match, letter) {
7975                  return letter.toUpperCase();
7976                });
7977            }
7978
7979            var directiveNName = ngAttrName.replace(/(Start|End)$/, '');
7980            if (directiveIsMultiElement(directiveNName)) {
7981              if (ngAttrName === directiveNName + 'Start') {
7982                attrStartName = name;
7983                attrEndName = name.substr(0, name.length - 5) + 'end';
7984                name = name.substr(0, name.length - 6);
7985              }
7986            }
7987
7988            nName = directiveNormalize(name.toLowerCase());
7989            attrsMap[nName] = name;
7990            if (isNgAttr || !attrs.hasOwnProperty(nName)) {
7991                attrs[nName] = value;
7992                if (getBooleanAttrName(node, nName)) {
7993                  attrs[nName] = true; // presence means true
7994                }
7995            }
7996            addAttrInterpolateDirective(node, directives, value, nName, isNgAttr);
7997            addDirective(directives, nName, 'A', maxPriority, ignoreDirective, attrStartName,
7998                          attrEndName);
7999          }
8000
8001          // use class as directive
8002          className = node.className;
8003          if (isObject(className)) {
8004              // Maybe SVGAnimatedString
8005              className = className.animVal;
8006          }
8007          if (isString(className) && className !== '') {
8008            while (match = CLASS_DIRECTIVE_REGEXP.exec(className)) {
8009              nName = directiveNormalize(match[2]);
8010              if (addDirective(directives, nName, 'C', maxPriority, ignoreDirective)) {
8011                attrs[nName] = trim(match[3]);
8012              }
8013              className = className.substr(match.index + match[0].length);
8014            }
8015          }
8016          break;
8017        case NODE_TYPE_TEXT: /* Text Node */
8018          if (msie === 11) {
8019            // Workaround for #11781
8020            while (node.parentNode && node.nextSibling && node.nextSibling.nodeType === NODE_TYPE_TEXT) {
8021              node.nodeValue = node.nodeValue + node.nextSibling.nodeValue;
8022              node.parentNode.removeChild(node.nextSibling);
8023            }
8024          }
8025          addTextInterpolateDirective(directives, node.nodeValue);
8026          break;
8027        case NODE_TYPE_COMMENT: /* Comment */
8028          try {
8029            match = COMMENT_DIRECTIVE_REGEXP.exec(node.nodeValue);
8030            if (match) {
8031              nName = directiveNormalize(match[1]);
8032              if (addDirective(directives, nName, 'M', maxPriority, ignoreDirective)) {
8033                attrs[nName] = trim(match[2]);
8034              }
8035            }
8036          } catch (e) {
8037            // turns out that under some circumstances IE9 throws errors when one attempts to read
8038            // comment's node value.
8039            // Just ignore it and continue. (Can't seem to reproduce in test case.)
8040          }
8041          break;
8042      }
8043
8044      directives.sort(byPriority);
8045      return directives;
8046    }
8047
8048    /**
8049     * Given a node with an directive-start it collects all of the siblings until it finds
8050     * directive-end.
8051     * @param node
8052     * @param attrStart
8053     * @param attrEnd
8054     * @returns {*}
8055     */
8056    function groupScan(node, attrStart, attrEnd) {
8057      var nodes = [];
8058      var depth = 0;
8059      if (attrStart && node.hasAttribute && node.hasAttribute(attrStart)) {
8060        do {
8061          if (!node) {
8062            throw $compileMinErr('uterdir',
8063                      "Unterminated attribute, found '{0}' but no matching '{1}' found.",
8064                      attrStart, attrEnd);
8065          }
8066          if (node.nodeType == NODE_TYPE_ELEMENT) {
8067            if (node.hasAttribute(attrStart)) depth++;
8068            if (node.hasAttribute(attrEnd)) depth--;
8069          }
8070          nodes.push(node);
8071          node = node.nextSibling;
8072        } while (depth > 0);
8073      } else {
8074        nodes.push(node);
8075      }
8076
8077      return jqLite(nodes);
8078    }
8079
8080    /**
8081     * Wrapper for linking function which converts normal linking function into a grouped
8082     * linking function.
8083     * @param linkFn
8084     * @param attrStart
8085     * @param attrEnd
8086     * @returns {Function}
8087     */
8088    function groupElementsLinkFnWrapper(linkFn, attrStart, attrEnd) {
8089      return function(scope, element, attrs, controllers, transcludeFn) {
8090        element = groupScan(element[0], attrStart, attrEnd);
8091        return linkFn(scope, element, attrs, controllers, transcludeFn);
8092      };
8093    }
8094
8095    /**
8096     * Once the directives have been collected, their compile functions are executed. This method
8097     * is responsible for inlining directive templates as well as terminating the application
8098     * of the directives if the terminal directive has been reached.
8099     *
8100     * @param {Array} directives Array of collected directives to execute their compile function.
8101     *        this needs to be pre-sorted by priority order.
8102     * @param {Node} compileNode The raw DOM node to apply the compile functions to
8103     * @param {Object} templateAttrs The shared attribute function
8104     * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the
8105     *                                                  scope argument is auto-generated to the new
8106     *                                                  child of the transcluded parent scope.
8107     * @param {JQLite} jqCollection If we are working on the root of the compile tree then this
8108     *                              argument has the root jqLite array so that we can replace nodes
8109     *                              on it.
8110     * @param {Object=} originalReplaceDirective An optional directive that will be ignored when
8111     *                                           compiling the transclusion.
8112     * @param {Array.<Function>} preLinkFns
8113     * @param {Array.<Function>} postLinkFns
8114     * @param {Object} previousCompileContext Context used for previous compilation of the current
8115     *                                        node
8116     * @returns {Function} linkFn
8117     */
8118    function applyDirectivesToNode(directives, compileNode, templateAttrs, transcludeFn,
8119                                   jqCollection, originalReplaceDirective, preLinkFns, postLinkFns,
8120                                   previousCompileContext) {
8121      previousCompileContext = previousCompileContext || {};
8122
8123      var terminalPriority = -Number.MAX_VALUE,
8124          newScopeDirective = previousCompileContext.newScopeDirective,
8125          controllerDirectives = previousCompileContext.controllerDirectives,
8126          newIsolateScopeDirective = previousCompileContext.newIsolateScopeDirective,
8127          templateDirective = previousCompileContext.templateDirective,
8128          nonTlbTranscludeDirective = previousCompileContext.nonTlbTranscludeDirective,
8129          hasTranscludeDirective = false,
8130          hasTemplate = false,
8131          hasElementTranscludeDirective = previousCompileContext.hasElementTranscludeDirective,
8132          $compileNode = templateAttrs.$$element = jqLite(compileNode),
8133          directive,
8134          directiveName,
8135          $template,
8136          replaceDirective = originalReplaceDirective,
8137          childTranscludeFn = transcludeFn,
8138          linkFn,
8139          directiveValue;
8140
8141      // executes all directives on the current element
8142      for (var i = 0, ii = directives.length; i < ii; i++) {
8143        directive = directives[i];
8144        var attrStart = directive.$$start;
8145        var attrEnd = directive.$$end;
8146
8147        // collect multiblock sections
8148        if (attrStart) {
8149          $compileNode = groupScan(compileNode, attrStart, attrEnd);
8150        }
8151        $template = undefined;
8152
8153        if (terminalPriority > directive.priority) {
8154          break; // prevent further processing of directives
8155        }
8156
8157        if (directiveValue = directive.scope) {
8158
8159          // skip the check for directives with async templates, we'll check the derived sync
8160          // directive when the template arrives
8161          if (!directive.templateUrl) {
8162            if (isObject(directiveValue)) {
8163              // This directive is trying to add an isolated scope.
8164              // Check that there is no scope of any kind already
8165              assertNoDuplicate('new/isolated scope', newIsolateScopeDirective || newScopeDirective,
8166                                directive, $compileNode);
8167              newIsolateScopeDirective = directive;
8168            } else {
8169              // This directive is trying to add a child scope.
8170              // Check that there is no isolated scope already
8171              assertNoDuplicate('new/isolated scope', newIsolateScopeDirective, directive,
8172                                $compileNode);
8173            }
8174          }
8175
8176          newScopeDirective = newScopeDirective || directive;
8177        }
8178
8179        directiveName = directive.name;
8180
8181        if (!directive.templateUrl && directive.controller) {
8182          directiveValue = directive.controller;
8183          controllerDirectives = controllerDirectives || createMap();
8184          assertNoDuplicate("'" + directiveName + "' controller",
8185              controllerDirectives[directiveName], directive, $compileNode);
8186          controllerDirectives[directiveName] = directive;
8187        }
8188
8189        if (directiveValue = directive.transclude) {
8190          hasTranscludeDirective = true;
8191
8192          // Special case ngIf and ngRepeat so that we don't complain about duplicate transclusion.
8193          // This option should only be used by directives that know how to safely handle element transclusion,
8194          // where the transcluded nodes are added or replaced after linking.
8195          if (!directive.$$tlb) {
8196            assertNoDuplicate('transclusion', nonTlbTranscludeDirective, directive, $compileNode);
8197            nonTlbTranscludeDirective = directive;
8198          }
8199
8200          if (directiveValue == 'element') {
8201            hasElementTranscludeDirective = true;
8202            terminalPriority = directive.priority;
8203            $template = $compileNode;
8204            $compileNode = templateAttrs.$$element =
8205                jqLite(document.createComment(' ' + directiveName + ': ' +
8206                                              templateAttrs[directiveName] + ' '));
8207            compileNode = $compileNode[0];
8208            replaceWith(jqCollection, sliceArgs($template), compileNode);
8209
8210            childTranscludeFn = compile($template, transcludeFn, terminalPriority,
8211                                        replaceDirective && replaceDirective.name, {
8212                                          // Don't pass in:
8213                                          // - controllerDirectives - otherwise we'll create duplicates controllers
8214                                          // - newIsolateScopeDirective or templateDirective - comb
vendor: 4,761 bytes, lines 8214-8322
8214ining templates with
8215                                          //   element transclusion doesn't make sense.
8216                                          //
8217                                          // We need only nonTlbTranscludeDirective so that we prevent putting transclusion
8218                                          // on the same element more than once.
8219                                          nonTlbTranscludeDirective: nonTlbTranscludeDirective
8220                                        });
8221          } else {
8222            $template = jqLite(jqLiteClone(compileNode)).contents();
8223            $compileNode.empty(); // clear contents
8224            childTranscludeFn = compile($template, transcludeFn);
8225          }
8226        }
8227
8228        if (directive.template) {
8229          hasTemplate = true;
8230          assertNoDuplicate('template', templateDirective, directive, $compileNode);
8231          templateDirective = directive;
8232
8233          directiveValue = (isFunction(directive.template))
8234              ? directive.template($compileNode, templateAttrs)
8235              : directive.template;
8236
8237          directiveValue = denormalizeTemplate(directiveValue);
8238
8239          if (directive.replace) {
8240            replaceDirective = directive;
8241            if (jqLiteIsTextNode(directiveValue)) {
8242              $template = [];
8243            } else {
8244              $template = removeComments(wrapTemplate(directive.templateNamespace, trim(directiveValue)));
8245            }
8246            compileNode = $template[0];
8247
8248            if ($template.length != 1 || compileNode.nodeType !== NODE_TYPE_ELEMENT) {
8249              throw $compileMinErr('tplrt',
8250                  "Template for directive '{0}' must have exactly one root element. {1}",
8251                  directiveName, '');
8252            }
8253
8254            replaceWith(jqCollection, $compileNode, compileNode);
8255
8256            var newTemplateAttrs = {$attr: {}};
8257
8258            // combine directives from the original node and from the template:
8259            // - take the array of directives for this element
8260            // - split it into two parts, those that already applied (processed) and those that weren't (unprocessed)
8261            // - collect directives from the template and sort them by priority
8262            // - combine directives as: processed + template + unprocessed
8263            var templateDirectives = collectDirectives(compileNode, [], newTemplateAttrs);
8264            var unprocessedDirectives = directives.splice(i + 1, directives.length - (i + 1));
8265
8266            if (newIsolateScopeDirective) {
8267              markDirectivesAsIsolate(templateDirectives);
8268            }
8269            directives = directives.concat(templateDirectives).concat(unprocessedDirectives);
8270            mergeTemplateAttributes(templateAttrs, newTemplateAttrs);
8271
8272            ii = directives.length;
8273          } else {
8274            $compileNode.html(directiveValue);
8275          }
8276        }
8277
8278        if (directive.templateUrl) {
8279          hasTemplate = true;
8280          assertNoDuplicate('template', templateDirective, directive, $compileNode);
8281          templateDirective = directive;
8282
8283          if (directive.replace) {
8284            replaceDirective = directive;
8285          }
8286
8287          nodeLinkFn = compileTemplateUrl(directives.splice(i, directives.length - i), $compileNode,
8288              templateAttrs, jqCollection, hasTranscludeDirective && childTranscludeFn, preLinkFns, postLinkFns, {
8289                controllerDirectives: controllerDirectives,
8290                newScopeDirective: (newScopeDirective !== directive) && newScopeDirective,
8291                newIsolateScopeDirective: newIsolateScopeDirective,
8292                templateDirective: templateDirective,
8293                nonTlbTranscludeDirective: nonTlbTranscludeDirective
8294              });
8295          ii = directives.length;
8296        } else if (directive.compile) {
8297          try {
8298            linkFn = directive.compile($compileNode, templateAttrs, childTranscludeFn);
8299            if (isFunction(linkFn)) {
8300              addLinkFns(null, linkFn, attrStart, attrEnd);
8301            } else if (linkFn) {
8302              addLinkFns(linkFn.pre, linkFn.post, attrStart, attrEnd);
8303            }
8304          } catch (e) {
8305            $exceptionHandler(e, startingTag($compileNode));
8306          }
8307        }
8308
8309        if (directive.terminal) {
8310          nodeLinkFn.terminal = true;
8311          terminalPriority = Math.max(terminalPriority, directive.priority);
8312        }
8313
8314      }
8315
8316      nodeLinkFn.scope = newScopeDirective && newScopeDirective.scope === true;
8317      nodeLinkFn.transcludeOnThisElement = hasTranscludeDirective;
8318      nodeLinkFn.templateOnThisElement = hasTemplate;
8319      nodeLinkFn.transclude = childTranscludeFn;
8320
8321      previousCompileContext.hasElementTranscludeDirective = hasElementTranscludeDirective;
8322
8323      // might be normal or delayed nodeLinkFn depending on if templateUrl is present
8324      return nodeLinkFn;
8325
8326      ////////////////////
8327
8328      function addLinkFns(pre, post, attrStart, attrEnd) {
8329        if (pre) {
8330          if (attrStart) pre = groupElementsLinkFnWrapper(pre, attrStart, attrEnd);
8331          pre.require = directive.require;
8332          pre.directiveName = directiveName;
8333          if (newIsolateScopeDirective === directive || directive.$$isolateScope) {
8334            pre = cloneAndAnnotateFn(pre, {isolateScope: true});
8335          }
8336          preLinkFns.push(pre);
8337        }
8338        if (post) {
8339          if (attrStart) post = groupElementsLinkFnWrapper(post, attrStart, attrEnd);
8340          post.require = directive.require;
8341          post.directiveName = directiveName;
8342          if (newIsolateScopeDirective === directive || directive.$$isolateScope) {
8343            post = cloneAndAnnotateFn(post, {isolateScope: true});
8344          }
8345          postLinkFns.push(post);
8346        }
8347      }
8348
8349
8350      function getControllers(directiveName, require, $element, elementControllers) {
8351        var value;
8352
8353        if (isString(require)) {
8354          var match = require.match(REQUIRE_PREFIX_REGEXP);
8355          var name = require.substring(match[0].length);
8356          var inheritType = match[1] || match[3];
8357          var optional = match[2] === '?';
8358
8359          //If only parents then start at the parent element
8360          if (inheritType === '^^') {
8361            $element = $element.parent();
8362          //Otherwise attempt getting the controller from elementControllers in case
8363          //the element is transcluded (and has no data) and to avoid .data if possible
8364          } else {
8365            value = elementControllers && elementControllers[name];
8366            value = value && value.instance;
8367          }
8368
8369          if (!value) {
8370            var dataName = '$' + name + 'Controller';
8371            value = inheritType ? $element.inheritedData(dataName) : $element.data(dataName);
8372          }
8373
8374          if (!value && !optional) {
8375            throw $compileMinErr('ctreq',
8376                "Controller '{0}', required by directive '{1}', can't be found!",
8377                name, directiveName);
8378          }
8379        } else if (isArray(require)) {
8380          value = [];
8381          for (var i = 0, ii = require.length; i < ii; i++) {
8382            value[i] = getControllers(directiveName, require[i], $element, elementControllers);
8383          }
8384        }
8385
8386        return value || null;
8387      }
8388
8389      function setupControllers($element, attrs, transcludeFn, controllerDirectives, isolateScope, scope) {
8390        var elementControllers = createMap();
8391        for (var controllerKey in controllerDirectives) {
8392          var directive = controllerDirectives[controllerKey];
8393          var locals = {
8394            $scope: directive === newIsolateScopeDirective || directive.$$isolateScope ? isolateScope : scope,
8395            $element: $element,
8396            $attrs: attrs,
8397            $transclude: transcludeFn
8398          };
8399
8400          var controller = directive.controller;
8401          if (controller == '@') {
8402            controller = attrs[directive.name];
8403          }
8404
8405          var controllerInstance = $controller(controller, locals, true, directive.controllerAs);
8406
8407          // For directives with element transclusion the element is a comment,
8408          // but jQuery .data doesn't support attaching data to comment nodes as it's hard to
8409          // clean up (http://bugs.jquery.com/ticket/8335).
8410          // Instead, we save the controllers for the element in a local hash and attach to .data
8411          // later, once we have the actual element.
8412          elementControllers[directive.name] = controllerInstance;
8413          if (!hasElementTranscludeDirective) {
8414            $element.data('$' + directive.name + 'Controller', controllerInstance.instance);
8415          }
8416        }
8417        return elementControllers;
8418      }
8419
8420      function nodeLinkFn(childLinkFn, scope, linkNode, $rootElement, boundTranscludeFn,
8421                          thisLinkFn) {
8422        var i, ii, linkFn, controller, isolateScope, elementControllers, transcludeFn, $element,
8423            attrs;
8424
8425        if (compileNode === linkNode) {
8426          attrs = templateAttrs;
8427          $element = templateAttrs.$$element;
8428        } else {
8429          $element = jqLite(linkNode);
8430          attrs = new Attributes($element, templateAttrs);
8431        }
8432
8433        if (newIsolateScopeDirective) {
8434          isolateScope = scope.$new(true);
8435        }
8436
8437        if (boundTranscludeFn) {
8438          // track `boundTranscludeFn` so it can be unwrapped if `transcludeFn`
8439          // is later passed as `parentBoundTranscludeFn` to `publicLinkFn`
8440          transcludeFn = controllersBoundTransclude;
8441          transcludeFn.$$boundTransclude = boundTranscludeFn;
8442        }
8443
8444        if (controllerDirectives) {
8445          elementControllers = setupControllers($element, attrs, transcludeFn, controllerDirectives, isolateScope, scope);
8446        }
8447
8448        if (newIsolateScopeDirective) {
8449          // Initialize isolate scope bindings for new isolate scope directive.
8450          compile.$$addScopeInfo($element, isolateScope, true, !(templateDirective && (templateDirective === newIsolateScopeDirective ||
8451              templateDirective === newIsolateScopeDirective.$$originalDirective)));
8452          compile.$$addScopeClass($element, true);
8453          isolateScope.$$isolateBindings =
8454              newIsolateScopeDirective.$$isolateBindings;
8455          initializeDirectiveBindings(scope, attrs, isolateScope,
8456                                      isolateScope.$$isolateBindings,
8457                                      newIsolateScopeDirective, isolateScope);
8458        }
8459        if (elementControllers) {
8460          // Initialize bindToController bindings for new/isolate scopes
8461          var scopeDirective = newIsolateScopeDirective || newScopeDirective;
8462          var bindings;
8463          var controllerForBindings;
8464          if (scopeDirective && elementControllers[scopeDirective.name]) {
8465            bindings = scopeDirective.$$bindings.bindToController;
8466            controller = elementControllers[scopeDirective.name];
8467
8468            if (controller && controller.identifier && bindings) {
8469              controllerForBindings = controller;
8470              thisLinkFn.$$destroyBindings =
8471                  initializeDirectiveBindings(scope, attrs, controller.instance,
8472                                              bindings, scopeDirective);
8473            }
8474          }
8475          for (i in elementControllers) {
8476            controller = elementControllers[i];
8477            var controllerResult = controller();
8478
8479            if (controllerResult !== controller.instance) {
8480              // If the controller constructor has a return value, overwrite the instance
8481              // from setupControllers and update the element data
8482              controller.instance = controllerResult;
8483              $element.data('$' + i + 'Controller', controllerResult);
8484              if (controller === controllerForBindings) {
8485                // Remove and re-install bindToController bindings
8486                thisLinkFn.$$destroyBindings();
8487                thisLinkFn.$$destroyBindings =
8488                  initializeDirectiveBindings(scope, attrs, controllerResult, bindings, scopeDirective);
8489              }
8490            }
8491          }
8492        }
8493
8494        // PRELINKING
8495        for (i = 0, ii = preLinkFns.length; i < ii; i++) {
8496          linkFn = preLinkFns[i];
8497          invokeLinkFn(linkFn,
8498              linkFn.isolateScope ? isolateScope : scope,
8499              $element,
8500              attrs,
8501              linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers),
8502              transcludeFn
8503          );
8504        }
8505
8506        // RECURSION
8507        // We only pass the isolate scope, if the isolate directive has a template,
8508        // otherwise the child elements do not belong to the isolate directive.
8509        var scopeToChild = scope;
8510        if (newIsolateScopeDirective && (newIsolateScopeDirective.template || newIsolateScopeDirective.templateUrl === null)) {
8511          scopeToChild = isolateScope;
8512        }
8513        childLinkFn && childLinkFn(scopeToChild, linkNode.childNodes, undefined, boundTranscludeFn);
8514
8515        // POSTLINKING
8516        for (i = postLinkFns.length - 1; i >= 0; i--) {
8517          linkFn = postLinkFns[i];
8518          invokeLinkFn(linkFn,
8519              linkFn.isolateScope ? isolateScope : scope,
8520              $element,
8521              attrs,
8522              linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers),
8523              transcludeFn
8524          );
8525        }
8526
8527        // This is the function that is injected as `$transclude`.
8528        // Note: all arguments are optional!
8529        function controllersBoundTransclude(scope, cloneAttachFn, futureParentElement) {
8530          var transcludeControllers;
8531
8532          // No scope passed in:
8533          if (!isScope(scope)) {
8534            futureParentElement = cloneAttachFn;
8535            cloneAttachFn = scope;
8536            scope = undefined;
8537          }
8538
8539          if (hasElementTranscludeDirective) {
8540            transcludeControllers = elementControllers;
8541          }
8542          if (!futureParentElement) {
8543            futureParentElement = hasElementTranscludeDirective ? $element.parent() : $element;
8544          }
8545          return boundTranscludeFn(scope, cloneAttachFn, transcludeControllers, futureParentElement, scopeToChild);
8546        }
8547      }
8548    }
8549
8550    function markDirectivesAsIsolate(directives) {
8551      // mark all directives as needing isolate scope.
8552      for (var j = 0, jj = directives.length; j < jj; j++) {
8553        directives[j] = inherit(directives[j], {$$isolateScope: true});
8554      }
8555    }
8556
8557    /**
8558     * looks up the directive and decorates it with exception handling and proper parameters. We
8559     * call this the boundDirective.
8560     *
8561     * @param {string} name name of the directive to look up.
8562     * @param {string} location The directive must be found in specific format.
8563     *   String containing any of theses characters:
8564     *
8565     *   * `E`: element name
8566     *   * `A': attribute
8567     *   * `C`: class
8568     *   * `M`: comment
8569     * @returns {boolean} true if directive was added.
8570     */
8571    function addDirective(tDirectives, name, location, maxPriority, ignoreDirective, startAttrName,
8572                          endAttrName) {
8573      if (name === ignoreDirective) return null;
8574      var match = null;
8575      if (hasDirectives.hasOwnProperty(name)) {
8576        for (var directive, directives = $injector.get(name + Suffix),
8577            i = 0, ii = directives.length; i < ii; i++) {
8578          try {
8579            directive = directives[i];
8580            if ((isUndefined(maxPriority) || maxPriority > directive.priority) &&
8581                 directive.restrict.indexOf(location) != -1) {
8582              if (startAttrName) {
8583                directive = inherit(directive, {$$start: startAttrName, $$end: endAttrName});
8584              }
8585              tDirectives.push(directive);
8586              match = directive;
8587            }
8588          } catch (e) { $exceptionHandler(e); }
8589        }
8590      }
8591      return match;
8592    }
8593
8594
8595    /**
8596     * looks up the directive and returns true if it is a multi-element directive,
8597     * and therefore requires DOM nodes between -start and -end markers to be grouped
8598     * together.
8599     *
8600     * @param {string} name name of the directive to look up.
8601     * @returns true if directive was registered as multi-element.
8602     */
8603    function directiveIsMultiElement(name) {
8604      if (hasDirectives.hasOwnProperty(name)) {
8605        for (var directive, directives = $injector.get(name + Suffix),
8606            i = 0, ii = directives.length; i < ii; i++) {
8607          directive = directives[i];
8608          if (directive.multiElement) {
8609            return true;
8610          }
8611        }
8612      }
8613      return false;
8614    }
8615
8616    /**
8617     * When the element is replaced with HTML template then the new attributes
8618     * on the template need to be merged with the existing attributes in the DOM.
8619     * The desired effect is to have both of the attributes present.
8620     *
8621     * @param {object} dst destination attributes (original DOM)
8622     * @param {object} src source attributes (from the directive template)
8623     */
8624    function mergeTemplateAttributes(dst, src) {
8625      var srcAttr = src.$attr,
8626          dstAttr = dst.$attr,
8627          $element = dst.$$element;
8628
8629      // reapply the old attributes to the new element
8630      forEach(dst, function(value, key) {
8631        if (key.charAt(0) != '$') {
8632          if (src[key] && src[key] !== value) {
8633            value += (key === 'style' ? ';' : ' ') + src[key];
8634          }
8635          dst.$set(key, value, true, srcAttr[key]);
8636        }
8637      });
8638
8639      // copy the new attributes on the old attrs object
8640      forEach(src, function(value, key) {
8641        if (key == 'class') {
8642          safeAddClass($element, value);
8643          dst['class'] = (dst['class'] ? dst['class'] + ' ' : '') + value;
8644        } else if (key == 'style') {
8645          $element.attr('style', $element.attr('style') + ';' + value);
8646          dst['style'] = (dst['style'] ? dst['style'] + ';' : '') + value;
8647          // `dst` will never contain hasOwnProperty as DOM parser won't let it.
8648          // You will get an "InvalidCharacterError: DOM Exception 5" error if you
8649          // have an attribute like "has-own-property" or "data-has-own-property", etc.
8650        } else if (key.charAt(0) != '$' && !dst.hasOwnProperty(key)) {
8651          dst[key] = value;
8652          dstAttr[key] = srcAttr[key];
8653        }
8654      });
8655    }
8656
8657
8658    function compileTemplateUrl(directives, $compileNode, tAttrs,
8659        $rootElement, childTranscludeFn, preLinkFns, postLinkFns, previousCompileContext) {
8660      var linkQueue = [],
8661          afterTemplateNodeLinkFn,
8662          afterTemplateChildLinkFn,
8663          beforeTemplateCompileNode = $compileNode[0],
8664          origAsyncDirective = directives.shift(),
8665          derivedSyncDirective = inherit(origAsyncDirective, {
8666            templateUrl: null, transclude: null, replace: null, $$originalDirective: origAsyncDirective
8667          }),
8668          templateUrl = (isFunction(origAsyncDirective.templateUrl))
8669              ? origAsyncDirective.templateUrl($compileNode, tAttrs)
8670              : origAsyncDirective.templateUrl,
8671          templateNamespace = origAsyncDirective.templateNamespace;
8672
8673      $compileNode.empty();
8674
8675      $templateRequest(templateUrl)
8676        .then(function(content) {
8677          var compileNode, tempTemplateAttrs, $template, childBoundTranscludeFn;
8678
8679          content = denormalizeTemplate(content);
8680
8681          if (origAsyncDirective.replace) {
8682            if (jqLiteIsTextNode(content)) {
8683              $template = [];
8684            } else {
8685              $template = removeComments(wrapTemplate(templateNamespace, trim(content)));
8686            }
8687            compileNode = $template[0];
8688
8689            if ($template.length != 1 || compileNode.nodeType !== NODE_TYPE_ELEMENT) {
8690              throw $compileMinErr('tplrt',
8691                  "Template for directive '{0}' must have exactly one root element. {1}",
8692                  origAsyncDirective.name, templateUrl);
8693            }
8694
8695            tempTemplateAttrs = {$attr: {}};
8696            replaceWith($rootElement, $compileNode, compileNode);
8697            var templateDirectives = collectDirectives(compileNode, [], tempTemplateAttrs);
8698
8699            if (isObject(origAsyncDirective.scope)) {
8700              markDirectivesAsIsolate(templateDirectives);
8701            }
8702            directives = templateDirectives.concat(directives);
8703            mergeTemplateAttributes(tAttrs, tempTemplateAttrs);
8704          } else {
8705            compileNode = beforeTemplateCompileNode;
8706            $compileNode.html(content);
8707          }
8708
8709          directives.unshift(derivedSyncDirective);
8710
8711          afterTemplateNodeLinkFn = applyDirectivesToNode(directives, compileNode, tAttrs,
8712              childTranscludeFn, $compileNode, origAsyncDirective, preLinkFns, postLinkFns,
8713              previousCompileContext);
vendor: 6,217 bytes, lines 8714-8876
8714          forEach($rootElement, function(node, i) {
8715            if (node == compileNode) {
8716              $rootElement[i] = $compileNode[0];
8717            }
8718          });
8719          afterTemplateChildLinkFn = compileNodes($compileNode[0].childNodes, childTranscludeFn);
8720
8721          while (linkQueue.length) {
8722            var scope = linkQueue.shift(),
8723                beforeTemplateLinkNode = linkQueue.shift(),
8724                linkRootElement = linkQueue.shift(),
8725                boundTranscludeFn = linkQueue.shift(),
8726                linkNode = $compileNode[0];
8727
8728            if (scope.$$destroyed) continue;
8729
8730            if (beforeTemplateLinkNode !== beforeTemplateCompileNode) {
8731              var oldClasses = beforeTemplateLinkNode.className;
8732
8733              if (!(previousCompileContext.hasElementTranscludeDirective &&
8734                  origAsyncDirective.replace)) {
8735                // it was cloned therefore we have to clone as well.
8736                linkNode = jqLiteClone(compileNode);
8737              }
8738              replaceWith(linkRootElement, jqLite(beforeTemplateLinkNode), linkNode);
8739
8740              // Copy in CSS classes from original node
8741              safeAddClass(jqLite(linkNode), oldClasses);
8742            }
8743            if (afterTemplateNodeLinkFn.transcludeOnThisElement) {
8744              childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn);
8745            } else {
8746              childBoundTranscludeFn = boundTranscludeFn;
8747            }
8748            afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, linkNode, $rootElement,
8749              childBoundTranscludeFn, afterTemplateNodeLinkFn);
8750          }
8751          linkQueue = null;
8752        });
8753
8754      return function delayedNodeLinkFn(ignoreChildLinkFn, scope, node, rootElement, boundTranscludeFn) {
8755        var childBoundTranscludeFn = boundTranscludeFn;
8756        if (scope.$$destroyed) return;
8757        if (linkQueue) {
8758          linkQueue.push(scope,
8759                         node,
8760                         rootElement,
8761                         childBoundTranscludeFn);
8762        } else {
8763          if (afterTemplateNodeLinkFn.transcludeOnThisElement) {
8764            childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn);
8765          }
8766          afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, node, rootElement, childBoundTranscludeFn,
8767                                  afterTemplateNodeLinkFn);
8768        }
8769      };
8770    }
8771
8772
8773    /**
8774     * Sorting function for bound directives.
8775     */
8776    function byPriority(a, b) {
8777      var diff = b.priority - a.priority;
8778      if (diff !== 0) return diff;
8779      if (a.name !== b.name) return (a.name < b.name) ? -1 : 1;
8780      return a.index - b.index;
8781    }
8782
8783    function assertNoDuplicate(what, previousDirective, directive, element) {
8784
8785      function wrapModuleNameIfDefined(moduleName) {
8786        return moduleName ?
8787          (' (module: ' + moduleName + ')') :
8788          '';
8789      }
8790
8791      if (previousDirective) {
8792        throw $compileMinErr('multidir', 'Multiple directives [{0}{1}, {2}{3}] asking for {4} on: {5}',
8793            previousDirective.name, wrapModuleNameIfDefined(previousDirective.$$moduleName),
8794            directive.name, wrapModuleNameIfDefined(directive.$$moduleName), what, startingTag(element));
8795      }
8796    }
8797
8798
8799    function addTextInterpolateDirective(directives, text) {
8800      var interpolateFn = $interpolate(text, true);
8801      if (interpolateFn) {
8802        directives.push({
8803          priority: 0,
8804          compile: function textInterpolateCompileFn(templateNode) {
8805            var templateNodeParent = templateNode.parent(),
8806                hasCompileParent = !!templateNodeParent.length;
8807
8808            // When transcluding a template that has bindings in the root
8809            // we don't have a parent and thus need to add the class during linking fn.
8810            if (hasCompileParent) compile.$$addBindingClass(templateNodeParent);
8811
8812            return function textInterpolateLinkFn(scope, node) {
8813              var parent = node.parent();
8814              if (!hasCompileParent) compile.$$addBindingClass(parent);
8815              compile.$$addBindingInfo(parent, interpolateFn.expressions);
8816              scope.$watch(interpolateFn, function interpolateFnWatchAction(value) {
8817                node[0].nodeValue = value;
8818              });
8819            };
8820          }
8821        });
8822      }
8823    }
8824
8825
8826    function wrapTemplate(type, template) {
8827      type = lowercase(type || 'html');
8828      switch (type) {
8829      case 'svg':
8830      case 'math':
8831        var wrapper = document.createElement('div');
8832        wrapper.innerHTML = '<' + type + '>' + template + '</' + type + '>';
8833        return wrapper.childNodes[0].childNodes;
8834      default:
8835        return template;
8836      }
8837    }
8838
8839
8840    function getTrustedContext(node, attrNormalizedName) {
8841      if (attrNormalizedName == "srcdoc") {
8842        return $sce.HTML;
8843      }
8844      var tag = nodeName_(node);
8845      // maction[xlink:href] can source SVG.  It's not limited to <maction>.
8846      if (attrNormalizedName == "xlinkHref" ||
8847          (tag == "form" && attrNormalizedName == "action") ||
8848          (tag != "img" && (attrNormalizedName == "src" ||
8849                            attrNormalizedName == "ngSrc"))) {
8850        return $sce.RESOURCE_URL;
8851      }
8852    }
8853
8854
8855    function addAttrInterpolateDirective(node, directives, value, name, allOrNothing) {
8856      var trustedContext = getTrustedContext(node, name);
8857      allOrNothing = ALL_OR_NOTHING_ATTRS[name] || allOrNothing;
8858
8859      var interpolateFn = $interpolate(value, true, trustedContext, allOrNothing);
8860
8861      // no interpolation found -> ignore
8862      if (!interpolateFn) return;
8863
8864
8865      if (name === "multiple" && nodeName_(node) === "select") {
8866        throw $compileMinErr("selmulti",
8867            "Binding to the 'multiple' attribute is not supported. Element: {0}",
8868            startingTag(node));
8869      }
8870
8871      directives.push({
8872        priority: 100,
8873        compile: function() {
8874            return {
8875              pre: function attrInterpolatePreLinkFn(scope, element, attr) {
8876                var $$observers = (attr.$$observers || (attr.$$observers = createMap())
8876);
8877
8878                if (EVENT_HANDLER_ATTR_REGEXP.test(name)) {
8879                  throw $compileMinErr('nodomevents',
8880                      "Interpolations for HTML DOM event attributes are disallowed.  Please use the " +
8881                          "ng- versions (such as ng-click instead of onclick) instead.");
8882                }
8883
8884                // If the attribute has changed since last $interpolate()ed
8885                var newValue = attr[name];
8886                if (newValue !== value) {
8887                  // we need to interpolate again since the attribute value has been updated
8888                  // (e.g. by another directive's compile function)
8889                  // ensure unset/empty values make interpolateFn falsy
8890                  interpolateFn = newValue && $interpolate(newValue, true, trustedContext, allOrNothing);
8891                  value = newValue;
8892                }
8893
8894                // if attribute was updated so that there is no interpolation going on we don't want to
8895                // register any observers
8896                if (!interpolateFn) return;
8897
8898                // initialize attr object so that it's ready in case we need the value for isolate
8899                // scope initialization, otherwise the value would not be available from isolate
8900                // directive's linking fn during linking phase
8901                attr[name] = interpolateFn(scope);
8902
8903                ($$observers[name] || ($$observers[name] = [])).$$inter = true;
8904                (attr.$$observers && attr.$$observers[name].$$scope || scope).
8905                  $watch(interpolateFn, function interpolateFnWatchAction(newValue, oldValue) {
8906                    //special case for class attribute addition + removal
8907                    //so that class changes can tap into the animation
8908                    //hooks provided by the $animate service. Be sure to
8909                    //skip animations when the first digest occurs (when
8910                    //both the new and the old values are the same) since
8911                    //the CSS classes are the non-interpolated values
8912                    if (name === 'class' && newValue != oldValue) {
8913                      attr.$updateClass(newValue, oldValue);
8914                    } else {
8915                      attr.$set(name, newValue);
8916                    }
8917                  });
8918              }
8919            };
8920          }
8921      });
8922    }
8923
8924
8925    /**
8926     * This is a special jqLite.replaceWith, which can replace items which
8927     * have no parents, provided that the containing jqLite collection is provided.
8928     *
8929     * @param {JqLite=} $rootElement The root of the compile tree. Used so that we can replace nodes
8930     *                               in the root of the tree.
8931     * @param {JqLite} elementsToRemove The jqLite element which we are going to replace. We keep
8932     *                                  the shell, but replace its DOM node reference.
8933     * @param {Node} newNode The new DOM node.
8934     */
8935    function replaceWith($rootElement, elementsToRemove, newNode) {
8936      var firstElementToRemove = elementsToRemove[0],
8937          removeCount = elementsToRemove.length,
8938          parent = firstElementToRemove.parentNode,
8939          i, ii;
8940
8941      if ($rootElement) {
8942        for (i = 0, ii = $rootElement.length; i < ii; i++) {
8943          if ($rootElement[i] == firstElementToRemove) {
8944            $rootElement[i++] = newNode;
8945            for (var j = i, j2 = j + removeCount - 1,
8946                     jj = $rootElement.length;
8947                 j < jj; j++, j2++) {
8948              if (j2 < jj) {
8949                $rootElement[j] = $rootElement[j2];
8950              } else {
8951                delete $rootElement[j];
8952              }
8953            }
8954            $rootElement.length -= removeCount - 1;
8955
8956            // If the replaced element is also the jQuery .context then replace it
8957            // .context is a deprecated jQuery api, so we should set it only when jQuery set it
8958            // http://api.jquery.com/context/
8959            if ($rootElement.context === firstElementToRemove) {
8960              $rootElement.context = newNode;
8961            }
8962            break;
8963          }
8964        }
8965      }
8966
8967      if (parent) {
8968        parent.replaceChild(newNode, firstElementToRemove);
8969      }
8970
8971      // TODO(perf): what's this document fragment for? is it needed? can we at least reuse it?
8972      var fragment = document.createDocumentFragment();
8973      fragment.appendChild(firstElementToRemove);
8974
8975      if (jqLite.hasData(firstElementToRemove)) {
8976        // Copy over user data (that includes Angular's $scope etc.). Don't copy private
8977        // data here because there's no public interface in jQuery to do that and copying over
8978        // event listeners (which is the main use of private data) wouldn't work anyway.
8979        jqLite(newNode).data(jqLite(firstElementToRemove).data());
8980
8981        // Remove data of the replaced element. We cannot just call .remove()
8982        // on the element it since that would deallocate scope that is needed
8983        // for the new node. Instead, remove the data "manually".
8984        if (!jQuery) {
8985          delete jqLite.cache[firstElementToRemove[jqLite.expando]];
8986        } else {
8987          // jQuery 2.x doesn't expose the data storage. Use jQuery.cleanData to clean up after
8988          // the replaced element. The cleanData version monkey-patched by Angular would cause
8989          // the scope to be trashed and we do need the very same scope to work with the new
8990          // element. However, we cannot just cache the non-patched version and use it here as
8991          // that would break if another library patches the method after Angular does (one
8992          // example is jQuery UI). Instead, set a flag indicating scope destroying should be
8993          // skipped this one time.
8994          skipDestroyOnNextJQueryCleanData = true;
8995          jQuery.cleanData([firstElementToRemove]);
8996        }
8997      }
8998
8999      for (var k = 1, kk = elementsToRemove.length; k < kk; k++) {
9000        var element = elementsToRemove[k];
9001        jqLite(element).remove(); // must do this way to clean up expando
9002        fragment.appendChild(element);
9003        delete elementsToRemove[k];
9004      }
9005
9006      elementsToRemove[0] = newNode;
9007      elementsToRemove.length = 1;
9008    }
9009
9010
9011    function cloneAndAnnotateFn(fn, annotation) {
9012      return extend(function() { return fn.apply(null, arguments); }, fn, annotation);
9013    }
9014
9015
9016    function invokeLinkFn(linkFn, scope, $element, attrs, controllers, transcludeFn) {
9017      try {
9018        linkFn(scope, $element, attrs, controllers, transcludeFn);
9019      } catch (e) {
9020        $exceptionHandler(e, startingTag($element));
9021      }
9022    }
9023
9024
9025    // Set up $watches for isolate scope and controller bindings. This process
9026    // only occurs for isolate scopes and new scopes with controllerAs.
9027    function initializeDirectiveBindings(scope, attrs, destination, bindings,
9028                                         directive, newScope) {
9029      var onNewScopeDestroyed;
9030      forEach(bindings, function(definition, scopeName) {
9031        var attrName = definition.attrName,
9032        optional = definition.optional,
9033        mode = definition.mode, // @, =, or &
9034        lastValue,
9035        parentGet, parentSet, compare;
9036
9037        switch (mode) {
9038
9039          case '@':
9040            if (!optional && !hasOwnProperty.call(attrs, attrName)) {
9041              destination[scopeName] = attrs[attrName] = void 0;
9042            }
9043            attrs.$observe(attrName, function(value) {
9044              if (isString(value)) {
9045                destination[scopeName] = value;
9046              }
9047            });
9048            attrs.$$observers[attrName].$$scope = scope;
9049            if (isString(attrs[attrName])) {
9050              // If the attribute has been provided then we trigger an interpolation to ensure
9051              // the value is there for use in the link fn
9052              destination[scopeName] = $interpolate(attrs[attrName])(scope);
9053            }
9054            break;
9055
9056          case '=':
9057            if (!hasOwnProperty.call(attrs, attrName)) {
9058              if (optional) break;
9059              attrs[attrName] = void 0;
9060            }
9061            if (optional && !attrs[attrName]) break;
9062
9063            parentGet = $parse(attrs[attrName]);
9064            if (parentGet.literal) {
9065              compare = equals;
9066            } else {
9067              compare = function(a, b) { return a === b || (a !== a && b !== b); };
9068            }
9069            parentSet = parentGet.assign || function() {
9070              // reset the change, or we will throw this exception on every $digest
9071              lastValue = destination[scopeName] = parentGet(scope);
9072              throw $compileMinErr('nonassign',
9073                  "Expression '{0}' used with directive '{1}' is non-assignable!",
9074                  attrs[attrName], directive.name);
9075            };
9076            lastValue = destination[scopeName] = parentGet(scope);
9077            var parentValueWatch = function parentValueWatch(parentValue) {
9078              if (!compare(parentValue, destination[scopeName])) {
9079                // we are out of sync and need to copy
9080                if (!compare(parentValue, lastValue)) {
9081                  // parent changed and it has precedence
9082                  destination[scopeName] = parentValue;
9083                } else {
9084                  // if the parent can be assigned then do so
9085                  parentSet(scope, parentValue = destination[scopeName]);
9086                }
9087              }
9088              return lastValue = parentValue;
9089            };
9090            parentValueWatch.$stateful = true;
9091            var unwatch;
9092            if (definition.collection) {
9093              unwatch = scope.$watchCollection(attrs[attrName], parentValueWatch);
9094            } else {
9095              unwatch = scope.$watch($parse(attrs[attrName], parentValueWatch), null, parentGet.literal);
9096            }
9097            onNewScopeDestroyed = (onNewScopeDestroyed || []);
9098            onNewScopeDestroyed.push(unwatch);
9099            break;
9100
9101          case '&':
9102            // Don't assign Object.prototype method to scope
9103            parentGet = attrs.hasOwnProperty(attrName) ? $parse(attrs[attrName]) : noop;
9104
9105            // Don't assign noop to destination if expression is not val
9105id
9106            if (parentGet === noop && optional) break;
9107
9108            destination[scopeName] = function(locals) {
9109              return parentGet(scope, locals);
9110            };
9111            break;
9112        }
9113      });
9114      var destroyBindings = onNewScopeDestroyed ? function destroyBindings() {
9115        for (var i = 0, ii = onNewScopeDestroyed.length; i < ii; ++i) {
9116          onNewScopeDestroyed[i]();
9117        }
9118      } : noop;
9119      if (newScope && destroyBindings !== noop) {
9120        newScope.$on('$destroy', destroyBindings);
9121        return noop;
9122      }
9123      return destroyBindings;
9124    }
9125  }];
9126}
9127
9128var PREFIX_REGEXP = /^((?:x|data)[\:\-_])/i;
9129/**
9130 * Converts all accepted directives format into proper directive name.
9131 * @param name Name to normalize
9132 */
9133function directiveNormalize(name) {
9134  return camelCase(name.replace(PREFIX_REGEXP, ''));
9135}
9136
9137/**
9138 * @ngdoc type
9139 * @name $compile.directive.Attributes
9140 *
9141 * @description
9142 * A shared object between directive compile / linking functions which contains normalized DOM
9143 * element attributes. The values reflect current binding state `{{ }}`. The normalization is
9144 * needed since all of these are treated as equivalent in Angular:
9145 *
9146 * ```
9147 *    <span ng:bind="a" ng-bind="a" data-ng-bind="a" x-ng-bind="a">
9148 * ```
9149 */
9150
9151/**
9152 * @ngdoc property
9153 * @name $compile.directive.Attributes#$attr
9154 *
9155 * @description
9156 * A map of DOM element attribute names to the normalized name. This is
9157 * needed to do reverse lookup from normalized name back to actual name.
9158 */
9159
9160
9161/**
9162 * @ngdoc method
9163 * @name $compile.directive.Attributes#$set
9164 * @kind function
9165 *
9166 * @description
9167 * Set DOM element attribute value.
9168 *
9169 *
9170 * @param {string} name Normalized element attribute name of the property to modify. The name is
9171 *          reverse-translated using the {@link ng.$compile.directive.Attributes#$attr $attr}
9172 *          property to the original name.
9173 * @param {string} value Value to set the attribute to. The value can be an interpolated string.
9174 */
9175
9176
9177
9178/**
9179 * Closure compiler type information
9180 */
9181
9182function nodesetLinkingFn(
9183  /* angular.Scope */ scope,
9184  /* NodeList */ nodeList,
9185  /* Element */ rootElement,
9186  /* function(Function) */ boundTranscludeFn
9187) {}
9188
9189function directiveLinkingFn(
9190  /* nodesetLinkingFn */ nodesetLinkingFn,
9191  /* angular.Scope */ scope,
9192  /* Node */ node,
9193  /* Element */ rootElement,
9194  /* function(Function) */ boundTranscludeFn
9195) {}
9196
9197function tokenDifference(str1, str2) {
9198  var values = '',
9199      tokens1 = str1.split(/\s+/),
9200      tokens2 = str2.split(/\s+/);
9201
9202  outer:
9203  for (var i = 0; i < tokens1.length; i++) {
9204    var token = tokens1[i];
9205    for (var j = 0; j < tokens2.length; j++) {
9206      if (token == tokens2[j]) continue outer;
9207    }
9208    values += (values.length > 0 ? ' ' : '') + token;
9209  }
9210  return values;
9211}
9212
9213function removeComments(jqNodes) {
9214  jqNodes = jqLite(jqNodes);
9215  var i = jqNodes.length;
9216
9217  if (i <= 1) {
9218    return jqNodes;
9219  }
9220
9221  while (i--) {
9222    var node = jqNodes[i];
9223    if (node.nodeType === NODE_TYPE_COMMENT) {
9224      splice.call(jqNodes, i, 1);
9225    }
9226  }
9227  return jqNodes;
9228}
9229
9230var $controllerMinErr = minErr('$controller');
9231
9232
9233var CNTRL_REG = /^(\S+)(\s+as\s+(\w+))?$/;
9234function identifierForController(controller, ident) {
9235  if (ident && isString(ident)) return ident;
9236  if (isString(controller)) {
9237    var match = CNTRL_REG.exec(controller);
9238    if (match) return match[3];
9239  }
9240}
9241
9242
9243/**
9244 * @ngdoc provider
9245 * @name $controllerProvider
9246 * @description
9247 * The {@link ng.$controller $controller service} is used by Angular to create new
9248 * controllers.
9249 *
9250 * This provider allows controller registration via the
9251 * {@link ng.$controllerProvider#register register} method.
9252 */
9253function $ControllerProvider() {
9254  var controllers = {},
9255      globals = false;
9256
9257  /**
9258   * @ngdoc method
9259   * @name $controllerProvider#register
9260   * @param {string|Object} name Controller name, or an object map of controllers where the keys are
9261   *    the names and the values are the constructors.
9262   * @param {Function|Array} constructor Controller constructor fn (optionally decorated with DI
9263   *    annotations in the array notation).
9264   */
9265  this.register = function(name, constructor) {
9266    assertNotHasOwnProperty(name, 'controller');
9267    if (isObject(name)) {
9268      extend(controllers, name);
9269    } else {
9270      controllers[name] = constructor;
9271    }
9272  };
9273
9274  /**
9275   * @ngdoc method
9276   * @name $controllerProvider#allowGlobals
9277   * @description If called, allows `$controller` to find controller constructors on `window`
9278   */
9279  this.allowGlobals = function() {
9280    globals = true;
9281  };
9282
9283
9284  this.$get = ['$injector', '$window', function($injector, $window) {
9285
9286    /**
9287     * @ngdoc service
9288     * @name $controller
9289     * @requires $injector
9290     *
9291     * @param {Function|string} constructor If called with a function then it's considered to be the
9292     *    controller constructor function. Otherwise it's considered to be a string which is used
9293     *    to retrieve the controller constructor using the following steps:
9294     *
9295     *    * check if a controller with given name is registered via `$controllerProvider`
9296     *    * check if evaluating the string on the current scope returns a constructor
9297     *    * if $controllerProvider#allowGlobals, check `window[constructor]` on the global
9298     *      `window` object (not recommended)
9299     *
9300     *    The string can use the `controller as property` syntax, where the controller instance is published
9301     *    as the specified property on the `scope`; the `scope` must be injected into `locals` param for this
9302     *    to work correctly.
9303     *
9304     * @param {Object} locals Injection locals for Controller.
9305     * @return {Object} Instance of given controller.
9306     *
vendor: 4,024 bytes, lines 9307-9411
9307     * @description
9308     * `$controller` service is responsible for instantiating controllers.
9309     *
9310     * It's just a simple call to {@link auto.$injector $injector}, but extracted into
9311     * a service, so that one can override this service with [BC version](https://gist.github.com/1649788).
9312     */
9313    return function(expression, locals, later, ident) {
9314      // PRIVATE API:
9315      //   param `later` --- indicates that the controller's constructor is invoked at a later time.
9316      //                     If true, $controller will allocate the object with the correct
9317      //                     prototype chain, but will not invoke the controller until a returned
9318      //                     callback is invoked.
9319      //   param `ident` --- An optional label which overrides the label parsed from the controller
9320      //                     expression, if any.
9321      var instance, match, constructor, identifier;
9322      later = later === true;
9323      if (ident && isString(ident)) {
9324        identifier = ident;
9325      }
9326
9327      if (isString(expression)) {
9328        match = expression.match(CNTRL_REG);
9329        if (!match) {
9330          throw $controllerMinErr('ctrlfmt',
9331            "Badly formed controller string '{0}'. " +
9332            "Must match `__name__ as __id__` or `__name__`.", expression);
9333        }
9334        constructor = match[1],
9335        identifier = identifier || match[3];
9336        expression = controllers.hasOwnProperty(constructor)
9337            ? controllers[constructor]
9338            : getter(locals.$scope, constructor, true) ||
9339                (globals ? getter($window, constructor, true) : undefined);
9340
9341        assertArgFn(expression, constructor, true);
9342      }
9343
9344      if (later) {
9345        // Instantiate controller later:
9346        // This machinery is used to create an instance of the object before calling the
9347        // controller's constructor itself.
9348        //
9349        // This allows properties to be added to the controller before the constructor is
9350        // invoked. Primarily, this is used for isolate scope bindings in $compile.
9351        //
9352        // This feature is not intended for use by applications, and is thus not documented
9353        // publicly.
9354        // Object creation: http://jsperf.com/create-constructor/2
9355        var controllerPrototype = (isArray(expression) ?
9356          expression[expression.length - 1] : expression).prototype;
9357        instance = Object.create(controllerPrototype || null);
9358
9359        if (identifier) {
9360          addIdentifier(locals, identifier, instance, constructor || expression.name);
9361        }
9362
9363        var instantiate;
9364        return instantiate = extend(function() {
9365          var result = $injector.invoke(expression, instance, locals, constructor);
9366          if (result !== instance && (isObject(result) || isFunction(result))) {
9367            instance = result;
9368            if (identifier) {
9369              // If result changed, re-assign controllerAs value to scope.
9370              addIdentifier(locals, identifier, instance, constructor || expression.name);
9371            }
9372          }
9373          return instance;
9374        }, {
9375          instance: instance,
9376          identifier: identifier
9377        });
9378      }
9379
9380      instance = $injector.instantiate(expression, locals, constructor);
9381
9382      if (identifier) {
9383        addIdentifier(locals, identifier, instance, constructor || expression.name);
9384      }
9385
9386      return instance;
9387    };
9388
9389    function addIdentifier(locals, identifier, instance, name) {
9390      if (!(locals && isObject(locals.$scope))) {
9391        throw minErr('$controller')('noscp',
9392          "Cannot export controller '{0}' as '{1}'! No $scope object provided via `locals`.",
9393          name, identifier);
9394      }
9395
9396      locals.$scope[identifier] = instance;
9397    }
9398  }];
9399}
9400
9401/**
9402 * @ngdoc service
9403 * @name $document
9404 * @requires $window
9405 *
9406 * @description
9407 * A {@link angular.element jQuery or jqLite} wrapper for the browser's `window.document` object.
9408 *
9409 * @example
9410   <example module="documentExample">
9411     <file name="index.html">
9412       <div ng-controller="ExampleController">
9413         <p>$document title: <b ng-bind="title"></b></p>
9414         <p>window.document title: <b ng-bind="windowTitle"></b></p>
9415       </div>
9416     </file>
9417     <file name="script.js">
9418       angular.module('documentExample', [])
9419         .controller('ExampleController', ['$scope', '$document', function($scope, $document) {
9420           $scope.title = $document[0].title;
9421           $scope.windowTitle = angular.element(window.document)[0].title;
9422         }]);
9423     </file>
9424   </example>
9425 */
9426function $DocumentProvider() {
9427  this.$get = ['$window', function(window) {
9428    return jqLite(window.document);
9429  }];
9430}
9431
9432/**
9433 * @ngdoc service
9434 * @name $exceptionHandler
9435 * @requires ng.$log
9436 *
9437 * @description
9438 * Any uncaught exception in angular expressions is delegated to this service.
9439 * The default implementation simply delegates to `$log.error` which logs it into
9440 * the browser console.
9441 *
9442 * In unit tests, if `angular-mocks.js` is loaded, this service is overridden by
9443 * {@link ngMock.$exceptionHandler mock $exceptionHandler} which aids in testing.
9444 *
9445 * ## Example:
9446 *
9447 * ```js
9448 *   angular.module('exceptionOverride', []).factory('$exceptionHandler', function() {
9449 *     return function(exception, cause) {
9450 *       exception.message += ' (caused by "' + cause + '")';
9451 *       throw exception;
9452 *     };
9453 *   });
9454 * ```
9455 *
9456 * This example will override the normal action of `$exceptionHandler`, to make angular
9457 * exceptions fail hard when they happen, instead of just logging to the console.
9458 *
9459 * <hr />
9460 * Note, that code executed in event-listeners (even those registered using jqLite's `on`/`bind`
9461 * methods) does not delegate exceptions to the {@link ng.$exceptionHandler $exceptionHandler}
9462 * (unless executed during a digest).
9463 *
9464 * If you wish, you can manually delegate exceptions, e.g.
9465 * `try { ... } catch(e) { $exceptionHandler(e); }`
9466 *
9467 * @param {Error} exception Exception associated with the error.
9468 * @param {string=} cause optional information about the context in which
9469 *       the error was thrown.
9470 *
9471 */
9472function $ExceptionHandlerProvider() {
9473  this.$get = ['$log', function($log) {
9474    return function(exception, cause) {
9475      $log.error.apply($log, arguments);
9476    };
9477  }];
9478}
9479
9480var $$ForceReflowProvider = function() {
9481  this.$get = ['$document', function($document) {
9482    return function(domNode) {
9483      //the line below will force the browser to perform a repaint so
9484      //that all the animated elements within the animation frame will
9485      //be properly updated and drawn on screen. This is required to
9486      //ensure that the preparation animation is properly flushed so that
9487      //the active state picks up from there. DO NOT REMOVE THIS LINE.
9488      //DO NOT OPTIMIZE THIS LINE. THE MINIFIER WILL REMOVE IT OTHERWISE WHICH
9489      //WILL RESULT IN AN UNPREDICTABLE BUG THAT IS VERY HARD TO TRACK DOWN AND
9490      //WILL TAKE YEARS AWAY FROM YOUR LIFE.
9491      if (domNode) {
9492        if (!domNode.nodeType && domNode instanceof jqLite) {
9493          domNode = domNode[0];
9494        }
9495      } else {
9496        domNode = $document[0].body;
9497      }
9498      return domNode.offsetWidth + 1;
9499    };
9500  }];
9501};
9502
9503var APPLICATION_JSON = 'application/json';
9504var CONTENT_TYPE_APPLICATION_JSON = {'Content-Type': APPLICATION_JSON + ';charset=utf-8'};
9505var JSON_START = /^\[|^\{(?!\{)/;
9506var JSON_ENDS = {
9507  '[': /]$/,
9508  '{': /}$/
9509};
9510var JSON_PROTECTION_PREFIX = /^\)\]\}',?\n/;
9511var $httpMinErr = minErr('$http');
9512var $httpMinErrLegacyFn = function(method) {
9513  return function() {
9514    throw $httpMinErr('legacy', 'The method `{0}` on the promise returned from `$http` has been disabled.', method);
9515  };
9516};
9517
9518function serializeValue(v) {
9519  if (isObject(v)) {
9520    return isDate(v) ? v.toISOString() : toJson(v);
9521  }
9522  return v;
9523}
9524
9525
9526function $HttpParamSerializerProvider() {
9527  /**
9528   * @ngdoc service
9529   * @name $httpParamSerializer
9530   * @description
9531   *
9532   * Default {@link $http `$http`} params serializer that converts objects to strings
9533   * according to the following rules:
9534   *
9535   * * `{'foo': 'bar'}` results in `foo=bar`
9536   * * `{'foo': Date.now()}` results in `foo=2015-04-01T09%3A50%3A49.262Z` (`toISOString()` and encoded representation of a Date object)
9537   * * `{'foo': ['bar', 'baz']}` results in `foo=bar&foo=baz` (repeated key for each array element)
9538   * * `{'foo': {'bar':'baz'}}` results in `foo=%7B%22bar%22%3A%22baz%22%7D"` (stringified and encoded representation of an object)
9539   *
9540   * Note that serializer will sort the request parameters alphabetically.
9541   * */
9542
9543  this.$get = function() {
9544    return function ngParamSerializer(params) {
9545      if (!params) return '';
9546      var parts = [];
vendor: 3,682 bytes, lines 9547-9673
9547      forEachSorted(params, function(value, key) {
9548        if (value === null || isUndefined(value)) return;
9549        if (isArray(value)) {
9550          forEach(value, function(v, k) {
9551            parts.push(encodeUriQuery(key)  + '=' + encodeUriQuery(serializeValue(v)));
9552          });
9553        } else {
9554          parts.push(encodeUriQuery(key) + '=' + encodeUriQuery(serializeValue(value)));
9555        }
9556      });
9557
9558      return parts.join('&');
9559    };
9560  };
9561}
9562
9563function $HttpParamSerializerJQLikeProvider() {
9564  /**
9565   * @ngdoc service
9566   * @name $httpParamSerializerJQLike
9567   * @description
9568   *
9569   * Alternative {@link $http `$http`} params serializer that follows
9570   * jQuery's [`param()`](http://api.jquery.com/jquery.param/) method logic.
9571   * The serializer will also sort the params alphabetically.
9572   *
9573   * To use it for serializing `$http` request parameters, set it as the `paramSerializer` property:
9574   *
9575   * ```js
9576   * $http({
9577   *   url: myUrl,
9578   *   method: 'GET',
9579   *   params: myParams,
9580   *   paramSerializer: '$httpParamSerializerJQLike'
9581   * });
9582   * ```
9583   *
9584   * It is also possible to set it as the default `paramSerializer` in the
9585   * {@link $httpProvider#defaults `$httpProvider`}.
9586   *
9587   * Additionally, you can inject the serializer and use it explicitly, for example to serialize
9588   * form data for submission:
9589   *
9590   * ```js
9591   * .controller(function($http, $httpParamSerializerJQLike) {
9592   *   //...
9593   *
9594   *   $http({
9595   *     url: myUrl,
9596   *     method: 'POST',
9597   *     data: $httpParamSerializerJQLike(myData),
9598   *     headers: {
9599   *       'Content-Type': 'application/x-www-form-urlencoded'
9600   *     }
9601   *   });
9602   *
9603   * });
9604   * ```
9605   *
9606   * */
9607  this.$get = function() {
9608    return function jQueryLikeParamSerializer(params) {
9609      if (!params) return '';
9610      var parts = [];
9611      serialize(params, '', true);
9612      return parts.join('&');
9613
9614      function serialize(toSerialize, prefix, topLevel) {
9615        if (toSerialize === null || isUndefined(toSerialize)) return;
9616        if (isArray(toSerialize)) {
9617          forEach(toSerialize, function(value, index) {
9618            serialize(value, prefix + '[' + (isObject(value) ? index : '') + ']');
9619          });
9620        } else if (isObject(toSerialize) && !isDate(toSerialize)) {
9621          forEachSorted(toSerialize, function(value, key) {
9622            serialize(value, prefix +
9623                (topLevel ? '' : '[') +
9624                key +
9625                (topLevel ? '' : ']'));
9626          });
9627        } else {
9628          parts.push(encodeUriQuery(prefix) + '=' + encodeUriQuery(serializeValue(toSerialize)));
9629        }
9630      }
9631    };
9632  };
9633}
9634
9635function defaultHttpResponseTransform(data, headers) {
9636  if (isString(data)) {
9637    // Strip json vulnerability protection prefix and trim whitespace
9638    var tempData = data.replace(JSON_PROTECTION_PREFIX, '').trim();
9639
9640    if (tempData) {
9641      var contentType = headers('Content-Type');
9642      if ((contentType && (contentType.indexOf(APPLICATION_JSON) === 0)) || isJsonLike(tempData)) {
9643        data = fromJson(tempData);
9644      }
9645    }
9646  }
9647
9648  return data;
9649}
9650
9651function isJsonLike(str) {
9652    var jsonStart = str.match(JSON_START);
9653    return jsonStart && JSON_ENDS[jsonStart[0]].test(str);
9654}
9655
9656/**
9657 * Parse headers into key value object
9658 *
9659 * @param {string} headers Raw headers as a string
9660 * @returns {Object} Parsed headers as key value object
9661 */
9662function parseHeaders(headers) {
9663  var parsed = createMap(), i;
9664
9665  function fillInParsed(key, val) {
9666    if (key) {
9667      parsed[key] = parsed[key] ? parsed[key] + ', ' + val : val;
9668    }
9669  }
9670
9671  if (isString(headers)) {
9672    forEach(headers.split('\n'), function(line) {
9673      i = line.indexOf(':');
vendor: 4,257 bytes, lines 9674-9811
9674      fillInParsed(lowercase(trim(line.substr(0, i))), trim(line.substr(i + 1)));
9675    });
9676  } else if (isObject(headers)) {
9677    forEach(headers, function(headerVal, headerKey) {
9678      fillInParsed(lowercase(headerKey), trim(headerVal));
9679    });
9680  }
9681
9682  return parsed;
9683}
9684
9685
9686/**
9687 * Returns a function that provides access to parsed headers.
9688 *
9689 * Headers are lazy parsed when first requested.
9690 * @see parseHeaders
9691 *
9692 * @param {(string|Object)} headers Headers to provide access to.
9693 * @returns {function(string=)} Returns a getter function which if called with:
9694 *
9695 *   - if called with single an argument returns a single header value or null
9696 *   - if called with no arguments returns an object containing all headers.
9697 */
9698function headersGetter(headers) {
9699  var headersObj;
9700
9701  return function(name) {
9702    if (!headersObj) headersObj =  parseHeaders(headers);
9703
9704    if (name) {
9705      var value = headersObj[lowercase(name)];
9706      if (value === void 0) {
9707        value = null;
9708      }
9709      return value;
9710    }
9711
9712    return headersObj;
9713  };
9714}
9715
9716
9717/**
9718 * Chain all given functions
9719 *
9720 * This function is used for both request and response transforming
9721 *
9722 * @param {*} data Data to transform.
9723 * @param {function(string=)} headers HTTP headers getter fn.
9724 * @param {number} status HTTP status code of the response.
9725 * @param {(Function|Array.<Function>)} fns Function or an array of functions.
9726 * @returns {*} Transformed data.
9727 */
9728function transformData(data, headers, status, fns) {
9729  if (isFunction(fns)) {
9730    return fns(data, headers, status);
9731  }
9732
9733  forEach(fns, function(fn) {
9734    data = fn(data, headers, status);
9735  });
9736
9737  return data;
9738}
9739
9740
9741function isSuccess(status) {
9742  return 200 <= status && status < 300;
9743}
9744
9745
9746/**
9747 * @ngdoc provider
9748 * @name $httpProvider
9749 * @description
9750 * Use `$httpProvider` to change the default behavior of the {@link ng.$http $http} service.
9751 * */
9752function $HttpProvider() {
9753  /**
9754   * @ngdoc property
9755   * @name $httpProvider#defaults
9756   * @description
9757   *
9758   * Object containing default values for all {@link ng.$http $http} requests.
9759   *
9760   * - **`defaults.cache`** - {Object} - an object built with {@link ng.$cacheFactory `$cacheFactory`}
9761   * that will provide the cache for all requests who set their `cache` property to `true`.
9762   * If you set the `defaults.cache = false` then only requests that specify their own custom
9763   * cache object will be cached. See {@link $http#caching $http Caching} for more information.
9764   *
9765   * - **`defaults.xsrfCookieName`** - {string} - Name of cookie containing the XSRF token.
9766   * Defaults value is `'XSRF-TOKEN'`.
9767   *
9768   * - **`defaults.xsrfHeaderName`** - {string} - Name of HTTP header to populate with the
9769   * XSRF token. Defaults value is `'X-XSRF-TOKEN'`.
9770   *
9771   * - **`defaults.headers`** - {Object} - Default headers for all $http requests.
9772   * Refer to {@link ng.$http#setting-http-headers $http} for documentation on
9773   * setting default headers.
9774   *     - **`defaults.headers.common`**
9775   *     - **`defaults.headers.post`**
9776   *     - **`defaults.headers.put`**
9777   *     - **`defaults.headers.patch`**
9778   *
9779   *
9780   * - **`defaults.paramSerializer`** - `{string|function(Object<string,string>):string}` - A function
9781   *  used to the prepare string representation of request parameters (specified as an object).
9782   *  If specified as string, it is interpreted as a function registered with the {@link auto.$injector $injector}.
9783   *  Defaults to {@link ng.$httpParamSerializer $httpParamSerializer}.
9784   *
9785   **/
9786  var defaults = this.defaults = {
9787    // transform incoming response data
9788    transformResponse: [defaultHttpResponseTransform],
9789
9790    // transform outgoing request data
9791    transformRequest: [function(d) {
9792      return isObject(d) && !isFile(d) && !isBlob(d) && !isFormData(d) ? toJson(d) : d;
9793    }],
9794
9795    // default headers
9796    headers: {
9797      common: {
9798        'Accept': 'application/json, text/plain, */*'
9799      },
9800      post:   shallowCopy(CONTENT_TYPE_APPLICATION_JSON),
9801      put:    shallowCopy(CONTENT_TYPE_APPLICATION_JSON),
9802      patch:  shallowCopy(CONTENT_TYPE_APPLICATION_JSON)
9803    },
9804
9805    xsrfCookieName: 'XSRF-TOKEN',
9806    xsrfHeaderName: 'X-XSRF-TOKEN',
9807
9808    paramSerializer: '$httpParamSerializer'
9809  };
9810
9811  var useApplyAsync = false;
vendor: 4,300 bytes, lines 9812-9922
9812  /**
9813   * @ngdoc method
9814   * @name $httpProvider#useApplyAsync
9815   * @description
9816   *
9817   * Configure $http service to combine processing of multiple http responses received at around
9818   * the same time via {@link ng.$rootScope.Scope#$applyAsync $rootScope.$applyAsync}. This can result in
9819   * significant performance improvement for bigger applications that make many HTTP requests
9820   * concurrently (common during application bootstrap).
9821   *
9822   * Defaults to false. If no value is specified, returns the current configured value.
9823   *
9824   * @param {boolean=} value If true, when requests are loaded, they will schedule a deferred
9825   *    "apply" on the next tick, giving time for subsequent requests in a roughly ~10ms window
9826   *    to load and share the same digest cycle.
9827   *
9828   * @returns {boolean|Object} If a value is specified, returns the $httpProvider for chaining.
9829   *    otherwise, returns the current configured value.
9830   **/
9831  this.useApplyAsync = function(value) {
9832    if (isDefined(value)) {
9833      useApplyAsync = !!value;
9834      return this;
9835    }
9836    return useApplyAsync;
9837  };
9838
9839  var useLegacyPromise = true;
9840  /**
9841   * @ngdoc method
9842   * @name $httpProvider#useLegacyPromiseExtensions
9843   * @description
9844   *
9845   * Configure `$http` service to return promises without the shorthand methods `success` and `error`.
9846   * This should be used to make sure that applications work without these methods.
9847   *
9848   * Defaults to false. If no value is specified, returns the current configured value.
9849   *
9850   * @param {boolean=} value If true, `$http` will return a normal promise without the `success` and `error` methods.
9851   *
9852   * @returns {boolean|Object} If a value is specified, returns the $httpProvider for chaining.
9853   *    otherwise, returns the current configured value.
9854   **/
9855  this.useLegacyPromiseExtensions = function(value) {
9856    if (isDefined(value)) {
9857      useLegacyPromise = !!value;
9858      return this;
9859    }
9860    return useLegacyPromise;
9861  };
9862
9863  /**
9864   * @ngdoc property
9865   * @name $httpProvider#interceptors
9866   * @description
9867   *
9868   * Array containing service factories for all synchronous or asynchronous {@link ng.$http $http}
9869   * pre-processing of request or postprocessing of responses.
9870   *
9871   * These service factories are ordered by request, i.e. they are applied in the same order as the
9872   * array, on request, but reverse order, on response.
9873   *
9874   * {@link ng.$http#interceptors Interceptors detailed info}
9875   **/
9876  var interceptorFactories = this.interceptors = [];
9877
9878  this.$get = ['$httpBackend', '$$cookieReader', '$cacheFactory', '$rootScope', '$q', '$injector',
9879      function($httpBackend, $$cookieReader, $cacheFactory, $rootScope, $q, $injector) {
9880
9881    var defaultCache = $cacheFactory('$http');
9882
9883    /**
9884     * Make sure that default param serializer is exposed as a function
9885     */
9886    defaults.paramSerializer = isString(defaults.paramSerializer) ?
9887      $injector.get(defaults.paramSerializer) : defaults.paramSerializer;
9888
9889    /**
9890     * Interceptors stored in reverse order. Inner interceptors before outer interceptors.
9891     * The reversal is needed so that we can build up the interception chain around the
9892     * server request.
9893     */
9894    var reversedInterceptors = [];
9895
9896    forEach(interceptorFactories, function(interceptorFactory) {
9897      reversedInterceptors.unshift(isString(interceptorFactory)
9898          ? $injector.get(interceptorFactory) : $injector.invoke(interceptorFactory));
9899    });
9900
9901    /**
9902     * @ngdoc service
9903     * @kind function
9904     * @name $http
9905     * @requires ng.$httpBackend
9906     * @requires $cacheFactory
9907     * @requires $rootScope
9908     * @requires $q
9909     * @requires $injector
9910     *
9911     * @description
9912     * The `$http` service is a core Angular service that facilitates communication with the remote
9913     * HTTP servers via the browser's [XMLHttpRequest](https://developer.mozilla.org/en/xmlhttprequest)
9914     * object or via [JSONP](http://en.wikipedia.org/wiki/JSONP).
9915     *
9916     * For unit testing applications that use `$http` service, see
9917     * {@link ngMock.$httpBackend $httpBackend mock}.
9918     *
9919     * For a higher level of abstraction, please check out the {@link ngResource.$resource
9920     * $resource} service.
9921     *
9922     * The $http API is based on the {@link ng.$q deferred/promise APIs}
9922 exposed by
9923     * the $q service. While for simple usage patterns this doesn't matter much, for advanced usage
9924     * it is important to familiarize yourself with these APIs and the guarantees they provide.
9925     *
9926     *
9927     * ## General usage
9928     * The `$http` service is a function which takes a single argument — a {@link $http#usage configuration object} —
9929     * that is used to generate an HTTP request and returns  a {@link ng.$q promise}.
9930     *
9931     * ```js
9932     *   // Simple GET request example:
9933     *   $http({
9934     *     method: 'GET',
9935     *     url: '/someUrl'
9936     *   }).then(function successCallback(response) {
9937     *       // this callback will be called asynchronously
9938     *       // when the response is available
9939     *     }, function errorCallback(response) {
9940     *       // called asynchronously if an error occurs
9941     *       // or server returns response with an error status.
9942     *     });
9943     * ```
9944     *
9945     * The response object has these properties:
9946     *
9947     *   - **data** – `{string|Object}` – The response body transformed with the transform
9948     *     functions.
9949     *   - **status** – `{number}` – HTTP status code of the response.
9950     *   - **headers** – `{function([headerName])}` – Header getter function.
9951     *   - **config** – `{Object}` – The configuration object that was used to generate the request.
9952     *   - **statusText** – `{string}` – HTTP status text of the response.
9953     *
9954     * A response status code between 200 and 299 is considered a success status and
9955     * will result in the success callback being called. Note that if the response is a redirect,
9956     * XMLHttpRequest will transparently follow it, meaning that the error callback will not be
9957     * called for such responses.
9958     *
9959     *
9960     * ## Shortcut methods
9961     *
9962     * Shortcut methods are also available. All shortcut methods require passing in the URL, and
9963     * request data must be passed in for POST/PUT requests. An optional config can be passed as the
9964     * last argument.
9965     *
9966     * ```js
9967     *   $http.get('/someUrl', config).then(successCallback, errorCallback);
9968     *   $http.post('/someUrl', data, config).then(successCallback, errorCallback);
9969     * ```
9970     *
9971     * Complete list of shortcut methods:
9972     *
9973     * - {@link ng.$http#get $http.get}
9974     * - {@link ng.$http#head $http.head}
9975     * - {@link ng.$http#post $http.post}
9976     * - {@link ng.$http#put $http.put}
9977     * - {@link ng.$http#delete $http.delete}
9978     * - {@link ng.$http#jsonp $http.jsonp}
9979     * - {@link ng.$http#patch $http.patch}
9980     *
9981     *
9982     * ## Writing Unit Tests that use $http
9983     * When unit testing (using {@link ngMock ngMock}), it is necessary to call
9984     * {@link ngMock.$httpBackend#flush $httpBackend.flush()} to flush each pending
9985     * request using trained responses.
9986     *
9987     * ```
9988     * $httpBackend.expectGET(...);
9989     * $http.get(...);
9990     * $httpBackend.flush();
9991     * ```
9992     *
9993     * ## Deprecation Notice
9994     * <div class="alert alert-danger">
9995     *   The `$http` legacy promise methods `success` and `error` have been deprecated.
9996     *   Use the standard `then` method instead.
9997     *   If {@link $httpProvider#useLegacyPromiseExtensions `$httpProvider.useLegacyPromiseExtensions`} is set to
9998     *   `false` then these methods will throw {@link $http:legacy `$http/legacy`} error.
9999     * </div>
10000     *
10001     * ## Setting HTTP Headers
10002     *
10003     * The $http service will automatically add certain HTTP headers to all requests. These defaults
10004     * can be fully configured by accessing the `$httpProvider.defaults.headers` configuration
10005     * object, which currently contains this default configuration:
10006     *
10007     * - `$httpProvider.defaults.headers.common` (headers that are common for all requests):
10008     *   - `Accept: application/json, text/plain, * / *`
10009     * - `$httpProvider.defaults.headers.post`: (header defaults for POST requests)
10010     *   - `Content-Type: application/json`
10011     * - `$httpProvider.defaults.headers.put` (header defaults for PUT requests)
10012     *   - `Content-Type: application/json`
10013     *
10014     * To add or overwrite these defaults, simply add or remove a property from these configuration
10015     * objects. To add headers for an HTTP method other than POST or PUT, simply add a new object
10016     * with the lowercased HTTP method name as the key, e.g.
10017     * `$httpProvider.defaults.headers.get = { 'My-Header' : 'value' }`.
10018     *
10019     * The defaults can also be set at runtime via the `$http.defaults` object in the same
10020     * fashion. For example:
10021     *
10022     * ```
10023     * module.run(function($http) {
10024     *   $http.defaults.headers.common.Authorization = 'Basic YmVlcDpib29w'
10025     * });
10026     * ```
10027     *
10028     * In addition, you can supply a `headers` property in the config object passed when
10029     * calling `$http(config)`, which overrides the defaults without changing them globally.
10030     *
10031     * To explicitly remove a header automatically added via $httpProvider.defaults.headers on a per request basis,
10032     * Use the `headers` property, setting the desired header to `undefined`. For example:
10033     *
10034     * ```js
10035     * var req = {
10036     *  method: 'POST',
10037     *  url: 'http://example.com',
10038     *  headers: {
10039     *    'Content-Type': undefined
10040     *  },
10041     *  data: { test: 'test' }
10042     * }
10043     *
10044     * $http(req).then(function(){...}, function(){...});
10045     * ```
10046     *
10047     * ## Transforming Requests and Responses
10048     *
10049     * Both requests and responses can be transformed using transformation functions: `transformRequest`
10050     * and `transformResponse`. These properties can be a single function that returns
10051     * the transformed value (`function(data, headersGetter, status)`) or an array of such transformation functions,
10052     * which allows you to `push` or `unshift` a new transformation function into the transformation chain.
10053     *
10054     * ### Default Transformations
10055     *
10056     * The `$httpProvider` provider and `$http` service expose `defaults.transformRequest` and
10057     * `defaults.transformResponse` properties. If a request does not provide its own transformations
10058     * then these will be applied.
10059     *
10060     * You can augment or replace the default transformations by modifying these properties by adding to or
10061     * replacing the array.
10062     *
10063     * Angular provides the following default transformations:
10064     *
10065     * Request transformations (`$httpProvider.defaults.transformRequest` and `$http.defaults.transformRequest`):
10066     *
10067     * - If the `data` property of the request configuration object contains an object, serialize it
10068     *   into JSON format.
10069     *
10070     * Response transformations (`$httpProvider.defaults.transformResponse` and `$http.defaults.transformResponse`):
10071     *
10072     *  - If XSRF prefix is detected, strip it (see Security Considerations section below).
10073     *  - If JSON response is detected, deserialize it using a JSON parser.
10074     *
10075     *
10076     * ### Overriding the Default Transformations Per Request
10077     *
10078     * If you wish override the request/response transformations only for a single request then provide
10079     * `transformRequest` and/or `transformResponse` properties on the configuration object passed
10080     * into `$http`.
10081     *
10082     * Note that if you provide these properties on the config object the default transformations will be
10083     * overwritten. If you wish to augment the default transformations then you must include them in your
10084     * local transformation array.
10085     *
10086     * The following code demonstrates adding a new response transformation to be run after the default response
10087     * transformations have been run.
10088     *
10089     * ```js
10090     * function appendTransform(defaults, transform) {
10091     *
10092     *   // We can't guarantee that the default transformation is an array
10093     *   defaults = angular.isArray(defaults) ? defaults : [defaults];
10094     *
10095     *   // Append the new transformation to the defaults
10096     *   return defaults.concat(transform);
10097     * }
10098     *
10099     * $http({
10100     *   url: '...',
10101     *   method: 'GET',
10102     *   transformResponse: appendTransform($http.defaults.transformResponse, function(value) {
10103     *     return doTransform(value);
10104     *   })
10105     * });
10106     * ```
10107     *
10108     *
10109     * ## Caching
10110     *
10111     * To enable caching, set the request configuration `cache` property to `true` (to use default
10112     * cache) or to a custom cache object (built with {@link ng.$cacheFactory `$cacheFactory`}).
10113     * When the cache is enabled, `$http` stores the response from the server in the specified
10114     * cache. The next time the same request is made, the response is served from the cache without
10115     * sending a request to the server.
10116     *
10117     * Note that even if the response is served from cache, delivery of the data is asynchronous in
10118     * the same way that real requests are.
10119     *
10120     * If there are multiple GET requests for the same URL that should be cached using the same
10121     * cache, but the cache is not populated yet, only one request to the server will be made and
10122     * the remaining requests will be fulfilled using the response from the first request.
10123     *
10124     * You can change the default cache to a new object (built with
10125     * {@link ng.$cacheFactory `$cacheFactory`}) by updating the
10126     * {@link ng.$http#defaults `$http.defaults.cache`} property. All requests who set
10127     * their `cache` property to `true` will now use this cache object.
10128     *
10129     * If you set the default cache to `false` then only requests that specify their own custom
10130     * cache object will be cached.
10131     *
10132     * ## Interceptors
10133     *
10134     * Before you start creating interceptors, be sure to understand the
10135     * {@link ng.$q $q and deferred/promise APIs}.
10136     *
10137     * For purposes of global error handling, authentication, or any kind of synchronous or
10138     * asynchronous pre-processing of request or postprocessing of responses, it is desirable to be
10139     * able to intercept requests before they are handed to the server and
10140     * responses before they are handed over to the application code that
10141     * initiated these requests. The interceptors leverage the {@link ng.$q
10142     * promise APIs} to fulfill this need for both synchronous and asynchronous pre-processing.
10143     *
10144     * The interceptors are service factories that are registered with the `$httpProvider` by
10145     * adding them to the `$httpProvider.interceptors` array. The factory is called and
10146     * injected with dependencies (if specified) and returns the interceptor.
10147     *
10148     * There are two kinds of interceptors (and two kinds of rejection interceptors):
10149     *
10150     *   * `request`: interceptors get called with a http {@link $http#usage config} object. The function is free to
10151     *     modify the `config` object or create a new one. The function needs to return the `config`
10152     *     object directly, or a promise containing the `config` or a new `config` object.
10153     *   * `requestError`: interceptor gets called when a previous interceptor threw an error or
10154     *     resolved with a rejection.
10155     *   * `response`: interceptors get called with http `response` object. The function is free to
10156     *     modify the `response` object or create a new one. The function needs to return the `response`
10157     *     object directly, or as a promise containing the `response` or a new `response` object.
10158     *   * `responseError`: interceptor gets called when a previous interceptor threw an error or
10159     *     resolved with a rejection.
10160     *
10161     *
10162     * ```js
10163     *   // register the interceptor as a service
10164     *   $provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) {
10165     *     return {
10166     *       // optional method
10167     *       'request': function(config) {
10168     *         // do something on success
10169     *         return config;
10170     *       },
10171     *
10172     *       // optional method
10173     *      'requestError': function(rejection) {
10174     *         // do something on error
10175     *         if (canRecover(rejection)) {
10176     *           return responseOrNewPromise
10177     *         }
10178     *         return $q.reject(rejection);
10179     *       },
10180     *
10181     *
10182     *
10183     *       // optional method
10184     *       'response': function(response) {
10185     *         // do something on success
10186     *         return response;
10187     *       },
10188     *
10189     *       // optional method
10190     *      'responseError': function(rejection) {
10191     *         // do something on error
10192     *         if (canRecover(rejection)) {
10193     *           return responseOrNewPromise
10194     *         }
10195     *         return $q.reject(rejection);
10196     *       }
10197     *     };
10198     *   });
10199     *
10200     *   $httpProvider.interceptors.push('myHttpInterceptor');
10201     *
10202     *
10203     *   // alternatively, register the interceptor via an anonymous factory
10204     *   $httpProvider.interceptors.push(function($q, dependency1, dependency2) {
10205     *     return {
10206     *      'request': function(config) {
10207     *          // same as above
10208     *       },
10209     *
10210     *       'response': function(response) {
10211     *          // same as above
10212     *       }
10213     *     };
10214     *   });
10215     * ```
10216     *
10217     * ## Security Considerations
10218     *
10219     * When designing web applications, consider security threats from:
10220     *
10221     * - [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx)
10222     * - [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery)
10223     *
10224     * Both server and the client must cooperate in order to eliminate these threats. Angular comes
10225     * pre-configured with strategies that address these issues, but for this to work backend server
10226     * cooperation is required.
10227     *
10228     * ### JSON Vulnerability Protection
10229     *
10230     * A [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx)
10231     * allows third party website to turn your JSON resource URL into
10232     * [JSONP](http://en.wikipedia.org/wiki/JSONP) request under some conditions. To
10233     * counter this your server can prefix all JSON requests with following string `")]}',\n"`.
10234     * Angular will automatically strip the prefix before processing it as JSON.
10235     *
10236     * For example if your server needs to return:
10237     * ```js
10238     * ['one','two']
10239     * ```
10240     *
10241     * which is vulnerable to attack, your server can return:
10242     * ```js
10243     * )]}',
10244     * ['one','two']
10245     * ```
10246     *
10247     * Angular will strip the prefix, before processing the JSON.
10248     *
10249     *
10250     * ### Cross Site Request Forgery (XSRF) Protection
10251     *
10252     * [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery) is a technique by which
10253     * an unauthorized site can gain your user's private data. Angular provides a mechanism
10254     * to counter XSRF. When performing XHR requests, the $http service reads a token from a cookie
10255     * (by default, `XSRF-TOKEN`) and sets it as an HTTP header (`X-XSRF-TOKEN`). Since only
10256     * JavaScript that runs on your domain could read the cookie, your server can be assured that
10257     * the XHR came from JavaScript running on your domain. The header will not be set for
10258     * cross-domain requests.
10259     *
10260     * To take advantage of this, your server needs to set a token in a JavaScript readable session
10261     * cookie called `XSRF-TOKEN` on the first HTTP GET request. On subsequent XHR requests the
10262     * server can verify that the cookie matches `X-XSRF-TOKEN` HTTP header, and therefore be sure
10263     * that only JavaScript running on your domain could have sent the request. The token must be
10264     * unique for each user and must be verifiable by the server (to prevent the JavaScript from
10265     * making up its own tokens). We recommend that the token is a digest of your site's
10266     * authentication cookie with a [salt](https://en.wikipedia.org/wiki/Salt_(cryptography&#41;)
10267     * for added security.
10268     *
10269     * The name of the headers can be specified using the xsrfHeaderName and xsrfCookieName
10270     * properties of either $httpProvider.defaults at config-time, $http.defaults at run-time,
10271     * or the per-request config object.
10272     *
10273     * In order to prevent collisions in environments where multiple Angular apps share the
10274     * same domain or subdomain, we recommend that each application uses unique cookie name.
10275     *
10276     * @param {object} config Object describing the request to be made and how it should be
10277     *    processed. The object has following properties:
10278     *
10279     *    - **method** – `{string}` – HTTP method (e.g. 'GET', 'POST', etc)
10280     *    - **url** – `{string}` – Absolute or relative URL of the resource that is being requested.
10281     *    - **params** – `{Object.<string|Object>}` – Map of strings or objects which will be serialized
10282     *      with the `paramSerializer` and appended as GET parameters.
10283     *    - **data** – `{string|Object}` – Data to be sent as the request message data.
10284     *    - **headers** – `{Object}` – Map of strings or functions which return strings representing
10285     *      HTTP headers to send to the server. If the return value of a function is null, the
10286     *      header will not be sent. Functions accept a config object as an argument.
10287     *    - **xsrfHeaderName** – `{string}` – Name of HTTP header to populate with the XSRF token.
10288     *    - **xsrfCookieName** – `{string}` – Name of cookie containing the XSRF token.
10289     *    - **transformRequest** –
10290     *      `{function(data, headersGetter)|Array.<function(data, headersGetter)>
10290}` –
10291     *      transform function or an array of such functions. The transform function takes the http
10292     *      request body and headers and returns its transformed (typically serialized) version.
10293     *      See {@link ng.$http#overriding-the-default-transformations-per-request
10294     *      Overriding the Default Transformations}
10295     *    - **transformResponse** –
10296     *      `{function(data, headersGetter, status)|Array.<function(data, headersGetter, status)>}` –
10297     *      transform function or an array of such functions. The transform function takes the http
10298     *      response body, headers and status and returns its transformed (typically deserialized) version.
10299     *      See {@link ng.$http#overriding-the-default-transformations-per-request
10300     *      Overriding the Default TransformationjqLiks}
10301     *    - **paramSerializer** - `{string|function(Object<string,string>):string}` - A function used to
10302     *      prepare the string representation of request parameters (specified as an object).
10303     *      If specified as string, it is interpreted as function registered with the
10304     *      {@link $injector $injector}, which means you can create your own serializer
10305     *      by registering it as a {@link auto.$provide#service service}.
10306     *      The default serializer is the {@link $httpParamSerializer $httpParamSerializer};
10307     *      alternatively, you can use the {@link $httpParamSerializerJQLike $httpParamSerializerJQLike}
10308     *    - **cache** – `{boolean|Cache}` – If true, a default $http cache will be used to cache the
10309     *      GET request, otherwise if a cache instance built with
10310     *      {@link ng.$cacheFactory $cacheFactory}, this cache will be used for
10311     *      caching.
10312     *    - **timeout** – `{number|Promise}` – timeout in milliseconds, or {@link ng.$q promise}
10313     *      that should abort the request when resolved.
10314     *    - **withCredentials** - `{boolean}` - whether to set the `withCredentials` flag on the
10315     *      XHR object. See [requests with credentials](https://developer.mozilla.org/docs/Web/HTTP/Access_control_CORS#Requests_with_credentials)
10316     *      for more information.
10317     *    - **responseType** - `{string}` - see
10318     *      [XMLHttpRequest.responseType](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest#xmlhttprequest-responsetype).
10319     *
10320     * @returns {HttpPromise} Returns a {@link ng.$q `Promise}` that will be resolved to a response object
10321     *                        when the request succeeds or fails.
10322     *
10323     *
10324     * @property {Array.<Object>} pendingRequests Array of config objects for currently pending
10325     *   requests. This is primarily meant to be used for debugging purposes.
10326     *
10327     *
10328     * @example
10329<example module="httpExample">
10330<file name="index.html">
10331  <div ng-controller="FetchController">
10332    <select ng-model="method" aria-label="Request method">
10333      <option>GET</option>
10334      <option>JSONP</option>
10335    </select>
10336    <input type="text" ng-model="url" size="80" aria-label="URL" />
10337    <button id="fetchbtn" ng-click="fetch()">fetch</button><br>
10338    <button id="samplegetbtn" ng-click="updateModel('GET', 'http-hello.html')">Sample GET</button>
10339    <button id="samplejsonpbtn"
10340      ng-click="updateModel('JSONP',
10341                    'https://angularjs.org/greet.php?callback=JSON_CALLBACK&name=Super%20Hero')">
10342      Sample JSONP
10343    </button>
10344    <button id="invalidjsonpbtn"
10345      ng-click="updateModel('JSONP', 'https://angularjs.org/doesntexist&callback=JSON_CALLBACK')">
10346        Invalid JSONP
10347      </button>
10348    <pre>http status code: {{status}}</pre>
10349    <pre>http response data: {{data}}</pre>
10350  </div>
10351</file>
10352<file name="script.js">
10353  angular.module('httpExample', [])
10354    .controller('FetchController', ['$scope', '$http', '$templateCache',
10355      function($scope, $http, $templateCache) {
10356        $scope.method = 'GET';
10357        $scope.url = 'http-hello.html';
10358
10359        $scope.fetch = function() {
10360          $scope.code = null;
10361          $scope.response = null;
10362
10363          $http({method: $scope.method, url: $scope.url, cache: $templateCache}).
10364            then(function(response) {
10365              $scope.status = response.status;
10366              $scope.data = response.data;
10367            }, function(response) {
10368              $scope.data = response.data || "Request failed";
10369              $scope.status = response.status;
10370          });
10371        };
10372
10373        $scope.updateModel = function(method, url) {
10374          $scope.method = method;
10375          $scope.url = url;
10376        };
10377      }]);
10378</file>
10379<file name="http-hello.html">
10380  Hello, $http!
10381</file>
10382<file name="protractor.js" type="protractor">
10383  var status = element(by.binding('status'));
10384  var data = element(by.binding('data'));
10385  var fetchBtn = element(by.id('fetchbtn'));
10386  var sampleGetBtn = element(by.id('samplegetbtn'));
10387  var sampleJsonpBtn = element(by.id('samplejsonpbtn'));
10388  var invalidJsonpBtn = element(by.id('invalidjsonpbtn'));
10389
10390  it('should make an xhr GET request', function() {
10391    sampleGetBtn.click();
10392    fetchBtn.click();
10393    expect(status.getText()).toMatch('200');
10394    expect(data.getText()).toMatch(/Hello, \$http!/);
10395  });
10396
10397// Commented out due to flakes. See https://github.com/angular/angular.js/issues/9185
10398// it('should make a JSONP request to angularjs.org', function() {
10399//   sampleJsonpBtn.click();
10400//   fetchBtn.click();
10401//   expect(status.getText()).toMatch('200');
10402//   expect(data.getText()).toMatch(/Super Hero!/);
10403// });
10404
10405  it('should make JSONP request to invalid URL and invoke the error handler',
10406      function() {
10407    invalidJsonpBtn.click();
10408    fetchBtn.click();
10409    expect(status.getText()).toMatch('0');
10410    expect(data.getText()).toMatch('Request failed');
10411  });
10412</file>
10413</example>
10414     */
10415    function $http(requestConfig) {
10416
10417      if (!angular.isObject(requestConfig)) {
10418        throw minErr('$http')('badreq', 'Http request configuration must be an object.  Received: {0}', requestConfig);
10419      }
10420
10421      var config = extend({
10422        method: 'get',
10423        transformRequest: defaults.transformRequest,
10424        transformResponse: defaults.transformResponse,
10425        paramSerializer: defaults.paramSerializer
10426      }, requestConfig);
10427
10428      config.headers = mergeHeaders(requestConfig);
10429      config.method = uppercase(config.method);
10430      config.paramSerializer = isString(config.paramSerializer) ?
10431        $injector.get(config.paramSerializer) : config.paramSerializer;
10432
10433      var serverRequest = function(config) {
10434        var headers = config.headers;
10435        var reqData = transformData(config.data, headersGetter(headers), undefined, config.transformRequest);
10436
10437        // strip content-type if data is undefined
10438        if (isUndefined(reqData)) {
10439          forEach(headers, function(value, header) {
10440            if (lowercase(header) === 'content-type') {
10441                delete headers[header];
10442            }
10443          });
10444        }
10445
10446        if (isUndefined(config.withCredentials) && !isUndefined(defaults.withCredentials)) {
10447          config.withCredentials = defaults.withCredentials;
10448        }
10449
10450        // send request
10451        return sendReq(config, reqData).then(transformResponse, transformResponse);
10452      };
10453
10454      var chain = [serverRequest, undefined];
10455      var promise = $q.when(config);
10456
10457      // apply interceptors
10458      forEach(reversedInterceptors, function(interceptor) {
10459        if (interceptor.request || interceptor.requestError) {
10460          chain.unshift(interceptor.request, interceptor.requestError);
10461        }
10462        if (interceptor.response || interceptor.responseError) {
10463          chain.push(interceptor.response, interceptor.responseError);
10464        }
10465      });
10466
10467      while (chain.length) {
10468        var thenFn = chain.shift();
10469        var rejectFn = chain.shift();
10470
10471        promise = promise.then(thenFn, rejectFn);
10472      }
10473
10474      if (useLegacyPromise) {
10475        promise.success = function(fn) {
10476          assertArgFn(fn, 'fn');
10477
10478          promise.then(function(response) {
10479            fn(response.data, response.status, response.headers, config);
10480          });
10481          return promise;
10482        };
10483
10484        promise.error = function(fn) {
10485          assertArgFn(fn, 'fn');
10486
10487          promise.then(null, function(response) {
10488            fn(response.data, response.status, response.headers, config);
10489          });
10490          return promise;
10491        };
10492      } else {
10493        promise.success = $httpMinErrLegacyFn('success');
10494        promise.error = $httpMinErrLegacyFn('error');
10495      }
10496
10497      return promise;
10498
10499      function transformResponse(response) {
10500        // make a copy since the response must be cacheable
10501        var resp = extend({}, response);
10502        if (!response.data) {
10503          resp.data = response.data;
10504        } else {
10505          resp.data = transformData(response.data, response.headers, response.status, config.transformResponse);
10506        }
10507        return (isSuccess(response.status))
10508          ? resp
10509          : $q.reject(resp);
10510      }
10511
10512      function executeHeaderFns(headers, config) {
10513        var headerContent, processedHeaders = {};
10514
10515        forEach(headers, function(headerFn, header) {
10516          if (isFunction(headerFn)) {
10517            headerContent = headerFn(config);
10518            if (headerContent != null) {
10519              processedHeaders[header] = headerContent;
10520            }
10521          } else {
10522            processedHeaders[header] = headerFn;
10523          }
10524        });
10525
10526        return processedHeaders;
10527      }
10528
10529      function mergeHeaders(config) {
10530        var defHeaders = defaults.headers,
10531            reqHeaders = extend({}, config.headers),
10532            defHeaderName, lowercaseDefHeaderName, reqHeaderName;
10533
10534        defHeaders = extend({}, defHeaders.common, defHeaders[lowercase(config.method)]);
10535
vendor: 9,098 bytes, lines 10536-10836
10536        // using for-in instead of forEach to avoid unecessary iteration after header has been found
10537        defaultHeadersIteration:
10538        for (defHeaderName in defHeaders) {
10539          lowercaseDefHeaderName = lowercase(defHeaderName);
10540
10541          for (reqHeaderName in reqHeaders) {
10542            if (lowercase(reqHeaderName) === lowercaseDefHeaderName) {
10543              continue defaultHeadersIteration;
10544            }
10545          }
10546
10547          reqHeaders[defHeaderName] = defHeaders[defHeaderName];
10548        }
10549
10550        // execute if header value is a function for merged headers
10551        return executeHeaderFns(reqHeaders, shallowCopy(config));
10552      }
10553    }
10554
10555    $http.pendingRequests = [];
10556
10557    /**
10558     * @ngdoc method
10559     * @name $http#get
10560     *
10561     * @description
10562     * Shortcut method to perform `GET` request.
10563     *
10564     * @param {string} url Relative or absolute URL specifying the destination of the request
10565     * @param {Object=} config Optional configuration object
10566     * @returns {HttpPromise} Future object
10567     */
10568
10569    /**
10570     * @ngdoc method
10571     * @name $http#delete
10572     *
10573     * @description
10574     * Shortcut method to perform `DELETE` request.
10575     *
10576     * @param {string} url Relative or absolute URL specifying the destination of the request
10577     * @param {Object=} config Optional configuration object
10578     * @returns {HttpPromise} Future object
10579     */
10580
10581    /**
10582     * @ngdoc method
10583     * @name $http#head
10584     *
10585     * @description
10586     * Shortcut method to perform `HEAD` request.
10587     *
10588     * @param {string} url Relative or absolute URL specifying the destination of the request
10589     * @param {Object=} config Optional configuration object
10590     * @returns {HttpPromise} Future object
10591     */
10592
10593    /**
10594     * @ngdoc method
10595     * @name $http#jsonp
10596     *
10597     * @description
10598     * Shortcut method to perform `JSONP` request.
10599     *
10600     * @param {string} url Relative or absolute URL specifying the destination of the request.
10601     *                     The name of the callback should be the string `JSON_CALLBACK`.
10602     * @param {Object=} config Optional configuration object
10603     * @returns {HttpPromise} Future object
10604     */
10605    createShortMethods('get', 'delete', 'head', 'jsonp');
10606
10607    /**
10608     * @ngdoc method
10609     * @name $http#post
10610     *
10611     * @description
10612     * Shortcut method to perform `POST` request.
10613     *
10614     * @param {string} url Relative or absolute URL specifying the destination of the request
10615     * @param {*} data Request content
10616     * @param {Object=} config Optional configuration object
10617     * @returns {HttpPromise} Future object
10618     */
10619
10620    /**
10621     * @ngdoc method
10622     * @name $http#put
10623     *
10624     * @description
10625     * Shortcut method to perform `PUT` request.
10626     *
10627     * @param {string} url Relative or absolute URL specifying the destination of the request
10628     * @param {*} data Request content
10629     * @param {Object=} config Optional configuration object
10630     * @returns {HttpPromise} Future object
10631     */
10632
10633     /**
10634      * @ngdoc method
10635      * @name $http#patch
10636      *
10637      * @description
10638      * Shortcut method to perform `PATCH` request.
10639      *
10640      * @param {string} url Relative or absolute URL specifying the destination of the request
10641      * @param {*} data Request content
10642      * @param {Object=} config Optional configuration object
10643      * @returns {HttpPromise} Future object
10644      */
10645    createShortMethodsWithData('post', 'put', 'patch');
10646
10647        /**
10648         * @ngdoc property
10649         * @name $http#defaults
10650         *
10651         * @description
10652         * Runtime equivalent of the `$httpProvider.defaults` property. Allows configuration of
10653         * default headers, withCredentials as well as request and response transformations.
10654         *
10655         * See "Setting HTTP Headers" and "Transforming Requests and Responses" sections above.
10656         */
10657    $http.defaults = defaults;
10658
10659
10660    return $http;
10661
10662
10663    function createShortMethods(names) {
10664      forEach(arguments, function(name) {
10665        $http[name] = function(url, config) {
10666          return $http(extend({}, config || {}, {
10667            method: name,
10668            url: url
10669          }));
10670        };
10671      });
10672    }
10673
10674
10675    function createShortMethodsWithData(name) {
10676      forEach(arguments, function(name) {
10677        $http[name] = function(url, data, config) {
10678          return $http(extend({}, config || {}, {
10679            method: name,
10680            url: url,
10681            data: data
10682          }));
10683        };
10684      });
10685    }
10686
10687
10688    /**
10689     * Makes the request.
10690     *
10691     * !!! ACCESSES CLOSURE VARS:
10692     * $httpBackend, defaults, $log, $rootScope, defaultCache, $http.pendingRequests
10693     */
10694    function sendReq(config, reqData) {
10695      var deferred = $q.defer(),
10696          promise = deferred.promise,
10697          cache,
10698          cachedResp,
10699          reqHeaders = config.headers,
10700          url = buildUrl(config.url, config.paramSerializer(config.params));
10701
10702      $http.pendingRequests.push(config);
10703      promise.then(removePendingReq, removePendingReq);
10704
10705
10706      if ((config.cache || defaults.cache) && config.cache !== false &&
10707          (config.method === 'GET' || config.method === 'JSONP')) {
10708        cache = isObject(config.cache) ? config.cache
10709              : isObject(defaults.cache) ? defaults.cache
10710              : defaultCache;
10711      }
10712
10713      if (cache) {
10714        cachedResp = cache.get(url);
10715        if (isDefined(cachedResp)) {
10716          if (isPromiseLike(cachedResp)) {
10717            // cached request has already been sent, but there is no response yet
10718            cachedResp.then(resolvePromiseWithResult, resolvePromiseWithResult);
10719          } else {
10720            // serving from cache
10721            if (isArray(cachedResp)) {
10722              resolvePromise(cachedResp[1], cachedResp[0], shallowCopy(cachedResp[2]), cachedResp[3]);
10723            } else {
10724              resolvePromise(cachedResp, 200, {}, 'OK');
10725            }
10726          }
10727        } else {
10728          // put the promise for the non-transformed response into cache as a placeholder
10729          cache.put(url, promise);
10730        }
10731      }
10732
10733
10734      // if we won't have the response in cache, set the xsrf headers and
10735      // send the request to the backend
10736      if (isUndefined(cachedResp)) {
10737        var xsrfValue = urlIsSameOrigin(config.url)
10738            ? $$cookieReader()[config.xsrfCookieName || defaults.xsrfCookieName]
10739            : undefined;
10740        if (xsrfValue) {
10741          reqHeaders[(config.xsrfHeaderName || defaults.xsrfHeaderName)] = xsrfValue;
10742        }
10743
10744        $httpBackend(config.method, url, reqData, done, reqHeaders, config.timeout,
10745            config.withCredentials, config.responseType);
10746      }
10747
10748      return promise;
10749
10750
10751      /**
10752       * Callback registered to $httpBackend():
10753       *  - caches the response if desired
10754       *  - resolves the raw $http promise
10755       *  - calls $apply
10756       */
10757      function done(status, response, headersString, statusText) {
10758        if (cache) {
10759          if (isSuccess(status)) {
10760            cache.put(url, [status, response, parseHeaders(headersString), statusText]);
10761          } else {
10762            // remove promise from the cache
10763            cache.remove(url);
10764          }
10765        }
10766
10767        function resolveHttpPromise() {
10768          resolvePromise(response, status, headersString, statusText);
10769        }
10770
10771        if (useApplyAsync) {
10772          $rootScope.$applyAsync(resolveHttpPromise);
10773        } else {
10774          resolveHttpPromise();
10775          if (!$rootScope.$$phase) $rootScope.$apply();
10776        }
10777      }
10778
10779
10780      /**
10781       * Resolves the raw $http promise.
10782       */
10783      function resolvePromise(response, status, headers, statusText) {
10784        //status: HTTP response status code, 0, -1 (aborted by timeout / promise)
10785        status = status >= -1 ? status : 0;
10786
10787        (isSuccess(status) ? deferred.resolve : deferred.reject)({
10788          data: response,
10789          status: status,
10790          headers: headersGetter(headers),
10791          config: config,
10792          statusText: statusText
10793        });
10794      }
10795
10796      function resolvePromiseWithResult(result) {
10797        resolvePromise(result.data, result.status, shallowCopy(result.headers()), result.statusText);
10798      }
10799
10800      function removePendingReq() {
10801        var idx = $http.pendingRequests.indexOf(config);
10802        if (idx !== -1) $http.pendingRequests.splice(idx, 1);
10803      }
10804    }
10805
10806
10807    function buildUrl(url, serializedParams) {
10808      if (serializedParams.length > 0) {
10809        url += ((url.indexOf('?') == -1) ? '?' : '&') + serializedParams;
10810      }
10811      return url;
10812    }
10813  }];
10814}
10815
10816/**
10817 * @ngdoc service
10818 * @name $xhrFactory
10819 *
10820 * @description
10821 * Factory function used to create XMLHttpRequest objects.
10822 *
10823 * Replace or decorate this service to create your own custom XMLHttpRequest objects.
10824 *
10825 * ```
10826 * angular.module('myApp', [])
10827 * .factory('$xhrFactory', function() {
10828 *   return function createXhr(method, url) {
10829 *     return new window.XMLHttpRequest({mozSystem: true});
10830 *   };
10831 * });
10832 * ```
10833 *
10834 * @param {string} method HTTP method of the request (GET, POST, PUT, ..)
10835 * @param {string} url URL of the request.
10836 */
10837function $xhrFactoryProvider() {
10838  this.$get = function() {
10839    return function createXhr() {
10840      return new window.XMLHttpRequest();
10841    };
10842  };
10843}
10844
10845/**
10846 * @ngdoc service
10847 * @name $httpBackend
10848 * @requires $window
10849 * @requires $document
10850 * @requires $xhrFactory
10851 *
10852 * @description
10853 * HTTP backend used by the {@link ng.$http service} that delegates to
10854 * XMLHttpRequest object or JSONP and deals with browser incompatibilities.
10855 *
10856 * You should never need to use this service directly, instead use the higher-level abstractions:
10857 * {@link ng.$http $http} or {@link ngResource.$resource $resource}.
10858 *
10859 * During testing this implementation is swapped with {@link ngMock.$httpBackend mock
10860 * $httpBackend} which can be trained with responses.
10861 */
10862function $HttpBackendProvider() {
10863  this.$get = ['$browser', '$window', '$document', '$xhrFactory', function($browser, $window, $document, $xhrFactory) {
10864    return createHttpBackend($browser, $xhrFactory, $browser.defer, $window.angular.callbacks, $document[0]);
10865  }];
10866}
10867
10868function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDocument) {
10869  // TODO(vojta): fix the signature
10870  return function(method, url, post, callback, headers, timeout, withCredentials, responseType) {
10871    $browser.$$incOutstandingRequestCount();
10872    url = url || $browser.url();
10873
10874    if (lowercase(method) == 'jsonp') {
10875      var callbackId = '_' + (callbacks.counter++).toString(36);
10876      callbacks[callbackId] = function(data) {
10877        callbacks[callbackId].data = data;
10878        callbacks[callbackId].called = true;
10879      };
10880
10881      var jsonpDone = jsonpReq(url.replace('JSON_CALLBACK', 'angular.callbacks.' + callbackId),
10882          callbackId, function(status, text) {
10883        completeRequest(callback, status, callbacks[callbackId].data, "", text);
10884        callbacks[callbackId] = noop;
10885      });
10886    } else {
10887
10888      var xhr = createXhr(method, url);
10889
10890      xhr.open(method, url, true);
10891      forEach(headers, function(value, key) {
10892        if (isDefined(value)) {
10893            xhr.setRequestHeader(key, value);
10894        }
10895      });
10896
10897      xhr.onload = function requestLoaded() {
10898        var statusText = xhr.statusText || '';
10899
10900        // responseText is the old-school way of retrieving response (supported by IE9)
10901        // response/responseType properties were introduced in XHR Level2 spec (supported by IE10)
10902        var response = ('response' in xhr) ? xhr.response : xhr.responseText;
10903
10904        // normalize IE9 bug (http://bugs.jquery.com/ticket/1450)
10905        var status = xhr.status === 1223 ? 204 : xhr.status;
10906
10907        // fix status code when it is 0 (0 status is undocumented).
10908        // Occurs when accessing file resources or on Android 4.1 stock browser
10909        // while retrieving files from application cache.
10910        if (status === 0) {
10911          status = response ? 200 : urlResolve(url).protocol == 'file' ? 404 : 0;
10912        }
10913
10914        completeRequest(callback,
10915            status,
10916            response,
10917            xhr.getAllResponseHeaders(),
10918            statusText);
10919      };
10920
10921      var requestError = function() {
10922        // The response is always empty
10923        // See https://xhr.spec.whatwg.org/#request-error-steps and https://fetch.spec.whatwg.org/#concept-network-error
10924        completeRequest(callback, -1, null, null, '');
10925      };
10926
10927      xhr.onerror = requestError;
10928      xhr.onabort = requestError;
10929
10930      if (withCredentials) {
10931        xhr.withCredentials = true;
10932      }
10933
10934      if (responseType) {
10935        try {
10936          xhr.responseType = responseType;
10937        } catch (e) {
10938          // WebKit added support for the json responseType value on 09/03/2013
10939          // https://bugs.webkit.org/show_bug.cgi?id=73648. Versions of Safari prior to 7 are
10940          // known to throw when setting the value "json" as the response type. Other older
10941          // browsers implementing the responseType
10942          //
10943          // The json response type can be ignored if not supported, because JSON payloads are
10944          // parsed on the client-side regardless.
10945          if (responseType !== 'json') {
10946            throw e;
10947          }
10948        }
10949      }
10950
10951      xhr.send(isUndefined(post) ? null : post);
10952    }
10953
10954    if (timeout > 0) {
10955      var timeoutId = $browserDefer(timeoutRequest, timeout);
10956    } else if (isPromiseLike(timeout)) {
10957      timeout.then(timeoutRequest);
10958    }
10959
10960
10961    function timeoutRequest() {
10962      jsonpDone && jsonpDone();
10963      xhr && xhr.abort();
10964    }
10965
10966    function completeRequest(callback, status, response, headersString, statusText) {
10967      // cancel timeout and subsequent timeout promise resolution
10968      if (isDefined(timeoutId)) {
10969        $browserDefer.cancel(timeoutId);
10970      }
10971      jsonpDone = xhr = null;
10972
10973      callback(status, response, headersString, statusText);
10974      $browser.$$completeOutstandingRequest(noop);
10975    }
10976  };
10977
10978  function jsonpReq(url, callbackId, done) {
10979    // we can't use jQuery/jqLite here because jQuery does crazy stuff with script elements, e.g.:
10980    // - fetches local scripts via XHR and evals them
10981    // - adds and immediately removes script elements from the document
10982    var script = rawDocument.createElement('script'), callback = null;
10983    script.type = "text/javascript";
10984    script.src = url;
10985    script.async = true;
10986
10987    callback = function(event) {
10988      removeEventListenerFn(script, "load", callback);
10989      removeEventListenerFn(script, "error", callback);
10990      rawDocument.body.removeChild(script);
10991      script = null;
10992      var status = -1;
10993      var text = "unknown";
10994
10995      if (event) {
10996        if (event.type === "load" && !callbacks[callbackId].called) {
10997          event = { type: "error" };
10998        }
10999        text = event.type;
11000        status = event.type === "error" ? 404 : 200;
11001      }
11002
11003      if (done) {
11004        done(status, text);
11005      }
11006    };
11007
11008    addEventListenerFn(script, "load", callback);
11009    addEventListenerFn(script, "error", callback);
11010    rawDocument.body.appendChild(script);
11011    return callback;
11012  }
11013}
11014
11015var $interpolateMinErr = angular.$interpolateMinErr = minErr('$interpolate');
11016$interpolateMinErr.throwNoconcat = function(text) {
11017  throw $interpolateMinErr('noconcat',
11018      "Error while interpolating: {0}\nStrict Contextual Escaping disallows " +
11019      "interpolations that concatenate multiple expressions when a trusted value is " +
11020      "required.  See http://docs.angularjs.org/api/ng.$sce", text);
11021};
11022
11023$interpolateMinErr.interr = function(text, err) {
11024  return $interpolateMinErr('interr', "Can't interpolate: {0}\n{1}", text, err.toString());
11025};
11026
11027/**
11028 * @ngdoc provider
11029 * @name $interpolateProvider
11030 *
11031 * @description
11032 *
11033 * Used for configuring the interpolation markup. Defaults to `{{` and `}}`.
11034 *
11035 * @example
11036<example module="customInterpolationApp">
11037<file name="index.html">
11038<script>
11039  var customInterpolationApp = angular.module('customInterpolationApp', []);
11040
11041  customInterpolationApp.config(function($interpolateProvider) {
11042    $interpolateProvider.startSymbol('//');
11043    $interpolateProvider.endSymbol('//');
11044  });
11045
11046
11047  customInterpolationApp.controller('DemoController', function() {
11048      this.label = "This binding is brought you by // interpolation symbols.";
11049  });
11050</script>
11051<div ng-app="App" ng-controller="DemoController as demo">
11052    //demo.label//
11053</div>
11054</file>
11055<file name="protractor.js" type="protractor">
11056  it('should interpolate binding with custom symbols', function() {
11057    expect(element(by.binding('demo.label')).getText()).toBe('This binding is brought you by // interpolation symbols.');
11058  });
11059</file>
11060</example>
11061 */
11062function $InterpolateProvider() {
11063  var startSymbol = '{{';
11064  var endSymbol = '}}';
11065
11066  /**
11067   * @ngdoc method
11068   * @name $interpolateProvider#startSymbol
11069   * @description
11070   * Symbol to denote start of expression in the interpolated string. Defaults to `{{`.
11071   *
11072   * @param {string=} value new value to set the starting symbol to.
11073   * @returns {string|self} Returns the symbol when used as getter and self if used as setter.
11074   */
11075  this.startSymbol = function(value) {
11076    if (value) {
11077      startSymbol = value;
11078      return this;
11079    } else {
11080      return startSymbol;
11081    }
11082  };
11083
11084  /**
11085   * @ngdoc method
11086   * @name $interpolateProvider#endSymbol
11087   * @description
11088   * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
11089   *
11090   * @param {string=} value new value to set the ending symbol to.
11091   * @returns {string|self} Returns the symbol when used as getter and self if used as setter.
11092   */
11093  this.endSymbol = function(value) {
11094    if (value) {
11095      endSymbol = value;
11096      return this;
11097    } else {
11098      return endSymbol;
11099    }
11100  };
11101
11102
11103  this.$get = ['$parse', '$exceptionHandler', '$sce', function($parse, $exceptionHandler, $sce) {
11104    var startSymbolLength = startSymbol.length,
11105        endSymbolLength = endSymbol.length,
11106        escapedStartRegexp = new RegExp(startSymbol.replace(/./g, escape), 'g'),
11107        escapedEndRegexp = new RegExp(endSymbol.replace(/./g, escape), 'g');
11108
11109    function escape(ch) {
11110      return '\\\\\\' + ch;
11111    }
11112
11113    function unescapeText(text) {
11114      return text.replace(escapedStartRegexp, startSymbol).
11115        replace(escapedEndRegexp, endSymbol);
11116    }
11117
11118    function stringify(value) {
11119      if (value == null) { // null || undefined
11120        return '';
11121      }
11122      switch (typeof value) {
11123        case 'string':
11124          break;
11125        case 'number':
11126          value = '' + value;
11127          break;
11128        default:
11129          value = toJson(value);
11130      }
11131
11132      return value;
11133    }
11134
11135    /**
11136     * @ngdoc service
11137     * @name $interpolate
11138     * @kind function
11139     *
11140     * @requires $parse
11141     * @requires $sce
11142     *
11143     * @description
11144     *
11145     * Compiles a string with markup into an interpolation function. This service is used by the
11146     * HTML {@link ng.$compile $compile} service for data binding. See
11147     * {@link ng.$interpolateProvider $interpolateProvider} for configuring the
11148     * interpolation markup.
11149     *
11150     *
11151     * ```js
11152     *   var $interpolate = ...; // injected
11153     *   var exp = $interpolate('Hello {{name | uppercase}}!');
11154     *   expect(exp({name:'Angular'})).toEqual('Hello ANGULAR!');
11155     * ```
11156     *
11157     * `$interpolate` takes an optional fourth argument, `allOrNothing`. If `allOrNothing` is
11158     * `true`, the interpolation function will return `undefined` unless all embedded expressions
11159     * evaluate to a value other than `undefined`.
11160     *
11161     * ```js
11162     *   var $interpolate = ...; // injected
11163     *   var context = {greeting: 'Hello', name: undefined };
11164     *
11165     *   // default "forgiving" mode
11166     *   var exp = $interpolate('{{greeting}} {{name}}!');
11167     *   expect(exp(context)).toEqual('Hello !');
11168     *
11169     *   // "allOrNothing" mode
11170     *   exp = $interpolate('{{greeting}} {{name}}!', false, null, true);
11171     *   expect(exp(context)).toBeUndefined();
11172     *   context.name = 'Angular';
11173     *   expect(exp(context)).toEqual('Hello Angular!');
11174     * ```
11175     *
11176     * `allOrNothing` is useful for interpolating URLs. `ngSrc` and `ngSrcset` use this behavior.
11177     *
11178     * ####Escaped Interpolation
11179     * $interpolate provides a mechanism for escaping interpolation markers. Start and end markers
11180     * can be escaped by preceding each of their characters with a REVERSE SOLIDUS U+005C (backslash).
11181     * It will be rendered as a regular start/end marker, and will not be interpreted as an expression
11182     * or binding.
11183     *
11184     * This enables web-servers to prevent script injection attacks and defacing attacks, to some
11185     * degree, while also enabling code examples to work without relying on the
11186     * {@link ng.directive:ngNonBindable ngNonBindable} directive.
11187     *
11188     * **For security purposes, it is strongly encouraged that web servers escape user-supplied data,
11189     * replacing angle brackets (&lt;, &gt;) with &amp;lt; and &amp;gt; respectively, and replacing all
11190     * interpolation start/end markers with their escaped counterparts.**
11191     *
11192     * Escaped interpolation markers are only replaced with the actual interpolation markers in rendered
11193     * output when the $interpolate service processes the text. So, for HTML elements interpolated
11194     * by {@link ng.$compile $compile}
11194, or otherwise interpolated with the `mustHaveExpression` parameter
11195     * set to `true`, the interpolated text must contain an unescaped interpolation expression. As such,
11196     * this is typically useful only when user-data is used in rendering a template from the server, or
11197     * when otherwise untrusted data is used by a directive.
11198     *
11199     * <example>
11200     *  <file name="index.html">
11201     *    <div ng-init="username='A user'">
11202     *      <p ng-init="apptitle='Escaping demo'">{{apptitle}}: \{\{ username = "defaced value"; \}\}
11203     *        </p>
11204     *      <p><strong>{{username}}</strong> attempts to inject code which will deface the
11205     *        application, but fails to accomplish their task, because the server has correctly
11206     *        escaped the interpolation start/end markers with REVERSE SOLIDUS U+005C (backslash)
11207     *        characters.</p>
11208     *      <p>Instead, the result of the attempted script injection is visible, and can be removed
11209     *        from the database by an administrator.</p>
11210     *    </div>
11211     *  </file>
11212     * </example>
11213     *
11214     * @param {string} text The text with markup to interpolate.
11215     * @param {boolean=} mustHaveExpression if set to true then the interpolation string must have
11216     *    embedded expression in order to return an interpolation function. Strings with no
11217     *    embedded expression will return null for the interpolation function.
11218     * @param {string=} trustedContext when provided, the returned function passes the interpolated
11219     *    result through {@link ng.$sce#getTrusted $sce.getTrusted(interpolatedResult,
11220     *    trustedContext)} before returning it.  Refer to the {@link ng.$sce $sce} service that
11221     *    provides Strict Contextual Escaping for details.
11222     * @param {boolean=} allOrNothing if `true`, then the returned function returns undefined
11223     *    unless all embedded expressions evaluate to a value other than `undefined`.
11224     * @returns {function(context)} an interpolation function which is used to compute the
11225     *    interpolated string. The function has these parameters:
11226     *
11227     * - `context`: evaluation context for all expressions embedded in the interpolated text
11228     */
11229    function $interpolate(text, mustHaveExpression, trustedContext, allOrNothing) {
11230      allOrNothing = !!allOrNothing;
11231      var startIndex,
11232          endIndex,
11233          index = 0,
11234          expressions = [],
11235          parseFns = [],
11236          textLength = text.length,
11237          exp,
11238          concat = [],
11239          expressionPositions = [];
11240
11241      while (index < textLength) {
11242        if (((startIndex = text.indexOf(startSymbol, index)) != -1) &&
11243             ((endIndex = text.indexOf(endSymbol, startIndex + startSymbolLength)) != -1)) {
11244          if (index !== startIndex) {
11245            concat.push(unescapeText(text.substring(index, startIndex)));
11246          }
11247          exp = text.substring(startIndex + startSymbolLength, endIndex);
11248          expressions.push(exp);
11249          parseFns.push($parse(exp, parseStringifyInterceptor));
11250          index = endIndex + endSymbolLength;
11251          expressionPositions.push(concat.length);
11252          concat.push('');
11253        } else {
11254          // we did not find an interpolation, so we have to add the remainder to the separators array
11255          if (index !== textLength) {
11256            concat.push(unescapeText(text.substring(index)));
11257          }
11258          break;
11259        }
11260      }
11261
11262      // Concatenating expressions makes it hard to reason about whether some combination of
11263      // concatenated values are unsafe to use and could easily lead to XSS.  By requiring that a
11264      // single expression be used for iframe[src], object[src], etc., we ensure that the value
11265      // that's used is assigned or constructed by some JS code somewhere that is more testable or
11266      // make it obvious that you bound the value to some user controlled value.  This helps reduce
11267      // the load when auditing for XSS issues.
11268      if (trustedContext && concat.length > 1) {
11269          $interpolateMinErr.throwNoconcat(text);
11270      }
11271
11272      if (!mustHaveExpression || expressions.length) {
11273        var compute = function(values) {
11274          for (var i = 0, ii = expressions.length; i < ii; i++) {
11275            if (allOrNothing && isUndefined(values[i])) return;
11276            concat[expressionPositions[i]] = values[i];
11277          }
11278          return concat.join('');
11279        };
11280
11281        var getValue = function(value) {
11282          return trustedContext ?
11283            $sce.getTrusted(trustedContext, value) :
11284            $sce.valueOf(value);
11285        };
11286
11287        return extend(function interpolationFn(context) {
11288            var i = 0;
11289            var ii = expressions.length;
11290            var values = new Array(ii);
11291
11292            try {
11293              for (; i < ii; i++) {
11294                values[i] = parseFns[i](context);
11295              }
11296
11297              return compute(values);
11298            } catch (err) {
11299              $exceptionHandler($interpolateMinErr.interr(text, err));
11300            }
11301
11302          }, {
11303          // all of these properties are undocumented for now
11304          exp: text, //just for compatibility with regular watchers created via $watch
11305          expressions: expressions,
11306          $$watchDelegate: function(scope, listener) {
11307            var lastValue;
11308            return scope.$watchGroup(parseFns, function interpolateFnWatcher(values, oldValues) {
11309              var currValue = compute(values);
11310              if (isFunction(listener)) {
11311                listener.call(this, currValue, values !== oldValues ? lastValue : currValue, scope);
11312              }
11313              lastValue = currValue;
11314            });
11315          }
11316        });
11317      }
11318
11319      function parseStringifyInterceptor(value) {
11320        try {
11321          value = getValue(value);
11322          return allOrNothing && !isDefined(value) ? value : stringify(value);
11323        } catch (err) {
11324          $exceptionHandler($interpolateMinErr.interr(text, err));
11325        }
11326      }
11327    }
11328
11329
11330    /**
11331     * @ngdoc method
11332     * @name $interpolate#startSymbol
11333     * @description
11334     * Symbol to denote the start of expression in the interpolated string. Defaults to `{{`.
11335     *
11336     * Use {@link ng.$interpolateProvider#startSymbol `$interpolateProvider.startSymbol`} to change
11337     * the symbol.
11338     *
11339     * @returns {string} start symbol.
11340     */
11341    $interpolate.startSymbol = function() {
11342      return startSymbol;
11343    };
11344
11345
11346    /**
11347     * @ngdoc method
11348     * @name $interpolate#endSymbol
11349     * @description
11350     * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
11351     *
11352     * Use {@link ng.$interpolateProvider#endSymbol `$interpolateProvider.endSymbol`} to change
11353     * the symbol.
11354     *
11355     * @returns {string} end symbol.
11356     */
11357    $interpolate.endSymbol = function() {
11358      return endSymbol;
11359    };
11360
11361    return $interpolate;
11362  }];
11363}
11364
11365function $IntervalProvider() {
11366  this.$get = ['$rootScope', '$window', '$q', '$$q',
11367       function($rootScope,   $window,   $q,   $$q) {
11368    var intervals = {};
11369
11370
11371     /**
11372      * @ngdoc service
11373      * @name $interval
11374      *
11375      * @description
11376      * Angular's wrapper for `window.setInterval`. The `fn` function is executed every `delay`
11377      * milliseconds.
11378      *
11379      * The return value of registering an interval function is a promise. This promise will be
11380      * notified upon each tick of the interval, and will be resolved after `count` iterations, or
11381      * run indefinitely if `count` is not defined. The value of the notification will be the
11382      * number of iterations that have run.
11383      * To cancel an interval, call `$interval.cancel(promise)`.
11384      *
11385      * In tests you can use {@link ngMock.$interval#flush `$interval.flush(millis)`} to
11386      * move forward by `millis` milliseconds and trigger any functions scheduled to run in that
11387      * time.
11388      *
11389      * <div class="alert alert-warning">
11390      * **Note**: Intervals created by this service must be explicitly destroyed when you are finished
11391      * with them.  In particular they are not automatically destroyed when a controller's scope or a
11392      * directive's element are destroyed.
11393      * You should take this into consideration and make sure to always cancel the interval at the
11394      * appropriate moment.  See the example below for more details on how and when to do this.
11395      * </div>
11396      *
11397      * @param {function()} fn A function that should be called repeatedly.
11398      * @param {number} delay Number of milliseconds between each function call.
11399      * @param {number=} [count=0] Number of times to repeat. If not set, or 0, will repeat
11400      *   indefinitely.
11401      * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
11402      *   will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
11403      * @param {...*=} Pass additional parameters to the executed function.
11404      * @returns {promise} A promise which will be notified on each iteration.
11405      *
11406      * @example
11407      * <example module="intervalExample">
11408      * <file name="index.html">
11409      *   <script>
11410      *     angular.module('intervalExample', [])
11411      *       .controller('ExampleController', ['$scope', '$interval',
11412      *         function($scope, $interval) {
11413      *           $scope.format = 'M/d/yy h:mm:ss a';
11414      *           $scope.blood_1 = 100;
11415      *           $scope.blood_2 = 120;
11416      *
11417      *           var stop;
11418      *           $scope.fight = function() {
11419      *             // Don't start a new fight if we are already fighting
11420      *             if ( angular.isDefined(stop) ) return;
11421      *
11422      *             stop = $interval(function() {
11423      *               if ($scope.blood_1 > 0 && $scope.blood_2 > 0) {
11424      *                 $scope.blood_1 = $scope.blood_1 - 3;
11425      *                 $scope.blood_2 = $scope.blood_2 - 4;
11426      *               } else {
11427      *                 $scope.stopFight();
11428      *               }
11429      *             }, 100);
11430      *           };
11431      *
11432      *           $scope.stopFight = function() {
11433      *             if (angular.isDefined(stop)) {
11434      *               $interval.cancel(stop);
11435      *               stop = undefined;
11436      *             }
11437      *           };
11438      *
11439      *           $scope.resetFight = function() {
11440      *             $scope.blood_1 = 100;
11441      *             $scope.blood_2 = 120;
11442      *           };
11443      *
11444      *           $scope.$on('$destroy', function() {
11445      *             // Make sure that the interval is destroyed too
11446      *             $scope.stopFight();
11447      *           });
11448      *         }])
11449      *       // Register the 'myCurrentTime' directive factory method.
11450      *       // We inject $interval and dateFilter service since the factory method is DI.
11451      *       .directive('myCurrentTime', ['$interval', 'dateFilter',
11452      *         function($interval, dateFilter) {
11453      *           // return the directive link function. (compile function not needed)
11454      *           return function(scope, element, attrs) {
11455      *             var format,  // date format
11456      *                 stopTime; // so that we can cancel the time updates
11457      *
11458      *             // used to update the UI
11459      *             function updateTime() {
11460      *               element.text(dateFilter(new Date(), format));
11461      *             }
11462      *
11463      *             // watch the expression, and update the UI on change.
11464      *             scope.$watch(attrs.myCurrentTime, function(value) {
11465      *               format = value;
11466      *               updateTime();
11467      *             });
11468      *
11469      *             stopTime = $interval(updateTime, 1000);
11470      *
11471      *             // listen on DOM destroy (removal) event, and cancel the next UI update
11472      *             // to prevent updating time after the DOM element was removed.
11473      *             element.on('$destroy', function() {
11474      *               $interval.cancel(stopTime);
11475      *             });
11476      *           }
11477      *         }]);
11478      *   </script>
11479      *
11480      *   <div>
11481      *     <div ng-controller="ExampleController">
11482      *       <label>Date format: <input ng-model="format"></label> <hr/>
11483      *       Current time is: <span my-current-time="format"></span>
11484      *       <hr/>
11485      *       Blood 1 : <font color='red'>{{blood_1}}</font>
11486      *       Blood 2 : <font color='red'>{{blood_2}}</font>
11487      *       <button type="button" data-ng-click="fight()">Fight</button>
11488      *       <button type="button" data-ng-click="stopFight()">StopFight</button>
11489      *       <button type="button" data-ng-click="resetFight()">resetFight</button>
11490      *     </div>
11491      *   </div>
11492      *
11493      * </file>
11494      * </example>
11495      */
11496    function interval(fn, delay, count, invokeApply) {
11497      var hasParams = arguments.length > 4,
11498          args = hasParams ? sliceArgs(arguments, 4) : [],
11499          setInterval = $window.setInterval,
11500          clearInterval = $window.clearInterval,
11501          iteration = 0,
11502          skipApply = (isDefined(invokeApply) && !invokeApply),
11503          deferred = (skipApply ? $$q : $q).defer(),
11504          promise = deferred.promise;
11505
11506      count = isDefined(count) ? count : 0;
11507
11508      promise.then(null, null, (!hasParams) ? fn : function() {
11509        fn.apply(null, args);
11510      });
11511
11512      promise.$$intervalId = setInterval(function tick() {
11513        deferred.notify(iteration++);
11514
11515        if (count > 0 && iteration >= count) {
11516          deferred.resolve(iteration);
11517          clearInterval(promise.$$intervalId);
11518          delete intervals[promise.$$intervalId];
11519        }
11520
11521        if (!skipApply) $rootScope.$apply();
11522
11523      }, delay);
11524
11525      intervals[promise.$$intervalId] = deferred;
11526
11527      return promise;
11528    }
11529
11530
11531     /**
11532      * @ngdoc method
11533      * @name $interval#cancel
11534      *
11535      * @description
11536      * Cancels a task associated with the `promise`.
11537      *
11538      * @param {Promise=} promise returned by the `$interval` function.
11539      * @returns {boolean} Returns `true` if the task was successfully canceled.
11540      */
11541    interval.cancel = function(promise) {
11542      if (promise && promise.$$intervalId in intervals) {
11543        intervals[promise.$$intervalId].reject('canceled');
11544        $window.clearInterval(promise.$$intervalId);
11545        delete intervals[promise.$$intervalId];
11546        return true;
11547      }
11548      return false;
11549    };
11550
11551    return interval;
11552  }];
11553}
11554
11555/**
11556 * @ngdoc service
11557 * @name $locale
11558 *
11559 * @description
11560 * $locale service provides localization rules for various Angular components. As of right now the
11561 * only public api is:
11562 *
11563 * * `id` – `{string}` – locale id formatted as `languageId-countryId` (e.g. `en-us`)
11564 */
11565
11566var PATH_MATCH = /^([^\?#]*)(\?([^#]*))?(#(.*))?$/,
11567    DEFAULT_PORTS = {'http': 80, 'https': 443, 'ftp': 21};
11568var $locationMinErr = minErr('$location');
11569
11570
11571/**
11572 * Encode path using encodeUriSegment, ignoring forward slashes
11573 *
11574 * @param {string} path Path to encode
11575 * @returns {string}
11576 */
11577function encodePath(path) {
11578  var segments = path.split('/'),
11579      i = segments.length;
11580
11581  while (i--) {
11582    segments[i] = encodeUriSegment(segments[i]);
11583  }
11584
11585  return segments.join('/');
11586}
11587
11588function parseAbsoluteUrl(absoluteUrl, locationObj) {
11589  var parsedUrl = urlResolve(absoluteUrl);
11590
11591  locationObj.$$protocol = parsedUrl.protocol;
11592  locationObj.$$host = parsedUrl.hostname;
11593  locationObj.$$port = toInt(parsedUrl.port) || DEFAULT_PORTS[parsedUrl.protocol] || null;
11594}
11595
11596
11597function parseAppUrl(relativeUrl, locationObj) {
11598  var prefixed = (relativeUrl.charAt(0) !== '/');
11599  if (prefixed) {
11600    relativeUrl = '/' + relativeUrl;
11601  }
11602  var match = urlResolve(relativeUrl);
11603  locationObj.$$path = decodeURIComponent(prefixed && match.pathname.charAt(0) === '/' ?
11604      match.pathname.substring(1) : match.pathname);
11605  locationObj.$$search = parseKeyValue(match.search);
11606  locationObj.$$hash = decodeURIComponent(match.hash);
11607
11608  // make sure path starts with '/';
11609  if (locationObj.$$path && locationObj.$$path.charAt(0) != '/') {
11610    locationObj.$$path = '/' + locationObj.$$path;
11611  }
11612}
11613
11614
11615/**
11616 *
11617 * @param {string} begin
11618 * @param {string} whole
11619 * @returns {string} returns text from whole after begin or undefined if it does not begin with
11620 *                   expected string.
11621 */
11622function beginsWith(begin, whole) {
11623  if (whole.indexOf(begin) === 0) {
11624    return whole.substr(begin.length);
11625  }
11626}
11627
11628
11629function stripHash(url) {
11630  var index = url.indexOf('#');
11631  return index == -1 ? url : url.substr(0, index);
11632}
11633
11634function trimEmptyHash(url) {
11635  return url.replace(/(#.+)|#$/, '$1');
11636}
11637
11638
11639function stripFile(url) {
11640  return url.substr(0, stripHash(url).lastIndexOf('/') + 1);
11641}
11642
11643/* return the server only (scheme://host:port) */
11644function serverBase(url) {
11645  return url.substring(0, url.indexOf('/', url.indexOf('//') + 2));
11646}
11647
11648
11649/**
11650 * LocationHtml5Url represents an url
11651 * This object is exposed as $location service when HTML5 mode is enabled and supported
11652 *
11653 * @constructor
11654 * @param {string} appBase application base URL
11655 * @param {string} appBaseNoFile application base URL stripped of any filename
11656 * @param {string} basePrefix url path prefix
11657 */
11658function LocationHtml5Url(appBase, appBaseNoFile, basePrefix) {
11659  this.$$html5 = true;
11660  basePrefix = basePrefix || '';
11661  parseAbsoluteUrl(appBase, this);
11662
11663
11664  /**
11665   * Parse given html5 (regular) url string into properties
11666   * @param {string} url HTML5 url
11667   * @private
11668   */
11669  this.$$parse = function(url) {
11670    var pathUrl = beginsWith(appBaseNoFile, url);
11671    if (!isString(pathUrl)) {
11672      throw $locationMinErr('ipthprfx', 'Invalid url "{0}", missing path prefix "{1}".', url,
11673          appBaseNoFile);
11674    }
11675
11676    parseAppUrl(pathUrl, this);
11677
11678    if (!this.$$path) {
11679      this.$$path = '/';
11680    }
11681
11682    this.$$compose();
11683  };
11684
11685  /**
11686   * Compose url and update `absUrl` property
11687   * @private
11688   */
11689  this.$$compose = function() {
vendor: 4,177 bytes, lines 11690-11821
11690    var search = toKeyValue(this.$$search),
11691        hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
11692
11693    this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
11694    this.$$absUrl = appBaseNoFile + this.$$url.substr(1); // first char is always '/'
11695  };
11696
11697  this.$$parseLinkUrl = function(url, relHref) {
11698    if (relHref && relHref[0] === '#') {
11699      // special case for links to hash fragments:
11700      // keep the old url and only replace the hash fragment
11701      this.hash(relHref.slice(1));
11702      return true;
11703    }
11704    var appUrl, prevAppUrl;
11705    var rewrittenUrl;
11706
11707    if (isDefined(appUrl = beginsWith(appBase, url))) {
11708      prevAppUrl = appUrl;
11709      if (isDefined(appUrl = beginsWith(basePrefix, appUrl))) {
11710        rewrittenUrl = appBaseNoFile + (beginsWith('/', appUrl) || appUrl);
11711      } else {
11712        rewrittenUrl = appBase + prevAppUrl;
11713      }
11714    } else if (isDefined(appUrl = beginsWith(appBaseNoFile, url))) {
11715      rewrittenUrl = appBaseNoFile + appUrl;
11716    } else if (appBaseNoFile == url + '/') {
11717      rewrittenUrl = appBaseNoFile;
11718    }
11719    if (rewrittenUrl) {
11720      this.$$parse(rewrittenUrl);
11721    }
11722    return !!rewrittenUrl;
11723  };
11724}
11725
11726
11727/**
11728 * LocationHashbangUrl represents url
11729 * This object is exposed as $location service when developer doesn't opt into html5 mode.
11730 * It also serves as the base class for html5 mode fallback on legacy browsers.
11731 *
11732 * @constructor
11733 * @param {string} appBase application base URL
11734 * @param {string} appBaseNoFile application base URL stripped of any filename
11735 * @param {string} hashPrefix hashbang prefix
11736 */
11737function LocationHashbangUrl(appBase, appBaseNoFile, hashPrefix) {
11738
11739  parseAbsoluteUrl(appBase, this);
11740
11741
11742  /**
11743   * Parse given hashbang url into properties
11744   * @param {string} url Hashbang url
11745   * @private
11746   */
11747  this.$$parse = function(url) {
11748    var withoutBaseUrl = beginsWith(appBase, url) || beginsWith(appBaseNoFile, url);
11749    var withoutHashUrl;
11750
11751    if (!isUndefined(withoutBaseUrl) && withoutBaseUrl.charAt(0) === '#') {
11752
11753      // The rest of the url starts with a hash so we have
11754      // got either a hashbang path or a plain hash fragment
11755      withoutHashUrl = beginsWith(hashPrefix, withoutBaseUrl);
11756      if (isUndefined(withoutHashUrl)) {
11757        // There was no hashbang prefix so we just have a hash fragment
11758        withoutHashUrl = withoutBaseUrl;
11759      }
11760
11761    } else {
11762      // There was no hashbang path nor hash fragment:
11763      // If we are in HTML5 mode we use what is left as the path;
11764      // Otherwise we ignore what is left
11765      if (this.$$html5) {
11766        withoutHashUrl = withoutBaseUrl;
11767      } else {
11768        withoutHashUrl = '';
11769        if (isUndefined(withoutBaseUrl)) {
11770          appBase = url;
11771          this.replace();
11772        }
11773      }
11774    }
11775
11776    parseAppUrl(withoutHashUrl, this);
11777
11778    this.$$path = removeWindowsDriveName(this.$$path, withoutHashUrl, appBase);
11779
11780    this.$$compose();
11781
11782    /*
11783     * In Windows, on an anchor node on documents loaded from
11784     * the filesystem, the browser will return a pathname
11785     * prefixed with the drive name ('/C:/path') when a
11786     * pathname without a drive is set:
11787     *  * a.setAttribute('href', '/foo')
11788     *   * a.pathname === '/C:/foo' //true
11789     *
11790     * Inside of Angular, we're always using pathnames that
11791     * do not include drive names for routing.
11792     */
11793    function removeWindowsDriveName(path, url, base) {
11794      /*
11795      Matches paths for file protocol on windows,
11796      such as /C:/foo/bar, and captures only /foo/bar.
11797      */
11798      var windowsFilePathExp = /^\/[A-Z]:(\/.*)/;
11799
11800      var firstPathSegmentMatch;
11801
11802      //Get the relative path from the input URL.
11803      if (url.indexOf(base) === 0) {
11804        url = url.replace(base, '');
11805      }
11806
11807      // The input URL intentionally contains a first path segment that ends with a colon.
11808      if (windowsFilePathExp.exec(url)) {
11809        return path;
11810      }
11811
11812      firstPathSegmentMatch = windowsFilePathExp.exec(path);
11813      return firstPathSegmentMatch ? firstPathSegmentMatch[1] : path;
11814    }
11815  };
11816
11817  /**
11818   * Compose hashbang url and update `absUrl` property
11819   * @private
11820   */
11821  this.$$compose = function() {
11822    var search = toKeyValue(this.$$search),
11823        hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
11824
11825    this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
11826    this.$$absUrl = appBase + (this.$$url ? hashPrefix + this.$$url : '');
11827  };
11828
11829  this.$$parseLinkUrl = function(url, relHref) {
11830    if (stripHash(appBase) == stripHash(url)) {
11831      this.$$parse(url);
11832      return true;
11833    }
11834    return false;
11835  };
11836}
11837
11838
11839/**
11840 * LocationHashbangUrl represents url
11841 * This object is exposed as $location service when html5 history api is enabled but the browser
11842 * does not support it.
11843 *
11844 * @constructor
11845 * @param {string} appBase application base URL
11846 * @param {string} appBaseNoFile application base URL stripped of any filename
11847 * @param {string} hashPrefix hashbang prefix
11848 */
11849function LocationHashbangInHtml5Url(appBase, appBaseNoFile, hashPrefix) {
11850  this.$$html5 = true;
11851  LocationHashbangUrl.apply(this, arguments);
11852
11853  this.$$parseLinkUrl = function(url, relHref) {
11854    if (relHref && relHref[0] === '#') {
11855      // special case for links to hash fragments:
11856      // keep the old url and only replace the hash fragment
11857      this.hash(relHref.slice(1));
11858      return true;
11859    }
11860
11861    var rewrittenUrl;
11862    var appUrl;
11863
11864    if (appBase == stripHash(url)) {
11865      rewrittenUrl = url;
11866    } else if ((appUrl = beginsWith(appBaseNoFile, url))) {
11867      rewrittenUrl = appBase + hashPrefix + appUrl;
11868    } else if (appBaseNoFile === url + '/') {
11869      rewrittenUrl = appBaseNoFile;
11870    }
11871    if (rewrittenUrl) {
11872      this.$$parse(rewrittenUrl);
11873    }
11874    return !!rewrittenUrl;
11875  };
11876
11877  this.$$compose = function() {
11878    var search = toKeyValue(this.$$search),
11879        hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
11880
11881    this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
11882    // include hashPrefix in $$absUrl when $$url is empty so IE9 does not reload page because of removal of '#'
11883    this.$$absUrl = appBase + hashPrefix + this.$$url;
11884  };
11885
11886}
11887
11888
11889var locationPrototype = {
11890
11891  /**
11892   * Are we in html5 mode?
11893   * @private
11894   */
11895  $$html5: false,
11896
11897  /**
11898   * Has any change been replacing?
11899   * @private
11900   */
11901  $$replace: false,
11902
11903  /**
11904   * @ngdoc method
11905   * @name $location#absUrl
11906   *
11907   * @description
11908   * This method is getter only.
11909   *
11910   * Return full url representation with all segments encoded according to rules specified in
11911   * [RFC 3986](http://www.ietf.org/rfc/rfc3986.txt).
11912   *
11913   *
11914   * ```js
11915   * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
11916   * var absUrl = $location.absUrl();
11917   * // => "http://example.com/#/some/path?foo=bar&baz=xoxo"
11918   * ```
11919   *
11920   * @return {string} full url
11921   */
11922  absUrl: locationGetter('$$absUrl'),
11923
11924  /**
11925   * @ngdoc method
11926   * @name $location#url
11927   *
11928   * @description
11929   * This method is getter / setter.
11930   *
11931   * Return url (e.g. `/path?a=b#hash`) when called without any parameter.
11932   *
11933   * Change path, search and hash, when called with parameter and return `$location`.
11934   *
11935   *
11936   * ```js
11937   * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
11938   * var url = $location.url();
11939   * // => "/some/path?foo=bar&baz=xoxo"
11940   * ```
11941   *
11942   * @param {string=} url New url without base prefix (e.g. `/path?a=b#hash`)
11943   * @return {string} url
11944   */
11945  url: function(url) {
11946    if (isUndefined(url)) {
11947      return this.$$url;
11948    }
11949
11950    var match = PATH_MATCH.exec(url);
11951    if (match[1] || url === '') this.path(decodeURIComponent(match[1]));
11952    if (match[2] || match[1] || url === '') this.search(match[3] || '');
11953    this.hash(match[5] || '');
11954
11955    return this;
11956  },
11957
11958  /**
11959   * @ngdoc method
11960   * @name $location#protocol
11961   *
11962   * @description
11963   * This method is getter only.
11964   *
11965   * Return protocol of current url.
11966   *
11967   *
11968   * ```js
11969   * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
11970   * var protocol = $location.protocol();
11971   * // => "http"
11972   * ```
11973   *
11974   * @return {string} protocol of current url
11975   */
11976  protocol: locationGetter('$$protocol'),
11977
11978  /**
11979   * @ngdoc method
11980   * @name $location#host
11981   *
11982   * @description
11983   * This method is getter only.
11984   *
11985   * Return host of current url.
11986   *
11987   * Note: compared to the non-angular version `location.host` which returns `hostname:port`, this returns the `hostname` portion only.
11988   *
11989   *
11990   * ```js
11991   * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
11992   * var host = $location.host();
11993   * // => "example.com"
11994   *
11995   * // given url http://user:[email protected]:8080/#/some/path?foo=bar&baz=xoxo
11996   * host = $location.host();
11997   * // => "example.com"
11998   * host = location.host;
11999   * // => "example.com:8080"
12000   * ```
12001   *
12002   * @return {string} host of current url.
12003   */
12004  host: locationGetter('$$host'),
12005
12006  /**
12007   * @ngdoc method
12008   * @name $location#port
12009   *
12010   * @description
12011   * This method is getter only.
12012   *
12013   * Return port of current url.
12014   *
12015   *
12016   * ```js
12017   * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
12018   * var port = $location.port();
12019   * // => 80
12020   * ```
12021   *
12022   * @return {Number} port
12023   */
12024  port: locationGetter('$$port'),
12025
12026  /**
12027   * @ngdoc method
12028   * @name $location#path
12029   *
12030   * @description
12031   * This method is getter / setter.
12032   *
12033   * Return path of current url when called without any parameter.
12034   *
12035   * Change path when called with parameter and return `$location`.
12036   *
12037   * Note: Path should always begin with forward slash (/), this method will add the forward slash
12038   * if it is missing.
12039   *
12040   *
12041   * ```js
12042   * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
12043   * var path = $location.path();
12044   * // => "/some/path"
12045   * ```
12046   *
12047   * @param {(string|number)=} path New path
12048   * @return {string} path
12049   */
12050  path: locationGetterSetter('$$path', function(path) {
12051    path = path !== null ? path.toString() : '';
12052    return path.charAt(0) == '/' ? path : '/' + path;
12053  }),
12054
12055  /**
12056   * @ngdoc method
12057   * @name $location#search
12058   *
12059   * @description
12060   * This method is getter / setter.
12061   *
12062   * Return search part (as object) of current url when called without any parameter.
12063   *
12064   * Change search part when called with parameter and return `$location`.
12065   *
12066   *
12067   * ```js
12068   * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
12069   * var searchObject = $location.search();
12070   * // => {foo: 'bar', baz: 'xoxo'}
12071   *
12072   * // set foo to 'yipee'
12073   * $location.search('foo', 'yipee');
12074   * // $location.search() => {foo: 'yipee', baz: 'xoxo'}
12075   * ```
12076   *
12077   * @param {string|Object.<string>|Object.<Array.<string>>} search New search params - string or
12078   * hash object.
12079   *
12080   * When called with a single argument the method acts as a setter, setting the `search` component
12081   * of `$location` to the specified value.
12082   *
12083   * If the argument is a hash object containing an array of values, these values will be encoded
12084   * as duplicate search parameters in the url.
12085   *
12086   * @param {(string|Number|Array<string>|boolean)=} paramValue If `search` is a string or number, then `paramValue`
12087   * will override only a single search property.
12088   *
12089   * If `paramValue` is an array, it will override the property of the `search` component of
12090   * `$location` specified via the first argument.
12091   *
12092   * If `paramValue` is `null`, the property specified via the first argument will be deleted.
12093   *
12094   * If `paramValue` is `true`, the property specified via the first argument will be added with no
12095   * value nor trailing equal sign.
12096   *
12097   * @return {Object} If called with no arguments returns the parsed `search` object. If called with
12098   * one or more arguments returns `$location` object itself.
12099   */
12100  search: function(search, paramValue) {
12101    switch (arguments.length) {
12102      case 0:
12103        return this.$$search;
12104      case 1:
12105        if (isString(search) || isNumber(search)) {
12106          search = search.toString();
12107          this.$$search = parseKeyValue(search);
12108        } else if (isObject(search)) {
12109          search = copy(search, {});
12110          // remove object undefined or null properties
12111          forEach(search, function(value, key) {
12112            if (value == null) delete search[key];
12113          });
12114
12115          this.$$search = search;
12116        } else {
12117          throw $locationMinErr('isrcharg',
12118              'The first argument of the `$location#search()` call must be a string or an object.');
12119        }
12120        break;
12121      default:
12122        if (isUndefined(paramValue) || paramValue === null) {
12123          delete this.$$search[search];
12124        } else {
12125          this.$$search[search] = paramValue;
12126        }
12127    }
12128
12129    this.$$compose();
12130    return this;
12131  },
12132
12133  /**
12134   * @ngdoc method
12135   * @name $location#hash
12136   *
12137   * @description
12138   * This method is getter / setter.
12139   *
12140   * Return hash fragment when called without any parameter.
12141   *
12142   * Change hash fragment when called with parameter and return `$location`.
12143   *
12144   *
12145   * ```js
12146   * // given url http://example.com/#/some/path?foo=bar&baz=xoxo#hashValue
12147   * var hash = $location.hash();
12148   * // => "hashValue"
12149   * ```
12150   *
12151   * @param {(string|number)=} hash New hash fragment
12152   * @return {string} hash
12153   */
12154  hash: locationGetterSetter('$$hash', function(hash) {
12155    return hash !== null ? hash.toString() : '';
12156  }),
12157
12158  /**
12159   * @ngdoc method
12160   * @name $location#replace
12161   *
12162   * @description
12163   * If called, all changes to $location during current `$digest` will be replacing current history
12164   * record, instead of adding new one.
12165   */
12166  replace: function() {
12167    this.$$replace = true;
12168    return this;
12169  }
12170};
12171
12172forEach([LocationHashbangInHtml5Url, LocationHashbangUrl, LocationHtml5Url], function(Location) {
12173  Location.prototype = Object.create(locationPrototype);
12174
12175  /**
12176   * @ngdoc method
12177   * @name $location#state
12178   *
12179   * @description
12180   * This method is getter / setter.
12181   *
12182   * Return the history state object when called without any parameter.
12183   *
12184   * Change the history state object when called with one parameter and return `$location`.
12185   * The state object is later passed to `pushState` or `replaceState`.
12186   *
12187   * NOTE: This method is supported only in HTML5 mode and only in browsers supporting
12188   * the HTML5 History API (i.e. methods `pushState` and `replaceState`). If you need to support
12189   * older browsers (like IE9 or Android < 4.0), don't use this method.
12190   *
12191   * @param {object=} state State object for pushState or replaceState
12192   * @return {object} state
12193   */
12194  Location.prototype.state = function(state) {
12195    if (!arguments.length) {
12196      return this.$$state;
12197    }
12198
12199    if (Location !== LocationHtml5Url || !this.$$html5) {
12200      throw $locationMinErr('nostate', 'History API state support is available only ' +
12201        'in HTML5 mode and only in browsers supporting HTML5 History API');
12202    }
12203    // The user might modify `stateObject` after invoking `$location.state(stateObject)`
12204    // but we're changing the $$state reference to $browser.state() during the $digest
12205    // so the modification window is narrow.
12206    this.$$state = isUndefined(state) ? null : state;
12207
12208    return this;
12209  };
12210});
12211
12212
12213function locationGetter(property) {
12214  return function() {
12215    return this[property];
12216  };
12217}
12218
12219
12220function locationGetterSetter(property, preprocess) {
12221  return function(value) {
12222    if (isUndefined(value)) {
12223      return this[property];
12224    }
12225
12226    this[property] = preprocess(value);
12227    this.$$compose();
12228
12229    return this;
12230  };
12231}
12232
12233
12234/**
12235 * @ngdoc service
12236 * @name $location
12237 *
12238 * @requires $rootElement
12239 *
12240 * @description
12241 * The $location service parses the URL in the browser address bar (based on the
12242 * [window.location](https://developer.mozilla.org/en/window.location)) and makes the URL
12243 * available to your application. Changes to the URL in the address bar are reflected into
12244 * $location service and changes to $location are reflected into the browser address bar.
12245 *
12246 * **The $location service:**
12247 *
12248 * - Exposes the current URL in the browser address bar, so you can
12249 *   - Watch and observe the URL.
12250 *   - Change the URL.
12251 * - Synchronizes the URL with the browser when the user
12252 *   - Changes the address bar.
12253 *   - Clicks the back or forward button (or clicks a History link).
12254 *   - Clicks on a link.
12255 * - Represents the URL object as a set of methods (protocol, host, port, path, search, hash).
12256 *
12257 * For more information see {@link guide/$location Developer Guide: Using $location}
12258 */
12259
12260/**
12261 * @ngdoc provider
12262 * @name $locationProvider
12263 * @description
12264 * Use the `$locationProvider` to configure how the application deep linking paths are stored.
12265 */
12266function $LocationProvider() {
12267  var hashPrefix = '',
12268      html5Mode = {
12269        enabled: false,
12270        requireBase: true,
12271        rewriteLinks: true
12272      };
12273
12274  /**
12275   * @ngdoc method
12276   * @name $locationProvider#hashPrefix
12277   * @description
12278   * @param {string=} prefix Prefix for hash part (containing path and search)
12279   * @returns {*} current value if used as getter or itself (chaining) if used as setter
12280   */
12281  this.hashPrefix = function(prefix) {
12282    if (isDefined(prefix)) {
12283      hashPrefix = prefix;
12284      return this;
12285    } else {
12286      return hashPrefix;
12287    }
12288  };
12289
12290  /**
12291   * @ngdoc method
12292   * @name $locationProvider#html5Mode
12293   * @description
12294   * @param {(boolean|Object)=} mode If boolean, sets `html5Mode.enabled` to value.
12295   *   If object, sets `enabled`, `requireBase` and `rewriteLinks` to respective values. Supported
12296   *   properties:
12297   *   - **enabled** – `{boolean}` – (default: false) If true, will rely on `history.pushState` to
12298   *     change urls where supported. Will fall back to hash-prefixed paths in browsers that do not
12299   *     support `pushState`.
12300   *   - **requireBase** - `{boolean}` - (default: `true`) When html5Mode is enabled, specifies
12301   *     whether or not a <base> tag is required to be present. If `enabled` and `requireBase` are
12302   *     true, and a base tag is not present, an error will be thrown when `$location` is injected.
12303   *     See the {@link guide/$location $location guide for more information}
12304   *   - **rewriteLinks** - `{boolean}` - (default: `true`) When html5Mode is enabled,
12305   *     enables/disables url rewriting for relative links.
12306   *
12307   * @returns {Object} html5Mode object if used as getter or itself (chaining) if used as setter
12308   */
12309  this.html5Mode = function(mode) {
12310    if (isBoolean(mode)) {
12311      html5Mode.enabled = mode;
12312      return this;
12313    } else if (isObject(mode)) {
12314
12315      if (isBoolean(mode.enabled)) {
12316        html5Mode.enabled = mode.enabled;
12317      }
12318
12319      if (isBoolean(mode.requireBase)) {
12320        html5Mode.requireBase = mode.requireBase;
12321      }
12322
12323      if (isBoolean(mode.rewriteLinks)) {
12324        html5Mode.rewriteLinks = mode.rewriteLinks;
12325      }
12326
12327      return this;
12328    } else {
12329      return html5Mode;
12330    }
12331  };
12332
12333  /**
12334   * @ngdoc event
12335   * @name $location#$locationChangeStart
12336   * @eventType broadcast on root scope
12337   * @description
12338   * Broadcasted before a URL will change.
12339   *
12340   * This change can be prevented by calling
12341   * `preventDefault` method of the event. See {@link ng.$rootScope.Scope#$on} for more
12342   * details about event object. Upon successful change
12343   * {@link ng.$location#$locationChangeSuccess $locationChangeSuccess} is fired.
12344   *
12345   * The `newState` and `oldState` parameters may be defined only in HTML5 mode and when
12346   * the browser supports the HTML5 History API.
12347   *
12348   * @param {Object} angularEvent Synthetic event object.
12349   * @param {string} newUrl New URL
12350   * @param {string=} oldUrl URL that was before it was changed.
12351   * @param {string=} newState New history state object
12352   * @param {string=} oldState History state object that was before it was changed.
12353   */
12354
12355  /**
12356   * @ngdoc event
12357   * @name $location#$locationChangeSuccess
12358   * @eventType broadcast on root scope
12359   * @description
12360   * Broadcasted after a URL was changed.
12361   *
12362   * The `newState` and `oldState` parameters may be defined only in HTML5 mode and when
12363   * the browser supports the HTML5 History API.
12364   *
12365   * @param {Object} angularEvent Synthetic event object.
12366   * @param {string} newUrl New URL
12367   * @param {string=} oldUrl URL that was before it was changed.
12368   * @param {string=} newState New history state object
12369   * @param {string=} oldState History state object that was before it was changed.
12370   */
12371
12372  this.$get = ['$rootScope', '$browser', '$sniffer', '$rootElement', '$window',
12373      function($rootScope, $browser, $sniffer, $rootElement, $window) {
12374    var $location,
12375        LocationMode,
12376        baseHref = $browser.baseHref(), // if base[href] is undefined, it defaults to ''
12377        initialUrl = $browser.url(),
12378        appBase;
12379
12380    if (html5Mode.enabled) {
12381      if (!baseHref && html5Mode.requireBase) {
12382        throw $locationMinErr('nobase',
12383          "$location in HTML5 mode requires a <base> tag to be present!");
12384      }
12385      appBase = serverBase(initialUrl) + (baseHref || '/');
12386      LocationMode = $sniffer.history ? LocationHtml5Url : LocationHashbangInHtml5Url;
12387    } else {
12388      appBase = stripHash(initialUrl);
12389      LocationMode = LocationHashbangUrl;
12390    }
12391    var appBaseNoFile = stripFile(appBase);
12392
12393    $location = new LocationMode(appBase, appBaseNoFile, '#' + hashPrefix);
12394    $location.$$parseLinkUrl(initialUrl, initialUrl);
12395
12396    $location.$$state = $browser.state();
12397
12398    var IGNORE_URI_REGEXP = /^\s*(javascript|mailto):/i;
12399
12400    function setBrowserUrlWithFallback(url, replace, state) {
12401      var oldUrl = $location.url();
12402      var oldState = $location.$$state;
12403      try {
12404        $browser.url(url, replace, state);
12405
12406        // Make sure $location.state() returns referentially identical (not just deeply equal)
12407        // state object; this makes possible quick checking if the state changed in the digest
12408        // loop. Checking deep equality would be too expensive.
12409        $location.$$state = $browser.state();
12410      } catch (e) {
12411        // Restore old values if pushState fails
12412        $location.url(oldUrl);
12413        $location.$$state = oldState;
12414
12415        throw e;
12416      }
12417    }
12418
12419    $rootElement.on('click', function(event) {
12420      // TODO(vojta): rewrite link when opening in new tab/window (in legacy browser)
12421      // currently we open nice url link and redirect then
12422
12423      if (!html5Mode.rewriteLinks || event.ctrlKey || event.metaKey || event.shiftKey || event.which == 2 || event.button == 2) return;
12424
12425      var elm = jqLite(event.target);
12426
12427      // traverse the DOM up to find first A tag
12428      while (nodeName_(elm[0]) !== 'a') {
12429        // ignore rewriting if no A tag (reached root element, or no parent - removed from document)
12430        if (elm[0] === $rootElement[0] || !(elm = elm.parent())[0]) return;
12431      }
12432
12433      var absHref = elm.prop('href');
12434      // get the actual href attribute - see
12435      // http://msdn.microsoft.com/en-us/library/ie/dd347148(v=vs.85).aspx
12436      var relHref = elm.attr('href') || elm.attr('xlink:href');
12437
12438      if (isObject(absHref) && absHref.toString() === '[object SVGAnimatedString]') {
12439        // SVGAnimatedString.animVal should be identical to SVGAnimatedString.baseVal, unless during
12440        // an animation.
12441        absHref = urlResolve(absHref.animVal).href;
12442      }
12443
12444      // Ignore when url is started with javascript: or mailto:
12445      if (IGNORE_URI_REGEXP.test(absHref)) return;
12446
12447      if (absHref && !elm.attr('target') && !event.isDefaultPrevented()) {
12448        if ($location.$$parseLinkUrl(absHref, relHref)) {
12449          // We do a preventDefault for all urls that are part of the angular application,
12450          // in html5mode and also without, so that we are able to abort navigation without
12451          // getting double entries in the location history.
12452          event.preventDefault();
12453          // update location manually
12454          if ($location.absUrl() != $browser.url()) {
12455            $rootScope.$apply();
12456            // hack to work around FF6 bug 684208 when scenario runner clicks on links
12457            $window.angular['ff-684208-preventDefault'] = true;
12458          }
12459        }
12460      }
12461    });
12462
12463
12464    // rewrite hashbang url <> html5 url
12465    if (trimEmptyHash($location.absUrl()) != trimEmptyHash(initialUrl)) {
12466      $browser.url($location.absUrl(), true);
12467    }
12468
12469    var initializing = true;
12470
12471    // update $location when $browser url changes
12472    $browser.onUrlChange(function(newUrl, newState) {
12473
12474      if (isUndefined(beginsWith(appBaseNoFile, newUrl))) {
12475        // If we are navigating outside of the app then force a reload
12476        $window.location.href = newUrl;
12477        return;
12478      }
12479
12480      $rootScope.$evalAsync(function() {
12481        var oldUrl = $location.absUrl();
12482        var oldState = $location.$$state;
12483        var defaultPrevented;
12484
12485        $location.$$parse(newUrl);
12486        $location.$$state = newState;
12487
12488        defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
12489            newState, oldState).defaultPrevented;
12490
12491        // if the location was changed by a `$locationChangeStart` handler then stop
12492        // processing this location change
12493        if ($location.absUrl() !== newUrl) return;
12494
12495        if (defaultPrevented) {
12496          $location.$$parse(oldUrl);
12497          $location.$$state = oldState;
12498          setBrowserUrlWithFallback(oldUrl, false, oldState);
12499        } else {
12500          initializing = false;
vendor: 4,210 bytes, lines 12501-12643
12501          afterLocationChange(oldUrl, oldState);
12502        }
12503      });
12504      if (!$rootScope.$$phase) $rootScope.$digest();
12505    });
12506
12507    // update browser
12508    $rootScope.$watch(function $locationWatch() {
12509      var oldUrl = trimEmptyHash($browser.url());
12510      var newUrl = trimEmptyHash($location.absUrl());
12511      var oldState = $browser.state();
12512      var currentReplace = $location.$$replace;
12513      var urlOrStateChanged = oldUrl !== newUrl ||
12514        ($location.$$html5 && $sniffer.history && oldState !== $location.$$state);
12515
12516      if (initializing || urlOrStateChanged) {
12517        initializing = false;
12518
12519        $rootScope.$evalAsync(function() {
12520          var newUrl = $location.absUrl();
12521          var defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
12522              $location.$$state, oldState).defaultPrevented;
12523
12524          // if the location was changed by a `$locationChangeStart` handler then stop
12525          // processing this location change
12526          if ($location.absUrl() !== newUrl) return;
12527
12528          if (defaultPrevented) {
12529            $location.$$parse(oldUrl);
12530            $location.$$state = oldState;
12531          } else {
12532            if (urlOrStateChanged) {
12533              setBrowserUrlWithFallback(newUrl, currentReplace,
12534                                        oldState === $location.$$state ? null : $location.$$state);
12535            }
12536            afterLocationChange(oldUrl, oldState);
12537          }
12538        });
12539      }
12540
12541      $location.$$replace = false;
12542
12543      // we don't need to return anything because $evalAsync will make the digest loop dirty when
12544      // there is a change
12545    });
12546
12547    return $location;
12548
12549    function afterLocationChange(oldUrl, oldState) {
12550      $rootScope.$broadcast('$locationChangeSuccess', $location.absUrl(), oldUrl,
12551        $location.$$state, oldState);
12552    }
12553}];
12554}
12555
12556/**
12557 * @ngdoc service
12558 * @name $log
12559 * @requires $window
12560 *
12561 * @description
12562 * Simple service for logging. Default implementation safely writes the message
12563 * into the browser's console (if present).
12564 *
12565 * The main purpose of this service is to simplify debugging and troubleshooting.
12566 *
12567 * The default is to log `debug` messages. You can use
12568 * {@link ng.$logProvider ng.$logProvider#debugEnabled} to change this.
12569 *
12570 * @example
12571   <example module="logExample">
12572     <file name="script.js">
12573       angular.module('logExample', [])
12574         .controller('LogController', ['$scope', '$log', function($scope, $log) {
12575           $scope.$log = $log;
12576           $scope.message = 'Hello World!';
12577         }]);
12578     </file>
12579     <file name="index.html">
12580       <div ng-controller="LogController">
12581         <p>Reload this page with open console, enter text and hit the log button...</p>
12582         <label>Message:
12583         <input type="text" ng-model="message" /></label>
12584         <button ng-click="$log.log(message)">log</button>
12585         <button ng-click="$log.warn(message)">warn</button>
12586         <button ng-click="$log.info(message)">info</button>
12587         <button ng-click="$log.error(message)">error</button>
12588         <button ng-click="$log.debug(message)">debug</button>
12589       </div>
12590     </file>
12591   </example>
12592 */
12593
12594/**
12595 * @ngdoc provider
12596 * @name $logProvider
12597 * @description
12598 * Use the `$logProvider` to configure how the application logs messages
12599 */
12600function $LogProvider() {
12601  var debug = true,
12602      self = this;
12603
12604  /**
12605   * @ngdoc method
12606   * @name $logProvider#debugEnabled
12607   * @description
12608   * @param {boolean=} flag enable or disable debug level messages
12609   * @returns {*} current value if used as getter or itself (chaining) if used as setter
12610   */
12611  this.debugEnabled = function(flag) {
12612    if (isDefined(flag)) {
12613      debug = flag;
12614    return this;
12615    } else {
12616      return debug;
12617    }
12618  };
12619
12620  this.$get = ['$window', function($window) {
12621    return {
12622      /**
12623       * @ngdoc method
12624       * @name $log#log
12625       *
12626       * @description
12627       * Write a log message
12628       */
12629      log: consoleLog('log'),
12630
12631      /**
12632       * @ngdoc method
12633       * @name $log#info
12634       *
12635       * @description
12636       * Write an information message
12637       */
12638      info: consoleLog('info'),
12639
12640      /**
12641       * @ngdoc method
12642       * @name $log#warn
12643       *
12644       * @description
12645       * Write a warning message
12646       */
12647      warn: consoleLog('warn'),
12648
12649      /**
12650       * @ngdoc method
12651       * @name $log#error
12652       *
12653       * @description
12654       * Write an error message
12655       */
12656      error: consoleLog('error'),
12657
12658      /**
12659       * @ngdoc method
12660       * @name $log#debug
12661       *
12662       * @description
12663       * Write a debug message
12664       */
12665      debug: (function() {
12666        var fn = consoleLog('debug');
12667
12668        return function() {
12669          if (debug) {
12670            fn.apply(self, arguments);
12671          }
12672        };
12673      }())
12674    };
12675
12676    function formatError(arg) {
12677      if (arg instanceof Error) {
12678        if (arg.stack) {
12679          arg = (arg.message && arg.stack.indexOf(arg.message) === -1)
12680              ? 'Error: ' + arg.message + '\n' + arg.stack
12681              : arg.stack;
12682        } else if (arg.sourceURL) {
12683          arg = arg.message + '\n' + arg.sourceURL + ':' + arg.line;
12684        }
12685      }
12686      return arg;
12687    }
12688
12689    function consoleLog(type) {
12690      var console = $window.console || {},
12691          logFn = console[type] || console.log || noop,
12692          hasApply = false;
12693
12694      // Note: reading logFn.apply throws an error in IE11 in IE8 document mode.
12695      // The reason behind this is that console.log has type "object" in IE8...
12696      try {
12697        hasApply = !!logFn.apply;
12698      } catch (e) {}
12699
12700      if (hasApply) {
12701        return function() {
12702          var args = [];
12703          forEach(arguments, function(arg) {
12704            args.push(formatError(arg));
12705          });
12706          return logFn.apply(console, args);
12707        };
12708      }
12709
12710      // we are IE which either doesn't have window.console => this is noop and we do nothing,
12711      // or we are IE where console.log doesn't have apply so we log at least first 2 args
12712      return function(arg1, arg2) {
12713        logFn(arg1, arg2 == null ? '' : arg2);
12714      };
12715    }
12716  }];
12717}
12718
12719/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
12720 *     Any commits to this file should be reviewed with security in mind.  *
12721 *   Changes to this file can potentially create security vulnerabilities. *
12722 *          An approval from 2 Core members with history of modifying      *
12723 *                         this file is required.                          *
12724 *                                                                         *
12725 *  Does the change somehow allow for arbitrary javascript to be executed? *
12726 *    Or allows for someone to change the prototype of built-in objects?   *
12727 *     Or gives undesired access to variables likes document or window?    *
12728 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
12729
12730var $parseMinErr = minErr('$parse');
12731
12732// Sandboxing Angular Expressions
12733// ------------------------------
12734// Angular expressions are generally considered safe because these expressions only have direct
12735// access to `$scope` and locals. However, one can obtain the ability to execute arbitrary JS code by
12736// obtaining a reference to native JS functions such as the Function constructor.
12737//
12738// As an example, consider the following Angular expression:
12739//
12740//   {}.toString.constructor('alert("evil JS code")')
12741//
12742// This sandboxing technique is not perfect and doesn't aim to be. The goal is to prevent exploits
12743// against the expression language, but not to prevent exploits that were enabled by exposing
12744// sensitive JavaScript or browser APIs on Scope. Exposing such objects on a Scope is never a good
12745// practice and therefore we are not even trying to protect against interaction with an object
12746// explicitly exposed in this way.
12747//
12748// In general, it is not possible to access a Window object from an angular expression unless a
12749// window or some DOM object that has a reference to window is published onto a Scope.
12750// Similarly we prevent invocations of function known to be dangerous, as well as assignments to
12751// native objects.
12752//
12753// See https://docs.angularjs.org/guide/security
12754
12755
12756function ensureSafeMemberName(name, fullExpression) {
12757  if (name === "__defineGetter__" || name === "__defineSetter__"
12758      || name === "__lookupGetter__" || name === "__lookupSetter__"
12759      || name === "__proto__") {
12760    throw $parseMinErr('isecfld',
12761        'Attempting to access a disallowed field in Angular expressions! '
12762        + 'Expression: {0}', fullExpression);
12763  }
12764  return name;
12765}
12766
12767function getStringValue(name, fullExpression) {
12768  // From the JavaScript docs:
12769  // Property names must be strings. This means that non-string objects cannot be used
12770  // as keys in an object. Any non-string object, including a number, is typecasted
12771  // into a string via the toString method.
12772  //
12773  // So, to ensure that we are checking the same `name` that JavaScript would use,
12774  // we cast it to a string, if possible.
12775  // Doing `name + ''` can cause a repl error if the result to `toString` is not a string,
12776  // this is, this will handle objects that misbehave.
12777  name = name + '';
12778  if (!isString(name)) {
12779    throw $parseMinErr('iseccst',
12780        'Cannot convert object to primitive value! '
12781        + 'Expression: {0}', fullExpression);
12782  }
12783  return name;
12784}
12785
12786function ensureSafeObject(obj, fullExpression) {
12787  // nifty check if obj is Function that is fast and works across iframes and other contexts
12788  if (obj) {
12789    if (obj.constructor === obj) {
12790      throw $parseMinErr('isecfn',
12791          'Referencing Function in Angular expressions is disallowed! Expression: {0}',
12792          fullExpression);
12793    } else if (// isWindow(obj)
12794        obj.window === obj) {
12795      throw $parseMinErr('isecwindow',
12796          'Referencing the Window in Angular expressions is disallowed! Expression: {0}',
12797          fullExpression);
12798    } else if (// isElement(obj)
12799        obj.children && (obj.nodeName || (obj.prop && obj.attr && obj.find))) {
12800      throw $parseMinErr('isecdom',
12801          'Referencing DOM nodes in Angular expressions is disallowed! Expression: {0}',
12802          fullExpression);
12803    } else if (// block Object so that we can't get hold of dangerous Object.* methods
12804        obj === Object) {
12805      throw $parseMinErr('isecobj',
12806          'Referencing Object in Angular expressions is disallowed! Expression: {0}',
12807          fullExpression);
12808    }
12809  }
12810  return obj;
12811}
12812
12813var CALL = Function.prototype.call;
12814var APPLY = Function.prototype.apply;
12815var BIND = Function.prototype.bind;
12816
12817function ensureSafeFunction(obj, fullExpression) {
12818  if (obj) {
12819    if (obj.constructor === obj) {
12820      throw $parseMinErr('isecfn',
12821        'Referencing Function in Angular expressions is disallowed! Expression: {0}',
12822        fullExpression);
12823    } else if (obj === CALL || obj === APPLY || obj === BIND) {
12824      throw $parseMinErr('isecff',
12825        'Referencing call, apply or bind in Angular expressions is disallowed! Expression: {0}',
12826        fullExpression);
12827    }
12828  }
12829}
12830
12831function ensureSafeAssignContext(obj, fullExpression) {
12832  if (obj) {
12833    if (obj === (0).constructor || obj === (false).constructor || obj === ''.constructor ||
12834        obj === {}.constructor || obj === [].constructor || obj === Function.constructor) {
12835      throw $parseMinErr('isecaf',
12836        'Assigning to a constructor is disallowed! Expression: {0}', fullExpression);
12837    }
12838  }
12839}
12840
12841var OPERATORS = createMap();
12842forEach('+ - * / % === !== == != < > <= >= && || ! = |'.split(' '), function(operator) { OPERATORS[operator] = true; });
12843var ESCAPE = {"n":"\n", "f":"\f", "r":"\r", "t":"\t", "v":"\v", "'":"'", '"':'"'};
12844
12845
vendor: 47,856 bytes, lines 12846-14352
12846/////////////////////////////////////////
12847
12848
12849/**
12850 * @constructor
12851 */
12852var Lexer = function(options) {
12853  this.options = options;
12854};
12855
12856Lexer.prototype = {
12857  constructor: Lexer,
12858
12859  lex: function(text) {
12860    this.text = text;
12861    this.index = 0;
12862    this.tokens = [];
12863
12864    while (this.index < this.text.length) {
12865      var ch = this.text.charAt(this.index);
12866      if (ch === '"' || ch === "'") {
12867        this.readString(ch);
12868      } else if (this.isNumber(ch) || ch === '.' && this.isNumber(this.peek())) {
12869        this.readNumber();
12870      } else if (this.isIdent(ch)) {
12871        this.readIdent();
12872      } else if (this.is(ch, '(){}[].,;:?')) {
12873        this.tokens.push({index: this.index, text: ch});
12874        this.index++;
12875      } else if (this.isWhitespace(ch)) {
12876        this.index++;
12877      } else {
12878        var ch2 = ch + this.peek();
12879        var ch3 = ch2 + this.peek(2);
12880        var op1 = OPERATORS[ch];
12881        var op2 = OPERATORS[ch2];
12882        var op3 = OPERATORS[ch3];
12883        if (op1 || op2 || op3) {
12884          var token = op3 ? ch3 : (op2 ? ch2 : ch);
12885          this.tokens.push({index: this.index, text: token, operator: true});
12886          this.index += token.length;
12887        } else {
12888          this.throwError('Unexpected next character ', this.index, this.index + 1);
12889        }
12890      }
12891    }
12892    return this.tokens;
12893  },
12894
12895  is: function(ch, chars) {
12896    return chars.indexOf(ch) !== -1;
12897  },
12898
12899  peek: function(i) {
12900    var num = i || 1;
12901    return (this.index + num < this.text.length) ? this.text.charAt(this.index + num) : false;
12902  },
12903
12904  isNumber: function(ch) {
12905    return ('0' <= ch && ch <= '9') && typeof ch === "string";
12906  },
12907
12908  isWhitespace: function(ch) {
12909    // IE treats non-breaking space as \u00A0
12910    return (ch === ' ' || ch === '\r' || ch === '\t' ||
12911            ch === '\n' || ch === '\v' || ch === '\u00A0');
12912  },
12913
12914  isIdent: function(ch) {
12915    return ('a' <= ch && ch <= 'z' ||
12916            'A' <= ch && ch <= 'Z' ||
12917            '_' === ch || ch === '$');
12918  },
12919
12920  isExpOperator: function(ch) {
12921    return (ch === '-' || ch === '+' || this.isNumber(ch));
12922  },
12923
12924  throwError: function(error, start, end) {
12925    end = end || this.index;
12926    var colStr = (isDefined(start)
12927            ? 's ' + start +  '-' + this.index + ' [' + this.text.substring(start, end) + ']'
12928            : ' ' + end);
12929    throw $parseMinErr('lexerr', 'Lexer Error: {0} at column{1} in expression [{2}].',
12930        error, colStr, this.text);
12931  },
12932
12933  readNumber: function() {
12934    var number = '';
12935    var start = this.index;
12936    while (this.index < this.text.length) {
12937      var ch = lowercase(this.text.charAt(this.index));
12938      if (ch == '.' || this.isNumber(ch)) {
12939        number += ch;
12940      } else {
12941        var peekCh = this.peek();
12942        if (ch == 'e' && this.isExpOperator(peekCh)) {
12943          number += ch;
12944        } else if (this.isExpOperator(ch) &&
12945            peekCh && this.isNumber(peekCh) &&
12946            number.charAt(number.length - 1) == 'e') {
12947          number += ch;
12948        } else if (this.isExpOperator(ch) &&
12949            (!peekCh || !this.isNumber(peekCh)) &&
12950            number.charAt(number.length - 1) == 'e') {
12951          this.throwError('Invalid exponent');
12952        } else {
12953          break;
12954        }
12955      }
12956      this.index++;
12957    }
12958    this.tokens.push({
12959      index: start,
12960      text: number,
12961      constant: true,
12962      value: Number(number)
12963    });
12964  },
12965
12966  readIdent: function() {
12967    var start = this.index;
12968    while (this.index < this.text.length) {
12969      var ch = this.text.charAt(this.index);
12970      if (!(this.isIdent(ch) || this.isNumber(ch))) {
12971        break;
12972      }
12973      this.index++;
12974    }
12975    this.tokens.push({
12976      index: start,
12977      text: this.text.slice(start, this.index),
12978      identifier: true
12979    });
12980  },
12981
12982  readString: function(quote) {
12983    var start = this.index;
12984    this.index++;
12985    var string = '';
12986    var rawString = quote;
12987    var escape = false;
12988    while (this.index < this.text.length) {
12989      var ch = this.text.charAt(this.index);
12990      rawString += ch;
12991      if (escape) {
12992        if (ch === 'u') {
12993          var hex = this.text.substring(this.index + 1, this.index + 5);
12994          if (!hex.match(/[\da-f]{4}/i)) {
12995            this.throwError('Invalid unicode escape [\\u' + hex + ']');
12996          }
12997          this.index += 4;
12998          string += String.fromCharCode(parseInt(hex, 16));
12999        } else {
13000          var rep = ESCAPE[ch];
13001          string = string + (rep || ch);
13002        }
13003        escape = false;
13004      } else if (ch === '\\') {
13005        escape = true;
13006      } else if (ch === quote) {
13007        this.index++;
13008        this.tokens.push({
13009          index: start,
13010          text: rawString,
13011          constant: true,
13012          value: string
13013        });
13014        return;
13015      } else {
13016        string += ch;
13017      }
13018      this.index++;
13019    }
13020    this.throwError('Unterminated quote', start);
13021  }
13022};
13023
13024var AST = function(lexer, options) {
13025  this.lexer = lexer;
13026  this.options = options;
13027};
13028
13029AST.Program = 'Program';
13030AST.ExpressionStatement = 'ExpressionStatement';
13031AST.AssignmentExpression = 'AssignmentExpression';
13032AST.ConditionalExpression = 'ConditionalExpression';
13033AST.LogicalExpression = 'LogicalExpression';
13034AST.BinaryExpression = 'BinaryExpression';
13035AST.UnaryExpression = 'UnaryExpression';
13036AST.CallExpression = 'CallExpression';
13037AST.MemberExpression = 'MemberExpression';
13038AST.Identifier = 'Identifier';
13039AST.Literal = 'Literal';
13040AST.ArrayExpression = 'ArrayExpression';
13041AST.Property = 'Property';
13042AST.ObjectExpression = 'ObjectExpression';
13043AST.ThisExpression = 'ThisExpression';
13044
13045// Internal use only
13046AST.NGValueParameter = 'NGValueParameter';
13047
13048AST.prototype = {
13049  ast: function(text) {
13050    this.text = text;
13051    this.tokens = this.lexer.lex(text);
13052
13053    var value = this.program();
13054
13055    if (this.tokens.length !== 0) {
13056      this.throwError('is an unexpected token', this.tokens[0]);
13057    }
13058
13059    return value;
13060  },
13061
13062  program: function() {
13063    var body = [];
13064    while (true) {
13065      if (this.tokens.length > 0 && !this.peek('}', ')', ';', ']'))
13066        body.push(this.expressionStatement());
13067      if (!this.expect(';')) {
13068        return { type: AST.Program, body: body};
13069      }
13070    }
13071  },
13072
13073  expressionStatement: function() {
13074    return { type: AST.ExpressionStatement, expression: this.filterChain() };
13075  },
13076
13077  filterChain: function() {
13078    var left = this.expression();
13079    var token;
13080    while ((token = this.expect('|'))) {
13081      left = this.filter(left);
13082    }
13083    return left;
13084  },
13085
13086  expression: function() {
13087    return this.assignment();
13088  },
13089
13090  assignment: function() {
13091    var result = this.ternary();
13092    if (this.expect('=')) {
13093      result = { type: AST.AssignmentExpression, left: result, right: this.assignment(), operator: '='};
13094    }
13095    return result;
13096  },
13097
13098  ternary: function() {
13099    var test = this.logicalOR();
13100    var alternate;
13101    var consequent;
13102    if (this.expect('?')) {
13103      alternate = this.expression();
13104      if (this.consume(':')) {
13105        consequent = this.expression();
13106        return { type: AST.ConditionalExpression, test: test, alternate: alternate, consequent: consequent};
13107      }
13108    }
13109    return test;
13110  },
13111
13112  logicalOR: function() {
13113    var left = this.logicalAND();
13114    while (this.expect('||')) {
13115      left = { type: AST.LogicalExpression, operator: '||', left: left, right: this.logicalAND() };
13116    }
13117    return left;
13118  },
13119
13120  logicalAND: function() {
13121    var left = this.equality();
13122    while (this.expect('&&')) {
13123      left = { type: AST.LogicalExpression, operator: '&&', left: left, right: this.equality()};
13124    }
13125    return left;
13126  },
13127
13128  equality: function() {
13129    var left = this.relational();
13130    var token;
13131    while ((token = this.expect('==','!=','===','!=='))) {
13132      left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.relational() };
13133    }
13134    return left;
13135  },
13136
13137  relational: function() {
13138    var left = this.additive();
13139    var token;
13140    while ((token = this.expect('<', '>', '<=', '>='))) {
13141      left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.additive() };
13142    }
13143    return left;
13144  },
13145
13146  additive: function() {
13147    var left = this.multiplicative();
13148    var token;
13149    while ((token = this.expect('+','-'))) {
13150      left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.multiplicative() };
13151    }
13152    return left;
13153  },
13154
13155  multiplicative: function() {
13156    var left = this.unary();
13157    var token;
13158    while ((token = this.expect('*','/','%'))) {
13159      left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.unary() };
13160    }
13161    return left;
13162  },
13163
13164  unary: function() {
13165    var token;
13166    if ((token = this.expect('+', '-', '!'))) {
13167      return { type: AST.UnaryExpression, operator: token.text, prefix: true, argument: this.unary() };
13168    } else {
13169      return this.primary();
13170    }
13171  },
13172
13173  primary: function() {
13174    var primary;
13175    if (this.expect('(')) {
13176      primary = this.filterChain();
13177      this.consume(')');
13178    } else if (this.expect('[')) {
13179      primary = this.arrayDeclaration();
13180    } else if (this.expect('{')) {
13181      primary = this.object();
13182    } else if (this.constants.hasOwnProperty(this.peek().text)) {
13183      primary = copy(this.constants[this.consume().text]);
13184    } else if (this.peek().identifier) {
13185      primary = this.identifier();
13186    } else if (this.peek().constant) {
13187      primary = this.constant();
13188    } else {
13189      this.throwError('not a primary expression', this.peek());
13190    }
13191
13192    var next;
13193    while ((next = this.expect('(', '[', '.'))) {
13194      if (next.text === '(') {
13195        primary = {type: AST.CallExpression, callee: primary, arguments: this.parseArguments() };
13196        this.consume(')');
13197      } else if (next.text === '[') {
13198        primary = { type: AST.MemberExpression, object: primary, property: this.expression(), computed: true };
13199        this.consume(']');
13200      } else if (next.text === '.') {
13201        primary = { type: AST.MemberExpression, object: primary, property: this.identifier(), computed: false };
13202      } else {
13203        this.throwError('IMPOSSIBLE');
13204      }
13205    }
13206    return primary;
13207  },
13208
13209  filter: function(baseExpression) {
13210    var args = [baseExpression];
13211    var result = {type: AST.CallExpression, callee: this.identifier(), arguments: args, filter: true};
13212
13213    while (this.expect(':')) {
13214      args.push(this.expression());
13215    }
13216
13217    return result;
13218  },
13219
13220  parseArguments: function() {
13221    var args = [];
13222    if (this.peekToken().text !== ')') {
13223      do {
13224        args.push(this.expression());
13225      } while (this.expect(','));
13226    }
13227    return args;
13228  },
13229
13230  identifier: function() {
13231    var token = this.consume();
13232    if (!token.identifier) {
13233      this.throwError('is not a valid identifier', token);
13234    }
13235    return { type: AST.Identifier, name: token.text };
13236  },
13237
13238  constant: function() {
13239    // TODO check that it is a constant
13240    return { type: AST.Literal, value: this.consume().value };
13241  },
13242
13243  arrayDeclaration: function() {
13244    var elements = [];
13245    if (this.peekToken().text !== ']') {
13246      do {
13247        if (this.peek(']')) {
13248          // Support trailing commas per ES5.1.
13249          break;
13250        }
13251        elements.push(this.expression());
13252      } while (this.expect(','));
13253    }
13254    this.consume(']');
13255
13256    return { type: AST.ArrayExpression, elements: elements };
13257  },
13258
13259  object: function() {
13260    var properties = [], property;
13261    if (this.peekToken().text !== '}') {
13262      do {
13263        if (this.peek('}')) {
13264          // Support trailing commas per ES5.1.
13265          break;
13266        }
13267        property = {type: AST.Property, kind: 'init'};
13268        if (this.peek().constant) {
13269          property.key = this.constant();
13270        } else if (this.peek().identifier) {
13271          property.key = this.identifier();
13272        } else {
13273          this.throwError("invalid key", this.peek());
13274        }
13275        this.consume(':');
13276        property.value = this.expression();
13277        properties.push(property);
13278      } while (this.expect(','));
13279    }
13280    this.consume('}');
13281
13282    return {type: AST.ObjectExpression, properties: properties };
13283  },
13284
13285  throwError: function(msg, token) {
13286    throw $parseMinErr('syntax',
13287        'Syntax Error: Token \'{0}\' {1} at column {2} of the expression [{3}] starting at [{4}].',
13288          token.text, msg, (token.index + 1), this.text, this.text.substring(token.index));
13289  },
13290
13291  consume: function(e1) {
13292    if (this.tokens.length === 0) {
13293      throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text);
13294    }
13295
13296    var token = this.expect(e1);
13297    if (!token) {
13298      this.throwError('is unexpected, expecting [' + e1 + ']', this.peek());
13299    }
13300    return token;
13301  },
13302
13303  peekToken: function() {
13304    if (this.tokens.length === 0) {
13305      throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text);
13306    }
13307    return this.tokens[0];
13308  },
13309
13310  peek: function(e1, e2, e3, e4) {
13311    return this.peekAhead(0, e1, e2, e3, e4);
13312  },
13313
13314  peekAhead: function(i, e1, e2, e3, e4) {
13315    if (this.tokens.length > i) {
13316      var token = this.tokens[i];
13317      var t = token.text;
13318      if (t === e1 || t === e2 || t === e3 || t === e4 ||
13319          (!e1 && !e2 && !e3 && !e4)) {
13320        return token;
13321      }
13322    }
13323    return false;
13324  },
13325
13326  expect: function(e1, e2, e3, e4) {
13327    var token = this.peek(e1, e2, e3, e4);
13328    if (token) {
13329      this.tokens.shift();
13330      return token;
13331    }
13332    return false;
13333  },
13334
13335
13336  /* `undefined` is not a constant, it is an identifier,
13337   * but using it as an identifier is not supported
13338   */
13339  constants: {
13340    'true': { type: AST.Literal, value: true },
13341    'false': { type: AST.Literal, value: false },
13342    'null': { type: AST.Literal, value: null },
13343    'undefined': {type: AST.Literal, value: undefined },
13344    'this': {type: AST.ThisExpression }
13345  }
13346};
13347
13348function ifDefined(v, d) {
13349  return typeof v !== 'undefined' ? v : d;
13350}
13351
13352function plusFn(l, r) {
13353  if (typeof l === 'undefined') return r;
13354  if (typeof r === 'undefined') return l;
13355  return l + r;
13356}
13357
13358function isStateless($filter, filterName) {
13359  var fn = $filter(filterName);
13360  return !fn.$stateful;
13361}
13362
13363function findConstantAndWatchExpressions(ast, $filter) {
13364  var allConstants;
13365  var argsToWatch;
13366  switch (ast.type) {
13367  case AST.Program:
13368    allConstants = true;
13369    forEach(ast.body, function(expr) {
13370      findConstantAndWatchExpressions(expr.expression, $filter);
13371      allConstants = allConstants && expr.expression.constant;
13372    });
13373    ast.constant = allConstants;
13374    break;
13375  case AST.Literal:
13376    ast.constant = true;
13377    ast.toWatch = [];
13378    break;
13379  case AST.UnaryExpression:
13380    findConstantAndWatchExpressions(ast.argument, $filter);
13381    ast.constant = ast.argument.constant;
13382    ast.toWatch = ast.argument.toWatch;
13383    break;
13384  case AST.BinaryExpression:
13385    findConstantAndWatchExpressions(ast.left, $filter);
13386    findConstantAndWatchExpressions(ast.right, $filter);
13387    ast.constant = ast.left.constant && ast.right.constant;
13388    ast.toWatch = ast.left.toWatch.concat(ast.right.toWatch);
13389    break;
13390  case AST.LogicalExpression:
13391    findConstantAndWatchExpressions(ast.left, $filter);
13392    findConstantAndWatchExpressions(ast.right, $filter);
13393    ast.constant = ast.left.constant && ast.right.constant;
13394    ast.toWatch = ast.constant ? [] : [ast];
13395    break;
13396  case AST.ConditionalExpression:
13397    findConstantAndWatchExpressions(ast.test, $filter);
13398    findConstantAndWatchExpressions(ast.alternate, $filter);
13399    findConstantAndWatchExpressions(ast.consequent, $filter);
13400    ast.constant = ast.test.constant && ast.alternate.constant && ast.consequent.constant;
13401    ast.toWatch = ast.constant ? [] : [ast];
13402    break;
13403  case AST.Identifier:
13404    ast.constant = false;
13405    ast.toWatch = [ast];
13406    break;
13407  case AST.MemberExpression:
13408    findConstantAndWatchExpressions(ast.object, $filter);
13409    if (ast.computed) {
13410      findConstantAndWatchExpressions(ast.property, $filter);
13411    }
13412    ast.constant = ast.object.constant && (!ast.computed || ast.property.constant);
13413    ast.toWatch = [ast];
13414    break;
13415  case AST.CallExpression:
13416    allConstants = ast.filter ? isStateless($filter, ast.callee.name) : false;
13417    argsToWatch = [];
13418    forEach(ast.arguments, function(expr) {
13419      findConstantAndWatchExpressions(expr, $filter);
13420      allConstants = allConstants && expr.constant;
13421      if (!expr.constant) {
13422        argsToWatch.push.apply(argsToWatch, expr.toWatch);
13423      }
13424    });
13425    ast.constant = allConstants;
13426    ast.toWatch = ast.filter && isStateless($filter, ast.callee.name) ? argsToWatch : [ast];
13427    break;
13428  case AST.AssignmentExpression:
13429    findConstantAndWatchExpressions(ast.left, $filter);
13430    findConstantAndWatchExpressions(ast.right, $filter);
13431    ast.constant = ast.left.constant && ast.right.constant;
13432    ast.toWatch = [ast];
13433    break;
13434  case AST.ArrayExpression:
13435    allConstants = true;
13436    argsToWatch = [];
13437    forEach(ast.elements, function(expr) {
13438      findConstantAndWatchExpressions(expr, $filter);
13439      allConstants = allConstants && expr.constant;
13440      if (!expr.constant) {
13441        argsToWatch.push.apply(argsToWatch, expr.toWatch);
13442      }
13443    });
13444    ast.constant = allConstants;
13445    ast.toWatch = argsToWatch;
13446    break;
13447  case AST.ObjectExpression:
13448    allConstants = true;
13449    argsToWatch = [];
13450    forEach(ast.properties, function(property) {
13451      findConstantAndWatchExpressions(property.value, $filter);
13452      allConstants = allConstants && property.value.constant;
13453      if (!property.value.constant) {
13454        argsToWatch.push.apply(argsToWatch, property.value.toWatch);
13455      }
13456    });
13457    ast.constant = allConstants;
13458    ast.toWatch = argsToWatch;
13459    break;
13460  case AST.ThisExpression:
13461    ast.constant = false;
13462    ast.toWatch = [];
13463    break;
13464  }
13465}
13466
13467function getInputs(body) {
13468  if (body.length != 1) return;
13469  var lastExpression = body[0].expression;
13470  var candidate = lastExpression.toWatch;
13471  if (candidate.length !== 1) return candidate;
13472  return candidate[0] !== lastExpression ? candidate : undefined;
13473}
13474
13475function isAssignable(ast) {
13476  return ast.type === AST.Identifier || ast.type === AST.MemberExpression;
13477}
13478
13479function assignableAST(ast) {
13480  if (ast.body.length === 1 && isAssignable(ast.body[0].expression)) {
13481    return {type: AST.AssignmentExpression, left: ast.body[0].expression, right: {type: AST.NGValueParameter}, operator: '='};
13482  }
13483}
13484
13485function isLiteral(ast) {
13486  return ast.body.length === 0 ||
13487      ast.body.length === 1 && (
13488      ast.body[0].expression.type === AST.Literal ||
13489      ast.body[0].expression.type === AST.ArrayExpression ||
13490      ast.body[0].expression.type === AST.ObjectExpression);
13491}
13492
13493function isConstant(ast) {
13494  return ast.constant;
13495}
13496
13497function ASTCompiler(astBuilder, $filter) {
13498  this.astBuilder = astBuilder;
13499  this.$filter = $filter;
13500}
13501
13502ASTCompiler.prototype = {
13503  compile: function(expression, expensiveChecks) {
13504    var self = this;
13505    var ast = this.astBuilder.ast(expression);
13506    this.state = {
13507      nextId: 0,
13508      filters: {},
13509      expensiveChecks: expensiveChecks,
13510      fn: {vars: [], body: [], own: {}},
13511      assign: {vars: [], body: [], own: {}},
13512      inputs: []
13513    };
13514    findConstantAndWatchExpressions(ast, self.$filter);
13515    var extra = '';
13516    var assignable;
13517    this.stage = 'assign';
13518    if ((assignable = assignableAST(ast))) {
13519      this.state.computing = 'assign';
13520      var result = this.nextId();
13521      this.recurse(assignable, result);
13522      this.return_(result);
13523      extra = 'fn.assign=' + this.generateFunction('assign', 's,v,l');
13524    }
13525    var toWatch = getInputs(ast.body);
13526    self.stage = 'inputs';
13527    forEach(toWatch, function(watch, key) {
13528      var fnKey = 'fn' + key;
13529      self.state[fnKey] = {vars: [], body: [], own: {}};
13530      self.state.computing = fnKey;
13531      var intoId = self.nextId();
13532      self.recurse(watch, intoId);
13533      self.return_(intoId);
13534      self.state.inputs.push(fnKey);
13535      watch.watchId = key;
13536    });
13537    this.state.computing = 'fn';
13538    this.stage = 'main';
13539    this.recurse(ast);
13540    var fnString =
13541      // The build and minification steps remove the string "use strict" from the code, but this is done using a regex.
13542      // This is a workaround for this until we do a better job at only removing the prefix only when we should.
13543      '"' + this.USE + ' ' + this.STRICT + '";\n' +
13544      this.filterPrefix() +
13545      'var fn=' + this.generateFunction('fn', 's,l,a,i') +
13546      extra +
13547      this.watchFns() +
13548      'return fn;';
13549
13550    /* jshint -W054 */
13551    var fn = (new Function('$filter',
13552        'ensureSafeMemberName',
13553        'ensureSafeObject',
13554        'ensureSafeFunction',
13555        'getStringValue',
13556        'ensureSafeAssignContext',
13557        'ifDefined',
13558        'plus',
13559        'text',
13560        fnString))(
13561          this.$filter,
13562          ensureSafeMemberName,
13563          ensureSafeObject,
13564          ensureSafeFunction,
13565          getStringValue,
13566          ensureSafeAssignContext,
13567          ifDefined,
13568          plusFn,
13569          expression);
13570    /* jshint +W054 */
13571    this.state = this.stage = undefined;
13572    fn.literal = isLiteral(ast);
13573    fn.constant = isConstant(ast);
13574    return fn;
13575  },
13576
13577  USE: 'use',
13578
13579  STRICT: 'strict',
13580
13581  watchFns: function() {
13582    var result = [];
13583    var fns = this.state.inputs;
13584    var self = this;
13585    forEach(fns, function(name) {
13586      result.push('var ' + name + '=' + self.generateFunction(name, 's'));
13587    });
13588    if (fns.length) {
13589      result.push('fn.inputs=[' + fns.join(',') + '];');
13590    }
13591    return result.join('');
13592  },
13593
13594  generateFunction: function(name, params) {
13595    return 'function(' + params + '){' +
13596        this.varsPrefix(name) +
13597        this.body(name) +
13598        '};';
13599  },
13600
13601  filterPrefix: function() {
13602    var parts = [];
13603    var self = this;
13604    forEach(this.state.filters, function(id, filter) {
13605      parts.push(id + '=$filter(' + self.escape(filter) + ')');
13606    });
13607    if (parts.length) return 'var ' + parts.join(',') + ';';
13608    return '';
13609  },
13610
13611  varsPrefix: function(section) {
13612    return this.state[section].vars.length ? 'var ' + this.state[section].vars.join(',') + ';' : '';
13613  },
13614
13615  body: function(section) {
13616    return this.state[section].body.join('');
13617  },
13618
13619  recurse: function(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck) {
13620    var left, right, self = this, args, expression;
13621    recursionFn = recursionFn || noop;
13622    if (!skipWatchIdCheck && isDefined(ast.watchId)) {
13623      intoId = intoId || this.nextId();
13624      this.if_('i',
13625        this.lazyAssign(intoId, this.computedMember('i', ast.watchId)),
13626        this.lazyRecurse(ast, intoId, nameId, recursionFn, create, true)
13627      );
13628      return;
13629    }
13630    switch (ast.type) {
13631    case AST.Program:
13632      forEach(ast.body, function(expression, pos) {
13633        self.recurse(expression.expression, undefined, undefined, function(expr) { right = expr; });
13634        if (pos !== ast.body.length - 1) {
13635          self.current().body.push(right, ';');
13636        } else {
13637          self.return_(right);
13638        }
13639      });
13640      break;
13641    case AST.Literal:
13642      expression = this.escape(ast.value);
13643      this.assign(intoId, expression);
13644      recursionFn(expression);
13645      break;
13646    case AST.UnaryExpression:
13647      this.recurse(ast.argument, undefined, undefined, function(expr) { right = expr; });
13648      expression = ast.operator + '(' + this.ifDefined(right, 0) + ')';
13649      this.assign(intoId, expression);
13650      recursionFn(expression);
13651      break;
13652    case AST.BinaryExpression:
13653      this.recurse(ast.left, undefined, undefined, function(expr) { left = expr; });
13654      this.recurse(ast.right, undefined, undefined, function(expr) { right = expr; });
13655      if (ast.operator === '+') {
13656        expression = this.plus(left, right);
13657      } else if (ast.operator === '-') {
13658        expression = this.ifDefined(left, 0) + ast.operator + this.ifDefined(right, 0);
13659      } else {
13660        expression = '(' + left + ')' + ast.operator + '(' + right + ')';
13661      }
13662      this.assign(intoId, expression);
13663      recursionFn(expression);
13664      break;
13665    case AST.LogicalExpression:
13666      intoId = intoId || this.nextId();
13667      self.recurse(ast.left, intoId);
13668      self.if_(ast.operator === '&&' ? intoId : self.not(intoId), self.lazyRecurse(ast.right, intoId));
13669      recursionFn(intoId);
13670      break;
13671    case AST.ConditionalExpression:
13672      intoId = intoId || this.nextId();
13673      self.recurse(ast.test, intoId);
13674      self.if_(intoId, self.lazyRecurse(ast.alternate, intoId), self.lazyRecurse(ast.consequent, intoId));
13675      recursionFn(intoId);
13676      break;
13677    case AST.Identifier:
13678      intoId = intoId || this.nextId();
13679      if (nameId) {
13680        nameId.context = self.stage === 'inputs' ? 's' : this.assign(this.nextId(), this.getHasOwnProperty('l', ast.name) + '?l:s');
13681        nameId.computed = false;
13682        nameId.name = ast.name;
13683      }
13684      ensureSafeMemberName(ast.name);
13685      self.if_(self.stage === 'inputs' || self.not(self.getHasOwnProperty('l', ast.name)),
13686        function() {
13687          self.if_(self.stage === 'inputs' || 's', function() {
13688            if (create && create !== 1) {
13689              self.if_(
13690                self.not(self.nonComputedMember('s', ast.name)),
13691                self.lazyAssign(self.nonComputedMember('s', ast.name), '{}'));
13692            }
13693            self.assign(intoId, self.nonComputedMember('s', ast.name));
13694          });
13695        }, intoId && self.lazyAssign(intoId, self.nonComputedMember('l', ast.name))
13696        );
13697      if (self.state.expensiveChecks || isPossiblyDangerousMemberName(ast.name)) {
13698        self.addEnsureSafeObject(intoId);
13699      }
13700      recursionFn(intoId);
13701      break;
13702    case AST.MemberExpression:
13703      left = nameId && (nameId.context = this.nextId()) || this.nextId();
13704      intoId = intoId || this.nextId();
13705      self.recurse(ast.object, left, undefined, function() {
13706        self.if_(self.notNull(left), function() {
13707          if (ast.computed) {
13708            right = self.nextId();
13709            self.recurse(ast.property, right);
13710            self.getStringValue(right);
13711            self.addEnsureSafeMemberName(right);
13712            if (create && create !== 1) {
13713              self.if_(self.not(self.computedMember(left, right)), self.lazyAssign(self.computedMember(left, right), '{}'));
13714            }
13715            expression = self.ensureSafeObject(self.computedMember(left, right));
13716            self.assign(intoId, expression);
13717            if (nameId) {
13718              nameId.computed = true;
13719              nameId.name = right;
13720            }
13721          } else {
13722            ensureSafeMemberName(ast.property.name);
13723            if (create && create !== 1) {
13724              self.if_(self.not(self.nonComputedMember(left, ast.property.name)), self.lazyAssign(self.nonComputedMember(left, ast.property.name), '{}'));
13725            }
13726            expression = self.nonComputedMember(left, ast.property.name);
13727            if (self.state.expensiveChecks || isPossiblyDangerousMemberName(ast.property.name)) {
13728              expression = self.ensureSafeObject(expression);
13729            }
13730            self.assign(intoId, expression);
13731            if (nameId) {
13732              nameId.computed = false;
13733              nameId.name = ast.property.name;
13734            }
13735          }
13736        }, function() {
13737          self.assign(intoId, 'undefined');
13738        });
13739        recursionFn(intoId);
13740      }, !!create);
13741      break;
13742    case AST.CallExpression:
13743      intoId = intoId || this.nextId();
13744      if (ast.filter) {
13745        right = self.filter(ast.callee.name);
13746        args = [];
13747        forEach(ast.arguments, function(expr) {
13748          var argument = self.nextId();
13749          self.recurse(expr, argument);
13750          args.push(argument);
13751        });
13752        expression = right + '(' + args.join(',') + ')';
13753        self.assign(intoId, expression);
13754        recursionFn(intoId);
13755      } else {
13756        right = self.nextId();
13757        left = {};
13758        args = [];
13759        self.recurse(ast.callee, right, left, function() {
13760          self.if_(self.notNull(right), function() {
13761            self.addEnsureSafeFunction(right);
13762            forEach(ast.arguments, function(expr) {
13763              self.recurse(expr, self.nextId(), undefined, function(argument) {
13764                args.push(self.ensureSafeObject(argument));
13765              });
13766            });
13767            if (left.name) {
13768              if (!self.state.expensiveChecks) {
13769                self.addEnsureSafeObject(left.context);
13770              }
13771              expression = self.member(left.context, left.name, left.computed) + '(' + args.join(',') + ')';
13772            } else {
13773              expression = right + '(' + args.join(',') + ')';
13774            }
13775            expression = self.ensureSafeObject(expression);
13776            self.assign(intoId, expression);
13777          }, function() {
13778            self.assign(intoId, 'undefined');
13779          });
13780          recursionFn(intoId);
13781        });
13782      }
13783      break;
13784    case AST.AssignmentExpression:
13785      right = this.nextId();
13786      left = {};
13787      if (!isAssignable(ast.left)) {
13788        throw $parseMinErr('lval', 'Trying to assing a value to a non l-value');
13789      }
13790      this.recurse(ast.left, undefined, left, function() {
13791        self.if_(self.notNull(left.context), function() {
13792          self.recurse(ast.right, right);
13793          self.addEnsureSafeObject(self.member(left.context, left.name, left.computed));
13794          self.addEnsureSafeAssignContext(left.context);
13795          expression = self.member(left.context, left.name, left.computed) + ast.operator + right;
13796          self.assign(intoId, expression);
13797          recursionFn(intoId || expression);
13798        });
13799      }, 1);
13800      break;
13801    case AST.ArrayExpression:
13802      args = [];
13803      forEach(ast.elements, function(expr) {
13804        self.recurse(expr, self.nextId(), undefined, function(argument) {
13805          args.push(argument);
13806        });
13807      });
13808      expression = '[' + args.join(',') + ']';
13809      this.assign(intoId, expression);
13810      recursionFn(expression);
13811      break;
13812    case AST.ObjectExpression:
13813      args = [];
13814      forEach(ast.properties, function(property) {
13815        self.recurse(property.value, self.nextId(), undefined, function(expr) {
13816          args.push(self.escape(
13817              property.key.type === AST.Identifier ? property.key.name :
13818                ('' + property.key.value)) +
13819              ':' + expr);
13820        });
13821      });
13822      expression = '{' + args.join(',') + '}';
13823      this.assign(intoId, expression);
13824      recursionFn(expression);
13825      break;
13826    case AST.ThisExpression:
13827      this.assign(intoId, 's');
13828      recursionFn('s');
13829      break;
13830    case AST.NGValueParameter:
13831      this.assign(intoId, 'v');
13832      recursionFn('v');
13833      break;
13834    }
13835  },
13836
13837  getHasOwnProperty: function(element, property) {
13838    var key = element + '.' + property;
13839    var own = this.current().own;
13840    if (!own.hasOwnProperty(key)) {
13841      own[key] = this.nextId(false, element + '&&(' + this.escape(property) + ' in ' + element + ')');
13842    }
13843    return own[key];
13844  },
13845
13846  assign: function(id, value) {
13847    if (!id) return;
13848    this.current().body.push(id, '=', value, ';');
13849    return id;
13850  },
13851
13852  filter: function(filterName) {
13853    if (!this.state.filters.hasOwnProperty(filterName)) {
13854      this.state.filters[filterName] = this.nextId(true);
13855    }
13856    return this.state.filters[filterName];
13857  },
13858
13859  ifDefined: function(id, defaultValue) {
13860    return 'ifDefined(' + id + ',' + this.escape(defaultValue) + ')';
13861  },
13862
13863  plus: function(left, right) {
13864    return 'plus(' + left + ',' + right + ')';
13865  },
13866
13867  return_: function(id) {
13868    this.current().body.push('return ', id, ';');
13869  },
13870
13871  if_: function(test, alternate, consequent) {
13872    if (test === true) {
13873      alternate();
13874    } else {
13875      var body = this.current().body;
13876      body.push('if(', test, '){');
13877      alternate();
13878      body.push('}');
13879      if (consequent) {
13880        body.push('else{');
13881        consequent();
13882        body.push('}');
13883      }
13884    }
13885  },
13886
13887  not: function(expression) {
13888    return '!(' + expression + ')';
13889  },
13890
13891  notNull: function(expression) {
13892    return expression + '!=null';
13893  },
13894
13895  nonComputedMember: function(left, right) {
13896    return left + '.' + right;
13897  },
13898
13899  computedMember: function(left, right) {
13900    return left + '[' + right + ']';
13901  },
13902
13903  member: function(left, right, computed) {
13904    if (computed) return this.computedMember(left, right);
13905    return this.nonComputedMember(left, right);
13906  },
13907
13908  addEnsureSafeObject: function(item) {
13909    this.current().body.push(this.ensureSafeObject(item), ';');
13910  },
13911
13912  addEnsureSafeMemberName: function(item) {
13913    this.current().body.push(this.ensureSafeMemberName(item), ';');
13914  },
13915
13916  addEnsureSafeFunction: function(item) {
13917    this.current().body.push(this.ensureSafeFunction(item), ';');
13918  },
13919
13920  addEnsureSafeAssignContext: function(item) {
13921    this.current().body.push(this.ensureSafeAssignContext(item), ';');
13922  },
13923
13924  ensureSafeObject: function(item) {
13925    return 'ensureSafeObject(' + item + ',text)';
13926  },
13927
13928  ensureSafeMemberName: function(item) {
13929    return 'ensureSafeMemberName(' + item + ',text)';
13930  },
13931
13932  ensureSafeFunction: function(item) {
13933    return 'ensureSafeFunction(' + item + ',text)';
13934  },
13935
13936  getStringValue: function(item) {
13937    this.assign(item, 'getStringValue(' + item + ',text)');
13938  },
13939
13940  ensureSafeAssignContext: function(item) {
13941    return 'ensureSafeAssignContext(' + item + ',text)';
13942  },
13943
13944  lazyRecurse: function(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck) {
13945    var self = this;
13946    return function() {
13947      self.recurse(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck);
13948    };
13949  },
13950
13951  lazyAssign: function(id, value) {
13952    var self = this;
13953    return function() {
13954      self.assign(id, value);
13955    };
13956  },
13957
13958  stringEscapeRegex: /[^ a-zA-Z0-9]/g,
13959
13960  stringEscapeFn: function(c) {
13961    return '\\u' + ('0000' + c.charCodeAt(0).toString(16)).slice(-4);
13962  },
13963
13964  escape: function(value) {
13965    if (isString(value)) return "'" + value.replace(this.stringEscapeRegex, this.stringEscapeFn) + "'";
13966    if (isNumber(value)) return value.toString();
13967    if (value === true) return 'true';
13968    if (value === false) return 'false';
13969    if (value === null) return 'null';
13970    if (typeof value === 'undefined') return 'undefined';
13971
13972    throw $parseMinErr('esc', 'IMPOSSIBLE');
13973  },
13974
13975  nextId: function(skip, init) {
13976    var id = 'v' + (this.state.nextId++);
13977    if (!skip) {
13978      this.current().vars.push(id + (init ? '=' + init : ''));
13979    }
13980    return id;
13981  },
13982
13983  current: function() {
13984    return this.state[this.state.computing];
13985  }
13986};
13987
13988
13989function ASTInterpreter(astBuilder, $filter) {
13990  this.astBuilder = astBuilder;
13991  this.$filter = $filter;
13992}
13993
13994ASTInterpreter.prototype = {
13995  compile: function(expression, expensiveChecks) {
13996    var self = this;
13997    var ast = this.astBuilder.ast(expression);
13998    this.expression = expression;
13999    this.expensiveChecks = expensiveChecks;
14000    findConstantAndWatchExpressions(ast, self.$filter);
14001    var assignable;
14002    var assign;
14003    if ((assignable = assignableAST(ast))) {
14004      assign = this.recurse(assignable);
14005    }
14006    var toWatch = getInputs(ast.body);
14007    var inputs;
14008    if (toWatch) {
14009      inputs = [];
14010      forEach(toWatch, function(watch, key) {
14011        var input = self.recurse(watch);
14012        watch.input = input;
14013        inputs.push(input);
14014        watch.watchId = key;
14015      });
14016    }
14017    var expressions = [];
14018    forEach(ast.body, function(expression) {
14019      expressions.push(self.recurse(expression.expression));
14020    });
14021    var fn = ast.body.length === 0 ? function() {} :
14022             ast.body.length === 1 ? expressions[0] :
14023             function(scope, locals) {
14024               var lastValue;
14025               forEach(expressions, function(exp) {
14026                 lastValue = exp(scope, locals);
14027               });
14028               return lastValue;
14029             };
14030    if (assign) {
14031      fn.assign = function(scope, value, locals) {
14032        return assign(scope, locals, value);
14033      };
14034    }
14035    if (inputs) {
14036      fn.inputs = inputs;
14037    }
14038    fn.literal = isLiteral(ast);
14039    fn.constant = isConstant(ast);
14040    return fn;
14041  },
14042
14043  recurse: function(ast, context, create) {
14044    var left, right, self = this, args, expression;
14045    if (ast.input) {
14046      return this.inputs(ast.input, ast.watchId);
14047    }
14048    switch (ast.type) {
14049    case AST.Literal:
14050      return this.value(ast.value, context);
14051    case AST.UnaryExpression:
14052      right = this.recurse(ast.argument);
14053      return this['unary' + ast.operator](right, context);
14054    case AST.BinaryExpression:
14055      left = this.recurse(ast.left);
14056      right = this.recurse(ast.right);
14057      return this['binary' + ast.operator](left, right, context);
14058    case AST.LogicalExpression:
14059      left = this.recurse(ast.left);
14060      right = this.recurse(ast.right);
14061      return this['binary' + ast.operator](left, right, context);
14062    case AST.ConditionalExpression:
14063      return this['ternary?:'](
14064        this.recurse(ast.test),
14065        this.recurse(ast.alternate),
14066        this.recurse(ast.consequent),
14067        context
14068      );
14069    case AST.Identifier:
14070      ensureSafeMemberName(ast.name, self.expression);
14071      return self.identifier(ast.name,
14072                             self.expensiveChecks || isPossiblyDangerousMemberName(ast.name),
14073                             context, create, self.expression);
14074    case AST.MemberExpression:
14075      left = this.recurse(ast.object, false, !!create);
14076      if (!ast.computed) {
14077        ensureSafeMemberName(ast.property.name, self.expression);
14078        right = ast.property.name;
14079      }
14080      if (ast.computed) right = this.recurse(ast.property);
14081      return ast.computed ?
14082        this.computedMember(left, right, context, create, self.expression) :
14083        this.nonComputedMember(left, right, self.expensiveChecks, context, create, self.expression);
14084    case AST.CallExpression:
14085      args = [];
14086      forEach(ast.arguments, function(expr) {
14087        args.push(self.recurse(expr));
14088      });
14089      if (ast.filter) right = this.$filter(ast.callee.name);
14090      if (!ast.filter) right = this.recurse(ast.callee, true);
14091      return ast.filter ?
14092        function(scope, locals, assign, inputs) {
14093          var values = [];
14094          for (var i = 0; i < args.length; ++i) {
14095            values.push(args[i](scope, locals, assign, inputs));
14096          }
14097          var value = right.apply(undefined, values, inputs);
14098          return context ? {context: undefined, name: undefined, value: value} : value;
14099        } :
14100        function(scope, locals, assign, inputs) {
14101          var rhs = right(scope, locals, assign, inputs);
14102          var value;
14103          if (rhs.value != null) {
14104            ensureSafeObject(rhs.context, self.expression);
14105            ensureSafeFunction(rhs.value, self.expression);
14106            var values = [];
14107            for (var i = 0; i < args.length; ++i) {
14108              values.push(ensureSafeObject(args[i](scope, locals, assign, inputs), self.expression));
14109            }
14110            value = ensureSafeObject(rhs.value.apply(rhs.context, values), self.expression);
14111          }
14112          return context ? {value: value} : value;
14113        };
14114    case AST.AssignmentExpression:
14115      left = this.recurse(ast.left, true, 1);
14116      right = this.recurse(ast.right);
14117      return function(scope, locals, assign, inputs) {
14118        var lhs = left(scope, locals, assign, inputs);
14119        var rhs = right(scope, locals, assign, inputs);
14120        ensureSafeObject(lhs.value, self.expression);
14121        ensureSafeAssignContext(lhs.context);
14122        lhs.context[lhs.name] = rhs;
14123        return context ? {value: rhs} : rhs;
14124      };
14125    case AST.ArrayExpression:
14126      args = [];
14127      forEach(ast.elements, function(expr) {
14128        args.push(self.recurse(expr));
14129      });
14130      return function(scope, locals, assign, inputs) {
14131        var value = [];
14132        for (var i = 0; i < args.length; ++i) {
14133          value.push(args[i](scope, locals, assign, inputs));
14134        }
14135        return context ? {value: value} : value;
14136      };
14137    case AST.ObjectExpression:
14138      args = [];
14139      forEach(ast.properties, function(property) {
14140        args.push({key: property.key.type === AST.Identifier ?
14141                        property.key.name :
14142                        ('' + property.key.value),
14143                   value: self.recurse(property.value)
14144        });
14145      });
14146      return function(scope, locals, assign, inputs) {
14147        var value = {};
14148        for (var i = 0; i < args.length; ++i) {
14149          value[args[i].key] = args[i].value(scope, locals, assign, inputs);
14150        }
14151        return context ? {value: value} : value;
14152      };
14153    case AST.ThisExpression:
14154      return function(scope) {
14155        return context ? {value: scope} : scope;
14156      };
14157    case AST.NGValueParameter:
14158      return function(scope, locals, assign, inputs) {
14159        return context ? {value: assign} : assign;
14160      };
14161    }
14162  },
14163
14164  'unary+': function(argument, context) {
14165    return function(scope, locals, assign, inputs) {
14166      var arg = argument(scope, locals, assign, inputs);
14167      if (isDefined(arg)) {
14168        arg = +arg;
14169      } else {
14170        arg = 0;
14171      }
14172      return context ? {value: arg} : arg;
14173    };
14174  },
14175  'unary-': function(argument, context) {
14176    return function(scope, locals, assign, inputs) {
14177      var arg = argument(scope, locals, assign, inputs);
14178      if (isDefined(arg)) {
14179        arg = -arg;
14180      } else {
14181        arg = 0;
14182      }
14183      return context ? {value: arg} : arg;
14184    };
14185  },
14186  'unary!': function(argument, context) {
14187    return function(scope, locals, assign, inputs) {
14188      var arg = !argument(scope, locals, assign, inputs);
14189      return context ? {value: arg} : arg;
14190    };
14191  },
14192  'binary+': function(left, right, context) {
14193    return function(scope, locals, assign, inputs) {
14194      var lhs = left(scope, locals, assign, inputs);
14195      var rhs = right(scope, locals, assign, inputs);
14196      var arg = plusFn(lhs, rhs);
14197      return context ? {value: arg} : arg;
14198    };
14199  },
14200  'binary-': function(left, right, context) {
14201    return function(scope, locals, assign, inputs) {
14202      var lhs = left(scope, locals, assign, inputs);
14203      var rhs = right(scope, locals, assign, inputs);
14204      var arg = (isDefined(lhs) ? lhs : 0) - (isDefined(rhs) ? rhs : 0);
14205      return context ? {value: arg} : arg;
14206    };
14207  },
14208  'binary*': function(left, right, context) {
14209    return function(scope, locals, assign, inputs) {
14210      var arg = left(scope, locals, assign, inputs) * right(scope, locals, assign, inputs);
14211      return context ? {value: arg} : arg;
14212    };
14213  },
14214  'binary/': function(left, right, context) {
14215    return function(scope, locals, assign, inputs) {
14216      var arg = left(scope, locals, assign, inputs) / right(scope, locals, assign, inputs);
14217      return context ? {value: arg} : arg;
14218    };
14219  },
14220  'binary%': function(left, right, context) {
14221    return function(scope, locals, assign, inputs) {
14222      var arg = left(scope, locals, assign, inputs) % right(scope, locals, assign, inputs);
14223      return context ? {value: arg} : arg;
14224    };
14225  },
14226  'binary===': function(left, right, context) {
14227    return function(scope, locals, assign, inputs) {
14228      var arg = left(scope, locals, assign, inputs) === right(scope, locals, assign, inputs);
14229      return context ? {value: arg} : arg;
14230    };
14231  },
14232  'binary!==': function(left, right, context) {
14233    return function(scope, locals, assign, inputs) {
14234      var arg = left(scope, locals, assign, inputs) !== right(scope, locals, assign, inputs);
14235      return context ? {value: arg} : arg;
14236    };
14237  },
14238  'binary==': function(left, right, context) {
14239    return function(scope, locals, assign, inputs) {
14240      var arg = left(scope, locals, assign, inputs) == right(scope, locals, assign, inputs);
14241      return context ? {value: arg} : arg;
14242    };
14243  },
14244  'binary!=': function(left, right, context) {
14245    return function(scope, locals, assign, inputs) {
14246      var arg = left(scope, locals, assign, inputs) != right(scope, locals, assign, inputs);
14247      return context ? {value: arg} : arg;
14248    };
14249  },
14250  'binary<': function(left, right, context) {
14251    return function(scope, locals, assign, inputs) {
14252      var arg = left(scope, locals, assign, inputs) < right(scope, locals, assign, inputs);
14253      return context ? {value: arg} : arg;
14254    };
14255  },
14256  'binary>': function(left, right, context) {
14257    return function(scope, locals, assign, inputs) {
14258      var arg = left(scope, locals, assign, inputs) > right(scope, locals, assign, inputs);
14259      return context ? {value: arg} : arg;
14260    };
14261  },
14262  'binary<=': function(left, right, context) {
14263    return function(scope, locals, assign, inputs) {
14264      var arg = left(scope, locals, assign, inputs) <= right(scope, locals, assign, inputs);
14265      return context ? {value: arg} : arg;
14266    };
14267  },
14268  'binary>=': function(left, right, context) {
14269    return function(scope, locals, assign, inputs) {
14270      var arg = left(scope, locals, assign, inputs) >= right(scope, locals, assign, inputs);
14271      return context ? {value: arg} : arg;
14272    };
14273  },
14274  'binary&&': function(left, right, context) {
14275    return function(scope, locals, assign, inputs) {
14276      var arg = left(scope, locals, assign, inputs) && right(scope, locals, assign, inputs);
14277      return context ? {value: arg} : arg;
14278    };
14279  },
14280  'binary||': function(left, right, context) {
14281    return function(scope, locals, assign, inputs) {
14282      var arg = left(scope, locals, assign, inputs) || right(scope, locals, assign, inputs);
14283      return context ? {value: arg} : arg;
14284    };
14285  },
14286  'ternary?:': function(test, alternate, consequent, context) {
14287    return function(scope, locals, assign, inputs) {
14288      var arg = test(scope, locals, assign, inputs) ? alternate(scope, locals, assign, inputs) : consequent(scope, locals, assign, inputs);
14289      return context ? {value: arg} : arg;
14290    };
14291  },
14292  value: function(value, context) {
14293    return function() { return context ? {context: undefined, name: undefined, value: value} : value; };
14294  },
14295  identifier: function(name, expensiveChecks, context, create, expression) {
14296    return function(scope, locals, assign, inputs) {
14297      var base = locals && (name in locals) ? locals : scope;
14298      if (create && create !== 1 && base && !(base[name])) {
14299        base[name] = {};
14300      }
14301      var value = base ? base[name] : undefined;
14302      if (expensiveChecks) {
14303        ensureSafeObject(value, expression);
14304      }
14305      if (context) {
14306        return {context: base, name: name, value: value};
14307      } else {
14308        return value;
14309      }
14310    };
14311  },
14312  computedMember: function(left, right, context, create, expression) {
14313    return function(scope, locals, assign, inputs) {
14314      var lhs = left(scope, locals, assign, inputs);
14315      var rhs;
14316      var value;
14317      if (lhs != null) {
14318        rhs = right(scope, locals, assign, inputs);
14319        rhs = getStringValue(rhs);
14320        ensureSafeMemberName(rhs, expression);
14321        if (create && create !== 1 && lhs && !(lhs[rhs])) {
14322          lhs[rhs] = {};
14323        }
14324        value = lhs[rhs];
14325        ensureSafeObject(value, expression);
14326      }
14327      if (context) {
14328        return {context: lhs, name: rhs, value: value};
14329      } else {
14330        return value;
14331      }
14332    };
14333  },
14334  nonComputedMember: function(left, right, expensiveChecks, context, create, expression) {
14335    return function(scope, locals, assign, inputs) {
14336      var lhs = left(scope, locals, assign, inputs);
14337      if (create && create !== 1 && lhs && !(lhs[right])) {
14338        lhs[right] = {};
14339      }
14340      var value = lhs != null ? lhs[right] : undefined;
14341      if (expensiveChecks || isPossiblyDangerousMemberName(right)) {
14342        ensureSafeObject(value, expression);
14343      }
14344      if (context) {
14345        return {context: lhs, name: right, value: value};
14346      } else {
14347        return value;
14348      }
14349    };
14350  },
14351  inputs: function(input, watchId) {
14352    return function(scope, value, locals, inputs) {
14353      if (inputs) return inputs[watchId];
14354      return input(scope, value, locals);
14355    };
14356  }
14357};
14358
14359/**
14360 * @constructor
14361 */
14362var Parser = function(lexer, $filter, options) {
14363  this.lexer = lexer;
14364  this.$filter = $filter;
14365  this.options = options;
14366  this.ast = new AST(this.lexer);
14367  this.astCompiler = options.csp ? new ASTInterpreter(this.ast, $filter) :
14368                                   new ASTCompiler(this.ast, $filter);
14369};
14370
14371Parser.prototype = {
14372  constructor: Parser,
14373
14374  parse: function(text) {
14375    return this.astCompiler.compile(text, this.options.expensiveChecks);
14376  }
14377};
14378
14379var getterFnCacheDefault = createMap();
14380var getterFnCacheExpensive = createMap();
14381
14382function isPossiblyDangerousMemberName(name) {
14383  return name == 'constructor';
14384}
14385
14386var objectValueOf = Object.prototype.valueOf;
14387
14388function getValueOf(value) {
14389  return isFunction(value.valueOf) ? value.valueOf() : objectValueOf.call(value);
14390}
14391
14392///////////////////////////////////
14393
14394/**
14395 * @ngdoc service
14396 * @name $parse
14397 * @kind function
14398 *
14399 * @description
14400 *
14401 * Converts Angular {@link guide/expression expression} into a function.
14402 *
14403 * ```js
14404 *   var getter = $parse('user.name');
14405 *   var setter = getter.assign;
14406 *   var context = {user:{name:'angular'}};
14407 *   var locals = {user:{name:'local'}};
14408 *
14409 *   expect(getter(context)).toEqual('angular');
14410 *   setter(context, 'newValue');
14411 *   expect(context.user.name).toEqual('newValue');
14412 *   expect(getter(context, locals)).toEqual('local');
14413 * ```
14414 *
14415 *
14416 * @param {string} expression String expression to compile.
14417 * @returns {function(context, locals)} a function which represents the compiled expression:
14418 *
14419 *    * `context` – `{object}` – an object against which any expressions embedded in the strings
14420 *      are evaluated against (typically a scope object).
14421 *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
14422 *      `context`.
14423 *
14424 *    The returned function also has the following properties:
14425 *      * `literal` – `{boolean}` – whether the expression's top-level node is a JavaScript
14426 *        literal.
14427 *      * `constant` – `{boolean}` – whether the expression is made entirely of JavaScript
14428 *        constant literals.
14429 *      * `assign` – `{?function(context, value)}` – if the expression is assignable, this will be
14430 *        set to a function to change its value on the given context.
14431 *
14432 */
14433
14434
14435/**
14436 * @ngdoc provider
14437 * @name $parseProvider
14438 *
14439 * @description
14440 * `$parseProvider` can be used for configuring the default behavior of the {@link ng.$parse $parse}
14441 *  service.
14442 */
14443function $ParseProvider() {
14444  var cacheDefault = createMap();
14445  var cacheExpensive = createMap();
14446
14447  this.$get = ['$filter', function($filter) {
14448    var noUnsafeEval = csp().noUnsafeEval;
14449    var $parseOptions = {
14450          csp: noUnsafeEval,
14451          expensiveChecks: false
14452        },
14453        $parseOptionsExpensive = {
14454          csp: noUnsafeEval,
14455          expensiveChecks: true
14456        };
14457
14458    return function $parse(exp, interceptorFn, expensiveChecks) {
14459      var parsedExpression, oneTime, cacheKey;
14460
14461      switch (typeof exp) {
14462        case 'string':
14463          exp = exp.trim();
14464          cacheKey = exp;
14465
14466          var cache = (expensiveChecks ? cacheExpensive : cacheDefault);
14467          parsedExpression = cache[cacheKey];
14468
14469          if (!parsedExpression) {
14470            if (exp.charAt(0) === ':' && exp.charAt(1) === ':') {
14471              oneTime = true;
14472              exp = exp.substring(2);
14473            }
14474            var parseOptions = expensiveChecks ? $parseOptionsExpensive : $parseOptions;
14475            var lexer = new Lexer(parseOptions);
14476            var parser = new Parser(lexer, $filter, parseOptions);
14477            parsedExpression = parser.parse(exp);
14478            if (parsedExpression.constant) {
14479              parsedExpression.$$watchDelegate = constantWatchDelegate;
14480            } else if (oneTime) {
14481              parsedExpression.$$watchDelegate = parsedExpression.literal ?
14482                  oneTimeLiteralWatchDelegate : oneTimeWatchDelegate;
14483            } else if (parsedExpression.inputs) {
14484              parsedExpression.$$watchDelegate = inputsWatchDelegate;
14485            }
14486            cache[cacheKey] = parsedExpression;
14487          }
14488          return addInterceptor(parsedExpression, interceptorFn);
14489
14490        case 'function':
14491          return addInterceptor(exp, interceptorFn);
14492
14493        default:
14494          return noop;
14495      }
14496    };
14497
14498    function expressionInputDirtyCheck(newValue, oldValueOfValue) {
14499
14500      if (newValue == null || oldValueOfValue == null) { // null/undefined
14501        return newValue === oldValueOfValue;
14502      }
14503
14504      if (typeof newValue === 'object') {
14505
14506        // attempt to convert the value to a primitive type
14507        // TODO(docs): add a note to docs that by implementing valueOf even objects and arrays can
14508        //             be cheaply dirty-checked
14509        newValue = getValueOf(newValue);
14510
14511        if (typeof newValue === 'object') {
14512          // objects/arrays are not supported - deep-watching them would be too expensive
14513          return false;
14514        }
14515
14516        // fall-through to the primitive equality check
14517      }
14518
14519      //Primitive or NaN
14520      return newValue === oldValueOfValue || (newValue !== newValue && oldValueOfValue !== oldValueOfValue);
14521    }
14522
14523    function inputsWatchDelegate(scope, listener, objectEquality, parsedExpression, prettyPrintExpression) {
14524      var inputExpressions = parsedExpression.inputs;
14525      var lastResult;
14526
14527      if (inputExpressions.length === 1) {
14528        var oldInputValueOf = expressionInputDirtyCheck; // init to something unique so that equals check fails
14529        inputExpressions = inputExpressions[0];
14530        return scope.$watch(function expressionInputWatch(scope) {
14531          var newInputValue = inputExpressions(scope);
14532          if (!expressionInputDirtyCheck(newInputValue, oldInputValueOf)) {
14533            lastResult = parsedExpression(scope, undefined, undefined, [newInputValue]);
14534            oldInputValueOf = newInputValue && getValueOf(newInputValue);
14535          }
14536          return lastResult;
14537        }, listener, objectEquality, prettyPrintExpression);
14538      }
14539
14540      var oldInputValueOfValues = [];
14541      var oldInputValues = [];
14542      for (var i = 0, ii = inputExpressions.length; i < ii; i++) {
14543        oldInputValueOfValues[i] = expressionInputDirtyCheck; // init to something unique so that equals check fails
14544        oldInputValues[i] = null;
14545      }
14546
14547      return scope.$watch(function expressionInputsWatch(scope) {
14548        var changed = false;
14549
14550        for (var i = 0, ii = inputExpressions.length; i < ii; i++) {
14551          var newInputValue = inputExpressions[i](scope);
14552          if (changed || (changed = !expressionInputDirtyCheck(newInputValue, oldInputValueOfValues[i]))) {
14553            oldInputValues[i] = newInputValue;
14554            oldInputValueOfValues[i] = newInputValue && getValueOf(newInputValue);
14555          }
14556        }
14557
14558        if (changed) {
14559          lastResult = parsedExpression(scope, undefined, undefined, oldInputValues);
14560        }
14561
14562        return lastResult;
14563      }, listener, objectEquality, prettyPrintExpression);
14564    }
14565
14566    function oneTimeWatchDelegate(scope, listener, objectEquality, parsedExpression) {
14567      var unwatch, lastValue;
14568      return unwatch = scope.$watch(function oneTimeWatch(scope) {
14569        return parsedExpression(scope);
14570      }, function oneTimeListener(value, old, scope) {
14571        lastValue = value;
14572        if (isFunction(listener)) {
14573          listener.apply(this, arguments);
14574        }
14575        if (isDefined(value)) {
14576          scope.$$postDigest(function() {
14577            if (isDefined(lastValue)) {
14578              unwatch();
14579            }
14580          });
14581        }
14582      }, objectEquality);
14583    }
14584
14585    function oneTimeLiteralWatchDelegate(scope, listener, objectEquality, parsedExpression) {
14586      var unwatch, lastValue;
14587      return unwatch = scope.$watch(function oneTimeWatch(scope) {
14588        return parsedExpression(scope);
14589      }, function oneTimeListener(value, old, scope) {
14590        lastValue = value;
14591        if (isFunction(listener)) {
14592          listener.call(this, value, old, scope);
14593        }
14594        if (isAllDefined(value)) {
14595          scope.$$postDigest(function() {
14596            if (isAllDefined(lastValue)) unwatch();
14597          });
14598        }
14599      }, objectEquality);
14600
14601      function isAllDefined(value) {
14602        var allDefined = true;
14603        forEach(value, function(val) {
14604          if (!isDefined(val)) allDefined = false;
14605        });
14606        return allDefined;
14607      }
14608    }
14609
14610    function constantWatchDelegate(scope, listener, objectEquality, parsedExpression) {
14611      var unwatch;
14612      return unwatch = scope.$watch(function constantWatch(scope) {
14613        return parsedExpression(scope);
14614      }, function constantListener(value, old, scope) {
14615        if (isFunction(listener)) {
14616          listener.apply(this, arguments);
14617        }
14618        unwatch();
14619      }, objectEquality);
14620    }
14621
14622    function addInterceptor(parsedExpression, interceptorFn) {
14623      if (!interceptorFn) return parsedExpression;
14624      var watchDelegate = parsedExpression.$$watchDelegate;
14625
14626      var regularWatch =
14627          watchDelegate !== oneTimeLiteralWatchDelegate &&
14628          watchDelegate !== oneTimeWatchDelegate;
14629
14630      var fn = regularWatch ? function regularInterceptedExpression(scope, locals, assign, inputs) {
14631        var value = parsedExpression(scope, locals, assign, inputs);
14632        return interceptorFn(value, scope, locals);
14633      } : function oneTimeInterceptedExpression(scope, locals, assign, inputs) {
14634        var value = parsedExpression(scope, locals, assign, inputs);
14635        var result = interceptorFn(value, scope, locals);
14636        // we only return the interceptor's result if the
14637        // initial value is defined (for bind-once)
14638        return isDefined(value) ? result : value;
14639      };
14640
14641      // Propagate $$watchDelegates other then inputsWatchDelegate
14642      if (parsedExpression.$$watchDelegate &&
14643          parsedExpression.$$watchDelegate !== inputsWatchDelegate) {
14644        fn.$$watchDelegate = parsedExpression.$$watchDelegate;
14645      } else if (!interceptorFn.$stateful) {
14646        // If there is an interceptor, but no watchDelegate then treat the interceptor like
14647        // we treat filters - it is assumed to be a pure function unless flagged with $stateful
14648        fn.$$watchDelegate = inputsWatchDelegate;
14649        fn.inputs = parsedExpression.inputs ? parsedExpression.inputs : [parsedExpression];
14650      }
14651
14652      return fn;
14653    }
14654  }];
14655}
14656
14657/**
14658 * @ngdoc service
14659 * @name $q
14660 * @requires $rootScope
14661 *
14662 * @description
14663 * A service that helps you run functions asynchronously, and use their return values (or exceptions)
14664 * when they are done processing.
14665 *
14666 * This is an implementation of promises/deferred objects inspired by
14667 * [Kris Kowal's Q](https://github.com/kriskowal/q).
14668 *
14669 * $q can be used in two fashions --- one which is more similar to Kris Kowal's Q or jQuery's Deferred
14670 * implementations, and the other which resembles ES6 promises to some degree.
14671 *
14672 * # $q constructor
14673 *
14674 * The streamlined ES6 style promise is essentially just using $q as a constructor which takes a `resolver`
14675 * function as the first argument. This is similar to the native Promise implementation from ES6 Harmony,
14676 * see [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise).
14677 *
14678 * While the constructor-style use is supported, not all of the supporting methods from ES6 Harmony promises are
14679 * available yet.
14680 *
14681 * It can be used like so:
14682 *
14683 * ```js
14684 *   // for the purpose of this example let's assume that variables `$q` and `okToGreet`
14685 *   // are available in the current lexical scope (they could have been injected or passed in).
14686 *
14687 *   function asyncGreet(name) {
14688 *     // perform some asynchronous operation, resolve or reject the promise when appropriate.
14689 *     return $q(function(resolve, reject) {
14690 *       setTimeout(function() {
14691 *         if (okToGreet(name)) {
14692 *           resolve('Hello, ' + name + '!');
14693 *         } else {
14694 *           reject('Greeting ' + name + ' is not allowed.');
14695 *         }
14696 *       }, 1000);
14697 *     });
14698 *   }
14699 *
14700 *   var promise = asyncGreet('Robin Hood');
14701 *   promise.then(function(greeting) {
14702 *     alert('Success: ' + greeting);
14703 *   }, function(reason) {
14704 *     alert('Failed: ' + reason);
14705 *   });
14706 * ```
14707 *
14708 * Note: progress/notify callbacks are not currently supported via the ES6-style interface.
14709 *
14710 * However, the more traditional CommonJS-style usage is still available, and documented below.
14711 *
14712 * [The CommonJS Promise proposal](http://wiki.commonjs.org/wiki/Promises) describes a promise as an
14713 * interface for interacting with an object that represents the result of an action that is
14714 * performed asynchronously, and may or may not be finished at any given point in time.
14715 *
14716 * From the perspective of dealing with error handling, deferred and promise APIs are to
14717 * asynchronous programming what `try`, `catch` and `throw` keywords are to synchronous programming.
14718 *
14719 * ```js
14720 *   // for the purpose of this example let's assume that variables `$q` and `okToGreet`
14721 *   // are available in the current lexical scope (they could have been injected or passed in).
14722 *
14723 *   function asyncGreet(name) {
14724 *     var deferred = $q.defer();
14725 *
14726 *     setTimeout(function() {
14727 *       deferred.notify('About to greet ' + name + '.');
14728 *
14729 *       if (okToGreet(name)) {
14730 *         deferred.resolve('Hello, ' + name + '!');
14731 *       } else {
14732 *         deferred.reject('Greeting ' + name + ' is not allowed.');
14733 *       }
14734 *     }, 1000);
14735 *
14736 *     return deferred.promise;
14737 *   }
14738 *
14739 *   var promise = asyncGreet('Robin Hood');
14740 *   promise.then(function(greeting) {
14741 *     alert('Success: ' + greeting);
14742 *   }, function(reason) {
14743 *     alert('Failed: ' + reason);
14744 *   }, function(update) {
14745 *     alert('Got notification: ' + update);
14746 *   });
14747 * ```
14748 *
14749 * At first it might not be obvious why this extra complexity is worth the trouble. The payoff
14750 * comes in the way of guarantees that promise and deferred APIs make, see
14751 * https://github.com/kriskowal/uncommonjs/blob/master/promises/specification.md.
14752 *
14753 * Additionally the promise api allows for composition that is very hard to do with the
14754 * traditional callback ([CPS](http://en.wikipedia.org/wiki/Continuation-passing_style)) approach.
14755 * For more on this please see the [Q documentation](https://github.com/kriskowal/q) especially the
14756 * section on serial or parallel joining of promises.
14757 *
14758 * # The Deferred API
14759 *
14760 * A new instance of deferred is constructed by calling `$q.defer()`.
14761 *
14762 * The purpose of the deferred object is to expose the associated Promise instance as well as APIs
14763 * that can be used for signaling the successful or unsuccessful completion, as well as the status
14764 * of the task.
14765 *
14766 * **Methods**
14767 *
14768 * - `resolve(value)` – resolves the derived promise with the `value`. If the value is a rejection
14769 *   constructed via `$q.reject`, the promise will be rejected instead.
14770 * - `reject(reason)` – rejects the derived promise with the `reason`. This is equivalent to
14771 *   resolving it with a rejection constructed via `$q.reject`.
14772 * - `notify(value)` - provides updates on the status of the promise's execution. This may be called
14773 *   multiple times before the promise is either resolved or rejected.
14774 *
14775 * **Properties**
14776 *
14777 * - promise – `{Promise}` – promise object associated with this deferred.
14778 *
14779 *
14780 * # The Promise API
14781 *
14782 * A new promise instance is created when a deferred instance is created and can be retrieved by
14783 * calling `deferred.promise`.
14784 *
14785 * The purpose of the promise object is to allow for interested parties to get access to the result
14786 * of the deferred task when it completes.
14787 *
14788 * **Methods**
14789 *
14790 * - `then(successCallback, errorCallback, notifyCallback)` – regardless of when the promise was or
14791 *   will be resolved or rejected, `then` calls one of the success or error callbacks asynchronously
14792 *   as soon as the result is available. The callbacks are called with a single argument: the result
14793 *   or rejection reason. Additionally, the notify callback may be called zero or more times to
14794 *   provide a progress indication, before the promise is resolved or rejected.
14795 *
14796 *   This method *returns a new promise* which is resolved or rejected via the return value of the
14797 *   `successCallback`, `errorCallback` (unless that value is a promise, in which case it is resolved
14798 *   with the value which is resolved in that promise using
14799 *   [promise chaining](http://www.html5rocks.com/en/tutorials/es6/promises/#toc-promises-queues)).
14800 *   It also notifies via the return value of the `notifyCallback` method. The promise cannot be
14801 *   resolved or rejected from the notifyCallback method.
14802 *
14803 * - `catch(errorCallback)` – shorthand for `promise.then(null, errorCallback)`
14804 *
14805 * - `finally(callback, notifyCallback)` – allows you to observe either the fulfillment or rejection of a promise,
14806 *   but to do so without modifying the final value. This is useful to release resources or do some
14807 *   clean-up that needs to be done whether the promise was rejected or resolved. See the [full
14808 *   specification](https://github.com/kriskowal/q/wiki/API-Reference#promisefinallycallback) for
14809 *   more information.
14810 *
14811 * # Chaining promises
14812 *
14813 * Because calling the `then` method of a promise returns a new derived promise, it is easily
14814 * possible to create a chain of promises:
14815 *
14816 * ```js
14817 *   promiseB = promiseA.then(function(result) {
14818 *     return result + 1;
14819 *   });
14820 *
14821 *   // promiseB will be resolved immediately after promiseA is resolved and its value
14822 *   // will be the result of promiseA incremented by 1
14823 * ```
14824 *
14825 * It is possible to create chains of any length and since a promise can be resolved with another
14826 * promise (which will defer its resolution further), it is possible to pause/defer resolution of
14827 * the promises at any point in the chain. This makes it possible to implement powerful APIs like
14828 * $http's response interceptors.
14829 *
14830 *
14831 * # Differences between Kris Kowal's Q and $q
14832 *
14833 *  There are two main differences:
14834 *
14835 * - $q is integrated with the {@link ng.$rootScope.Scope} Scope model observation
14836 *   mechanism in angular, which means faster propagation of resolution or rejection into your
14837 *   models and avoiding unnecessary browser repaints, which would result in flickering UI.
14838 * - Q has many more features than $q, but that comes at a cost of bytes. $q is tiny, but contains
14839 *   all the important functionality needed for common async tasks.
14840 *
14841 *  # Testing
14842 *
14843 *  ```js
14844 *    it('should simulate promise', inject(function($q, $rootScope) {
14845 *      var deferred = $q.defer();
14846 *      var promise = deferred.promise;
14847 *      var resolvedValue;
14848 *
14849 *      promise.then(function(value) { resolvedValue = value; });
14850 *      expect(resolvedValue).toBeUndefined();
14851 *
14852 *      // Simulate resolving of promise
14853 *      deferred.resolve(123);
14854 *      // Note that the 'then' function does not get called synchronously.
14855 *      // This is because we want the promise API to always be async, whether or not
14856 *      // it got called synchronously or asynchronously.
14857 *      expect(resolvedValue).toBeUndefined();
14858 *
14859 *      // Propagate promise resolution to 'then' functions using $apply().
14860 *      $rootScope.$apply();
14861 *      expect(resolvedValue).toEqual(123);
14862 *    }));
14863 *  ```
14864 *
14865 * @param {function(function, function)} resolver Function which is responsible for resolving or
14866 *   rejecting the newly created promise. The first parameter is a function which resolves the
14867 *   promise, the second parameter is a function which rejects the promise.
14868 *
14869 * @returns {Promise} The newly created promise.
14870 */
14871function $QProvider() {
14872
14873  this.$get = ['$rootScope', '$exceptionHandler', function($rootScope, $exceptionHandler) {
14874    return qFactory(function(callback) {
14875      $rootScope.$evalAsync(callback);
14876    }, $exceptionHandler);
14877  }];
14878}
14879
14880function $$QProvider() {
14881  this.$get = ['$browser', '$exceptionHandler', function($browser, $exceptionHandler) {
14882    return qFactory(function(callback) {
14883      $browser.defer(callback);
14884    }, $exceptionHandler);
14885  }];
14886}
14887
14888/**
14889 * Constructs a promise manager.
14890 *
14891 * @param {function(function)} nextTick Function for executing functions in the next turn.
14892 * @param {function(...*)} exceptionHandler Function into which unexpected exceptions are passed for
14893 *     debugging purposes.
14894 * @returns {object} Promise manager.
14895 */
14896function qFactory(nextTick, exceptionHandler) {
14897  var $qMinErr = minErr('$q', TypeError);
14898  function callOnce(self, resolveFn, rejectFn) {
14899    var called = false;
14900    function wrap(fn) {
14901      return function(value) {
14902        if (called) return;
14903        called = true;
14904        fn.call(self, value);
14905      };
14906    }
14907
14908    return [wrap(resolveFn), wrap(rejectFn)];
14909  }
14910
14911  /**
14912   * @ngdoc method
14913   * @name ng.$q#defer
14914   * @kind function
14915   *
14916   * @description
14917   * Creates a `Deferred` object which represents a task which will finish in the future.
14918   *
14919   * @returns {Deferred} Returns a new instance of deferred.
14920   */
14921  var defer = function() {
14922    return new Deferred();
14923  };
14924
14925  function Promise() {
14926    this.$$state = { status: 0 };
14927  }
14928
14929  extend(Promise.prototype, {
14930    then: function(onFulfilled, onRejected, progressBack) {
14931      if (isUndefined(onFulfilled) && isUndefined(onRejected) && isUndefined(progressBack)) {
14932        return this;
14933      }
14934      var result = new Deferred();
14935
14936      this.$$state.pending = this.$$state.pending || [];
14937      this.$$state.pending.push([result, onFulfilled, onRejected, progressBack]);
14938      if (this.$$state.status > 0) scheduleProcessQueue(this.$$state);
14939
14940      return result.promise;
14941    },
14942
14943    "catch": function(callback) {
14944      return this.then(null, callback);
14945    },
14946
14947    "finally": function(callback, progressBack) {
14948      return this.then(function(value) {
14949        return handleCallback(value, true, callback);
14950      }, function(error) {
14951        return handleCallback(error, false, callback);
14952      }, progressBack);
14953    }
14954  });
14955
14956  //Faster, more basic than angular.bind http://jsperf.com/angular-bind-vs-custom-vs-native
14957  function simpleBind(context, fn) {
14958    return function(value) {
14959      fn.call(context, value);
14960    };
14961  }
14962
14963  function processQueue(state) {
14964    var fn, deferred, pending;
14965
14966    pending = state.pending;
14967    state.processScheduled = false;
14968    state.pending = undefined;
14969    for (var i = 0, ii = pending.length; i < ii; ++i) {
14970      deferred = pending[i][0];
14971      fn = pending[i][state.status];
14972      try {
14973        if (isFunction(fn)) {
14974          deferred.resolve(fn(state.value));
14975        } else if (state.status === 1) {
14976          deferred.resolve(state.value);
14977        } else {
14978          deferred.reject(state.value);
14979        }
14980      } catch (e) {
14981        deferred.reject(e);
14982        exceptionHandler(e);
14983      }
14984    }
14985  }
14986
14987  function scheduleProcessQueue(state) {
14988    if (state.processScheduled || !state.pending) return;
14989    state.processScheduled = true;
14990    nextTick(function() { processQueue(state); });
14991  }
14992
14993  function Deferred() {
14994    this.promise = new Promise();
14995    //Necessary to support unbound execution :/
14996    this.resolve = simpleBind(this, this.resolve);
14997    this.reject = simpleBind(this, this.reject);
14998    this.notify = simpleBind(this, this.notify);
14999  }
15000
15001  extend(Deferred.prototype, {
15002    resolve: function(val) {
15003      if (this.promise.$$state.status) return;
15004      if (val === this.promise) {
15005        this.$$reject($qMinErr(
15006          'qcycle',
15007          "Expected promise to be resolved with value other than 
15007itself '{0}'",
15008          val));
15009      } else {
15010        this.$$resolve(val);
15011      }
15012
15013    },
15014
15015    $$resolve: function(val) {
15016      var then, fns;
15017
15018      fns = callOnce(this, this.$$resolve, this.$$reject);
15019      try {
15020        if ((isObject(val) || isFunction(val))) then = val && val.then;
15021        if (isFunction(then)) {
15022          this.promise.$$state.status = -1;
15023          then.call(val, fns[0], fns[1], this.notify);
15024        } else {
15025          this.promise.$$state.value = val;
15026          this.promise.$$state.status = 1;
15027          scheduleProcessQueue(this.promise.$$state);
15028        }
15029      } catch (e) {
15030        fns[1](e);
15031        exceptionHandler(e);
15032      }
15033    },
15034
15035    reject: function(reason) {
15036      if (this.promise.$$state.status) return;
15037      this.$$reject(reason);
15038    },
15039
15040    $$reject: function(reason) {
15041      this.promise.$$state.value = reason;
15042      this.promise.$$state.status = 2;
15043      scheduleProcessQueue(this.promise.$$state);
15044    },
15045
15046    notify: function(progress) {
15047      var callbacks = this.promise.$$state.pending;
15048
15049      if ((this.promise.$$state.status <= 0) && callbacks && callbacks.length) {
15050        nextTick(function() {
15051          var callback, result;
15052          for (var i = 0, ii = callbacks.length; i < ii; i++) {
15053            result = callbacks[i][0];
15054            callback = callbacks[i][3];
15055            try {
15056              result.notify(isFunction(callback) ? callback(progress) : progress);
15057            } catch (e) {
15058              exceptionHandler(e);
15059            }
15060          }
15061        });
15062      }
15063    }
15064  });
15065
15066  /**
15067   * @ngdoc method
15068   * @name $q#reject
15069   * @kind function
15070   *
15071   * @description
15072   * Creates a promise that is resolved as rejected with the specified `reason`. This api should be
15073   * used to forward rejection in a chain of promises. If you are dealing with the last promise in
15074   * a promise chain, you don't need to worry about it.
15075   *
15076   * When comparing deferreds/promises to the familiar behavior of try/catch/throw, think of
15077   * `reject` as the `throw` keyword in JavaScript. This also means that if you "catch" an error via
15078   * a promise error callback and you want to forward the error to the promise derived from the
15079   * current promise, you have to "rethrow" the error by returning a rejection constructed via
15080   * `reject`.
15081   *
15082   * ```js
15083   *   promiseB = promiseA.then(function(result) {
15084   *     // success: do something and resolve promiseB
15085   *     //          with the old or a new result
15086   *     return result;
15087   *   }, function(reason) {
15088   *     // error: handle the error if possible and
15089   *     //        resolve promiseB with newPromiseOrValue,
15090   *     //        otherwise forward the rejection to promiseB
15091   *     if (canHandle(reason)) {
15092   *      // handle the error and recover
15093   *      return newPromiseOrValue;
15094   *     }
15095   *     return $q.reject(reason);
15096   *   });
15097   * ```
15098   *
15099   * @param {*} reason Constant, message, exception or an object representing the rejection reason.
15100   * @returns {Promise} Returns a promise that was already resolved as rejected with the `reason`.
15101   */
15102  var reject = function(reason) {
15103    var result = new Deferred();
15104    result.reject(reason);
15105    return result.promise;
15106  };
15107
15108  var makePromise = function makePromise(value, resolved) {
15109    var result = new Deferred();
15110    if (resolved) {
15111      result.resolve(value);
15112    } else {
15113      result.reject(value);
15114    }
15115    return result.promise;
15116  };
15117
15118  var handleCallback = function handleCallback(value, isResolved, callback) {
15119    var callbackOutput = null;
15120    try {
15121      if (isFunction(callback)) callbackOutput = callback();
15122    } catch (e) {
15123      return makePromise(e, false);
15124    }
15125    if (isPromiseLike(callbackOutput)) {
15126      return callbackOutput.then(function() {
15127        return makePromise(value, isResolved);
15128      }, function(error) {
15129        return makePromise(error, false);
15130      });
15131    } else {
15132      return makePromise(value, isResolved);
15133    }
15134  };
15135
15136  /**
15137   * @ngdoc method
15138   * @name $q#when
15139   * @kind function
15140   *
15141   * @description
15142   * Wraps an object that might be a value or a (3rd party) then-able promise into a $q promise.
15143   * This is useful when you are dealing with an object that might or might not be a promise, or if
15144   * the promise comes from a source that can't be trusted.
15145   *
15146   * @param {*} value Value or a promise
15147   * @param {Function=} successCallback
15148   * @param {Function=} errorCallback
15149   * @param {Function=} progressCallback
15150   * @returns {Promise} Returns a promise of the passed value or promise
15151   */
15152
15153
15154  var when = function(value, callback, errback, progressBack) {
15155    var result = new Deferred();
15156    result.resolve(value);
15157    return result.promise.then(callback, errback, progressBack);
15158  };
15159
15160  /**
15161   * @ngdoc method
15162   * @name $q#resolve
15163   * @kind function
15164   *
15165   * @description
15166   * Alias of {@link ng.$q#when when} to maintain naming consistency with ES6.
15167   *
15168   * @param {*} value Value or a promise
15169   * @param {Function=} successCallback
15170   * @param {Function=} errorCallback
15171   * @param {Function=} progressCallback
15172   * @returns {Promise} Returns a promise of the passed value or promise
15173   */
15174  var resolve = when;
15175
15176  /**
15177   * @ngdoc method
15178   * @name $q#all
15179   * @kind function
15180   *
15181   * @description
15182   * Combines multiple promises into a single promise that is resolved when all of the input
15183   * promises are resolved.
15184   *
15185   * @param {Array.<Promise>|Object.<Promise>} promises An array or hash of promises.
15186   * @returns {Promise} Returns a single promise that will be resolved with an array/hash of values,
15187   *   each value corresponding to the promise at the same index/key in the `promises` array/hash.
15188   *   If any of the promises is resolved with a rejection, this resulting promise will be rejected
15189   *   with the same rejection value.
15190   */
15191
15192  function all(promises) {
15193    var deferred = new Deferred(),
15194        counter = 0,
15195        results = isArray(promises) ? [] : {};
15196
15197    forEach(promises, function(promise, key) {
15198      counter++;
15199      when(promise).then(function(value) {
15200        if (results.hasOwnProperty(key)) return;
15201        results[key] = value;
15202        if (!(--counter)) deferred.resolve(results);
15203      }, function(reason) {
15204        if (results.hasOwnProperty(key)) return;
15205        deferred.reject(reason);
15206      });
15207    });
15208
15209    if (counter === 0) {
15210      deferred.resolve(results);
15211    }
15212
15213    return deferred.promise;
15214  }
15215
15216  var $Q = function Q(resolver) {
15217    if (!isFunction(resolver)) {
15218      throw $qMinErr('norslvr', "Expected resolverFn, got '{0}'", resolver);
15219    }
15220
15221    if (!(this instanceof Q)) {
15222      // More useful when $Q is the Promise itself.
15223      return new Q(resolver);
15224    }
15225
15226    var deferred = new Deferred();
15227
15228    function resolveFn(value) {
15229      deferred.resolve(value);
15230    }
15231
15232    function rejectFn(reason) {
15233      deferred.reject(reason);
15234    }
15235
15236    resolver(resolveFn, rejectFn);
15237
15238    return deferred.promise;
15239  };
15240
15241  $Q.defer = defer;
15242  $Q.reject = reject;
15243  $Q.when = when;
15244  $Q.resolve = resolve;
15245  $Q.all = all;
15246
15247  return $Q;
15248}
15249
15250function $$RAFProvider() { //rAF
15251  this.$get = ['$window', '$timeout', function($window, $timeout) {
15252    var requestAnimationFrame = $window.requestAnimationFrame ||
15253                                $window.webkitRequestAnimationFrame;
15254
15255    var cancelAnimationFrame = $window.cancelAnimationFrame ||
15256                               $window.webkitCancelAnimationFrame ||
15257                               $window.webkitCancelRequestAnimationFrame;
15258
15259    var rafSupported = !!requestAnimationFrame;
15260    var raf = rafSupported
15261      ? function(fn) {
15262          var id = requestAnimationFrame(fn);
15263          return function() {
15264            cancelAnimationFrame(id);
15265          };
15266        }
15267      : function(fn) {
15268          var timer = $timeout(fn, 16.66, false); // 1000 / 60 = 16.666
15269          return function() {
15270            $timeout.cancel(timer);
15271          };
15272        };
15273
15274    raf.supported = rafSupported;
15275
15276    return raf;
15277  }];
15278}
15279
15280/**
15281 * DESIGN NOTES
15282 *
15283 * The design decisions behind the scope are heavily favored for speed and memory consumption.
15284 *
15285 * The typical use of scope is to watch the expressions, which most of the time return the same
15286 * value as last time so we optimize the operation.
15287 *
15288 * Closures construction is expensive in terms of speed as well as memory:
15289 *   - No closures, instead use prototypical inheritance for API
15290 *   - Internal state needs to be stored on scope directly, which means that private state is
15291 *     exposed as $$____ properties
15292 *
15293 * Loop operations are optimized by using while(count--) { ... }
15294 *   - this means that in order to keep the same order of execution as addition we have to add
15295 *     items to the array at the beginning (unshift) instead of at the end (push)
15296 *
15297 * Child scopes are created and removed often
15298 *   - Using an array would be slow since inserts in middle are expensive so we use linked list
15299 *
15300 * There are few watches then a lot of observers. This is why you don't want the observer to be
15301 * implemented in the same way as watch. Watch requires return of initialization function which
15302 * are expensive to construct.
15303 */
15304
15305
15306/**
15307 * @ngdoc provider
15308 * @name $rootScopeProvider
15309 * @description
15310 *
15311 * Provider for the $rootScope service.
15312 */
15313
15314/**
15315 * @ngdoc method
15316 * @name $rootScopeProvider#digestTtl
15317 * @description
15318 *
15319 * Sets the number of `$digest` iterations the scope should attempt to execute before giving up and
15320 * assuming that the model is unstable.
15321 *
15322 * The current default is 10 iterations.
15323 *
15324 * In complex applications it's possible that the dependencies between `$watch`s will result in
15325 * several digest iterations. However if an application needs more than the default 10 digest
15326 * iterations for its model to stabilize then you should investigate what is causing the model to
15327 * continuously change during the digest.
15328 *
15329 * Increasing the TTL could have performance implications, so you should not change it without
15330 * proper justification.
15331 *
15332 * @param {number} limit The number of digest iterations.
15333 */
15334
15335
15336/**
15337 * @ngdoc service
15338 * @name $rootScope
15339 * @description
15340 *
15341 * Every application has a single root {@link ng.$rootScope.Scope scope}.
15342 * All other scopes are descendant scopes of the root scope. Scopes provide separation
15343 * between the model and the view, via a mechanism for watching the model for changes.
15344 * They also provide an event emission/broadcast and subscription facility. See the
15345 * {@link guide/scope developer guide on scopes}.
15346 */
15347function $RootScopeProvider() {
15348  var TTL = 10;
15349  var $rootScopeMinErr = minErr('$rootScope');
15350  var lastDirtyWatch = null;
15351  var applyAsyncId = null;
15352
15353  this.digestTtl = function(value) {
15354    if (arguments.length) {
15355      TTL = value;
15356    }
15357    return TTL;
15358  };
15359
15360  function createChildScopeClass(parent) {
15361    function ChildScope() {
vendor: 4,085 bytes, lines 15362-15475
15362      this.$$watchers = this.$$nextSibling =
15363          this.$$childHead = this.$$childTail = null;
15364      this.$$listeners = {};
15365      this.$$listenerCount = {};
15366      this.$$watchersCount = 0;
15367      this.$id = nextUid();
15368      this.$$ChildScope = null;
15369    }
15370    ChildScope.prototype = parent;
15371    return ChildScope;
15372  }
15373
15374  this.$get = ['$injector', '$exceptionHandler', '$parse', '$browser',
15375      function($injector, $exceptionHandler, $parse, $browser) {
15376
15377    function destroyChildScope($event) {
15378        $event.currentScope.$$destroyed = true;
15379    }
15380
15381    /**
15382     * @ngdoc type
15383     * @name $rootScope.Scope
15384     *
15385     * @description
15386     * A root scope can be retrieved using the {@link ng.$rootScope $rootScope} key from the
15387     * {@link auto.$injector $injector}. Child scopes are created using the
15388     * {@link ng.$rootScope.Scope#$new $new()} method. (Most scopes are created automatically when
15389     * compiled HTML template is executed.) See also the {@link guide/scope Scopes guide} for
15390     * an in-depth introduction and usage examples.
15391     *
15392     *
15393     * # Inheritance
15394     * A scope can inherit from a parent scope, as in this example:
15395     * ```js
15396         var parent = $rootScope;
15397         var child = parent.$new();
15398
15399         parent.salutation = "Hello";
15400         expect(child.salutation).toEqual('Hello');
15401
15402         child.salutation = "Welcome";
15403         expect(child.salutation).toEqual('Welcome');
15404         expect(parent.salutation).toEqual('Hello');
15405     * ```
15406     *
15407     * When interacting with `Scope` in tests, additional helper methods are available on the
15408     * instances of `Scope` type. See {@link ngMock.$rootScope.Scope ngMock Scope} for additional
15409     * details.
15410     *
15411     *
15412     * @param {Object.<string, function()>=} providers Map of service factory which need to be
15413     *                                       provided for the current scope. Defaults to {@link ng}.
15414     * @param {Object.<string, *>=} instanceCache Provides pre-instantiated services which should
15415     *                              append/override services provided by `providers`. This is handy
15416     *                              when unit-testing and having the need to override a default
15417     *                              service.
15418     * @returns {Object} Newly created scope.
15419     *
15420     */
15421    function Scope() {
15422      this.$id = nextUid();
15423      this.$$phase = this.$parent = this.$$watchers =
15424                     this.$$nextSibling = this.$$prevSibling =
15425                     this.$$childHead = this.$$childTail = null;
15426      this.$root = this;
15427      this.$$destroyed = false;
15428      this.$$listeners = {};
15429      this.$$listenerCount = {};
15430      this.$$watchersCount = 0;
15431      this.$$isolateBindings = null;
15432    }
15433
15434    /**
15435     * @ngdoc property
15436     * @name $rootScope.Scope#$id
15437     *
15438     * @description
15439     * Unique scope ID (monotonically increasing) useful for debugging.
15440     */
15441
15442     /**
15443      * @ngdoc property
15444      * @name $rootScope.Scope#$parent
15445      *
15446      * @description
15447      * Reference to the parent scope.
15448      */
15449
15450      /**
15451       * @ngdoc property
15452       * @name $rootScope.Scope#$root
15453       *
15454       * @description
15455       * Reference to the root scope.
15456       */
15457
15458    Scope.prototype = {
15459      constructor: Scope,
15460      /**
15461       * @ngdoc method
15462       * @name $rootScope.Scope#$new
15463       * @kind function
15464       *
15465       * @description
15466       * Creates a new child {@link ng.$rootScope.Scope scope}.
15467       *
15468       * The parent scope will propagate the {@link ng.$rootScope.Scope#$digest $digest()} event.
15469       * The scope can be removed from the scope hierarchy using {@link ng.$rootScope.Scope#$destroy $destroy()}.
15470       *
15471       * {@link ng.$rootScope.Scope#$destroy $destroy()} must be called on a scope when it is
15472       * desired for the scope and its child scopes to be permanently detached from the parent and
15473       * thus stop participating in model change detection and listener notification by invoking.
15474       *
15475       * @param {boolean} isolate If true, then the scope does not prototypically inherit from the
15476       *         parent scope. The scope is isolated, as it can not see parent scope properties.
15477       *         When creating widgets, it is useful for the widget to not accidentally read parent
15478       *         state.
15479       *
15480       * @param {Scope} [parent=this] The {@link ng.$rootScope.Scope `Scope`} that will be the `$parent`
15481       *                              of the newly created scope. Defaults to `this` scope if not provided.
15482       *                              This is used when creating a transclude scope to correctly place it
15483       *                              in the scope hierarchy while maintaining the correct prototypical
15484       *                              inheritance.
15485       *
15486       * @returns {Object} The newly created child scope.
15487       *
15488       */
15489      $new: function(isolate, parent) {
15490        var child;
15491
15492        parent = parent || this;
15493
15494        if (isolate) {
15495          child = new Scope();
15496          child.$root = this.$root;
15497        } else {
15498          // Only create a child scope class if somebody asks for one,
15499          // but cache it to allow the VM to optimize lookups.
15500          if (!this.$$ChildScope) {
15501            this.$$ChildScope = createChildScopeClass(this);
15502          }
15503          child = new this.$$ChildScope();
15504        }
15505        child.$parent = parent;
15506        child.$$prevSibling = parent.$$childTail;
15507        if (parent.$$childHead) {
15508          parent.$$childTail.$$nextSibling = child;
15509          parent.$$childTail = child;
15510        } else {
15511          parent.$$childHead = parent.$$childTail = child;
15512        }
15513
15514        // When the new scope is not isolated or we inherit from `this`, and
15515        // the parent scope is destroyed, the property `$$destroyed` is inherited
15516        // prototypically. In all other cases, this property needs to be set
15517        // when the parent scope is destroyed.
15518        // The listener needs to be added after the parent is set
15519        if (isolate || parent != this) child.$on('$destroy', destroyChildScope);
15520
15521        return child;
15522      },
15523
15524      /**
15525       * @ngdoc method
15526       * @name $rootScope.Scope#$watch
15527       * @kind function
15528       *
15529       * @description
15530       * Registers a `listener` callback to be executed whenever the `watchExpression` changes.
15531       *
15532       * - The `watchExpression` is called on every call to {@link ng.$rootScope.Scope#$digest
15533       *   $digest()} and should return the value that will be watched. (`watchExpression` should not change
15534       *   its value when executed multiple times with the same input because it may be executed multiple
15535       *   times by {@link ng.$rootScope.Scope#$digest $digest()}. That is, `watchExpression` should be
15536       *   [idempotent](http://en.wikipedia.org/wiki/Idempotence).
15537       * - The `listener` is called only when the value from the current `watchExpression` and the
15538       *   previous call to `watchExpression` are not equal (with the exception of the initial run,
15539       *   see below). Inequality is determined according to reference inequality,
15540       *   [strict comparison](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators)
15541       *    via the `!==` Javascript operator, unless `objectEquality == true`
15542       *   (see next point)
15543       * - When `objectEquality == true`, inequality of the `watchExpression` is determined
15544       *   according to the {@link angular.equals} function. To save the value of the object for
15545       *   later comparison, the {@link angular.copy} function is used. This therefore means that
15546       *   watching complex objects will have adverse memory and performance implications.
15547       * - The watch `listener` may change the model, which may trigger other `listener`s to fire.
15548       *   This is achieved by rerunning the watchers until no changes are detected. The rerun
15549       *   iteration limit is 10 to prevent an infinite loop deadlock.
15550       *
15551       *
15552       * If you want to be notified whenever {@link ng.$rootScope.Scope#$digest $digest} is called,
15553       * you can register a `watchExpression` function with no `listener`. (Be prepared for
15554       * multiple calls to your `watchExpression` because it will execute multiple times in a
15555       * single {@link ng.$rootScope.Scope#$digest $digest} cycle if a change is detected.)
15556       *
15557       * After a watcher is registered with the scope, the `listener` fn is called asynchronously
15558       * (via {@link ng.$rootScope.Scope#$evalAsync $evalAsync}) to initialize the
15559       * watcher. In rare cases, this is undesirable because the listener is called when the result
15560       * of `watchExpression` didn't change. To detect this scenario within the `listener` fn, you
15561       * can compare the `newVal` and `oldVal`. If these two values are identical (`===`) then the
15562       * listener was called due to initialization.
15563       *
15564       *
15565       *
15566       * # Example
15567       * ```js
15568           // let's assume that scope was dependency injected as the $rootScope
15569           var scope = $rootScope;
15570           scope.name = 'misko';
15571           scope.counter = 0;
15572
15573           expect(scope.counter).toEqual(0);
15574           scope.$watch('name', function(newValue, oldValue) {
15575             scope.counter = scope.counter + 1;
15576           });
15577           expect(scope.counter).toEqual(0);
15578
15579           scope.$digest();
15580           // the listener is always called during the first $digest loop after it was registered
15581           expect(scope.counter).toEqual(1);
15582
15583           scope.$digest();
15584           // but now it will not be called unless the value changes
15585           expect(scope.counter).toEqual(1);
15586
15587           scope.name = 'adam';
15588           scope.$digest();
15589           expect(scope.counter).toEqual(2);
15590
15591
15592
15593           // Using a function as a watchExpression
15594           var food;
15595           scope.foodCounter = 0;
15596           expect(scope.foodCounter).toEqual(0);
15597           scope.$watch(
15598             // This function returns the value being watched. It is called for each turn of the $digest loop
15599             function() { return food; },
15600             // This is the change listener, called when the value returned from the above function changes
15601             function(newValue, oldValue) {
15602               if ( newValue !== oldValue ) {
15603                 // Only increment the counter if the value changed
15604                 scope.foodCounter = scope.foodCounter + 1;
15605               }
15606             }
15607           );
15608           // No digest has been run so the counter will be zero
15609           expect(scope.foodCounter).toEqual(0);
15610
15611           // Run the digest but since food has not changed count will still be zero
15612           scope.$digest();
15613           expect(scope.foodCounter).toEqual(0);
15614
15615           // Update food and run digest.  Now the counter will increment
15616           food = 'cheeseburger';
15617           scope.$digest();
15618           expect(scope.foodCounter).toEqual(1);
15619
15620       * ```
15621       *
15622       *
15623       *
15624       * @param {(function()|string)} watchExpression Expression that is evaluated on each
15625       *    {@link ng.$rootScope.Scope#$digest $digest} cycle. A change in the return value triggers
15626       *    a call to the `listener`.
15627       *
15628       *    - `string`: Evaluated as {@link guide/expression expression}
15629       *    - `function(scope)`: called with current `scope` as a parameter.
15630       * @param {function(newVal, oldVal, scope)} listener Callback called whenever the value
15631       *    of `watchExpression` changes.
15632       *
15633       *    - `newVal` contains the current value of the `watchExpression`
15634       *    - `oldVal` contains the previous value of the `watchExpression`
15635       *    - `scope` refers to the current scope
15636       * @param {boolean=} objectEquality Compare for object equality using {@link angular.equals} instead of
15637       *     comparing for reference equality.
15638       * @returns {function()} Returns a deregistration function for this listener.
15639       */
15640      $watch: function(watchExp, listener, objectEquality, prettyPrintExpression) {
15641        var get = $parse(watchExp);
15642
15643        if (get.$$watchDelegate) {
15644          return get.$$watchDelegate(this, listener, objectEquality, get, watchExp);
15645        }
15646        var scope = this,
15647            array = scope.$$watchers,
15648            watcher = {
15649              fn: listener,
15650              last: initWatchVal,
15651              get: get,
15652              exp: prettyPrintExpression || watchExp,
15653              eq: !!objectEquality
15654            };
15655
15656        lastDirtyWatch = null;
15657
15658        if (!isFunction(listener)) {
15659          watcher.fn = noop;
15660        }
15661
15662        if (!array) {
15663          array = scope.$$watchers = [];
15664        }
15665        // we use unshift since we use a while loop in $digest for speed.
15666        // the while loop reads in reverse order.
15667        array.unshift(watcher);
15668        incrementWatchersCount(this, 1);
15669
15670        return function deregisterWatch() {
15671          if (arrayRemove(array, watcher) >= 0) {
15672            incrementWatchersCount(scope, -1);
15673          }
15674          lastDirtyWatch = null;
15675        };
15676      },
15677
15678      /**
15679       * @ngdoc method
15680       * @name $rootScope.Scope#$watchGroup
15681       * @kind function
15682       *
15683       * @description
15684       * A variant of {@link ng.$rootScope.Scope#$watch $watch()} where it watches an array of `watchExpressions`.
15685       * If any one expression in the collection changes the `listener` is executed.
15686       *
15687       * - The items in the `watchExpressions` array are observed via standard $watch operation and are examined on every
15688       *   call to $digest() to see if any items changes.
15689       * - The `listener` is called whenever any expression in the `watchExpressions` array changes.
15690       *
15691       * @param {Array.<string|Function(scope)>} watchExpressions Array of expressions that will be individually
15692       * watched using {@link ng.$rootScope.Scope#$watch $watch()}
15693       *
15694       * @param {function(newValues, oldValues, scope)} listener Callback called whenever the return value of any
15695       *    expression in `watchExpressions` changes
15696       *    The `newValues` array contains the current values of the `watchExpressions`, with the indexes matching
15697       *    those of `watchExpression`
15698       *    and the `oldValues` array contains the previous values of the `watchExpressions`, with the indexes matching
15699       *    those of `watchExpression`
15700       *    The `scope` refers to the current scope.
15701       * @returns {function()} Returns a de-registration function for all listeners.
15702       */
15703      $watchGroup: function(watchExpressions, listener) {
15704        var oldValues = new Array(watchExpressions.length);
15705        var newValues = new Array(watchExpressions.length);
15706        var deregisterFns = [];
15707        var self = this;
15708        var changeReactionScheduled = false;
vendor: 10,192 bytes, lines 15709-15971
15709        var firstRun = true;
15710
15711        if (!watchExpressions.length) {
15712          // No expressions means we call the listener ASAP
15713          var shouldCall = true;
15714          self.$evalAsync(function() {
15715            if (shouldCall) listener(newValues, newValues, self);
15716          });
15717          return function deregisterWatchGroup() {
15718            shouldCall = false;
15719          };
15720        }
15721
15722        if (watchExpressions.length === 1) {
15723          // Special case size of one
15724          return this.$watch(watchExpressions[0], function watchGroupAction(value, oldValue, scope) {
15725            newValues[0] = value;
15726            oldValues[0] = oldValue;
15727            listener(newValues, (value === oldValue) ? newValues : oldValues, scope);
15728          });
15729        }
15730
15731        forEach(watchExpressions, function(expr, i) {
15732          var unwatchFn = self.$watch(expr, function watchGroupSubAction(value, oldValue) {
15733            newValues[i] = value;
15734            oldValues[i] = oldValue;
15735            if (!changeReactionScheduled) {
15736              changeReactionScheduled = true;
15737              self.$evalAsync(watchGroupAction);
15738            }
15739          });
15740          deregisterFns.push(unwatchFn);
15741        });
15742
15743        function watchGroupAction() {
15744          changeReactionScheduled = false;
15745
15746          if (firstRun) {
15747            firstRun = false;
15748            listener(newValues, newValues, self);
15749          } else {
15750            listener(newValues, oldValues, self);
15751          }
15752        }
15753
15754        return function deregisterWatchGroup() {
15755          while (deregisterFns.length) {
15756            deregisterFns.shift()();
15757          }
15758        };
15759      },
15760
15761
15762      /**
15763       * @ngdoc method
15764       * @name $rootScope.Scope#$watchCollection
15765       * @kind function
15766       *
15767       * @description
15768       * Shallow watches the properties of an object and fires whenever any of the properties change
15769       * (for arrays, this implies watching the array items; for object maps, this implies watching
15770       * the properties). If a change is detected, the `listener` callback is fired.
15771       *
15772       * - The `obj` collection is observed via standard $watch operation and is examined on every
15773       *   call to $digest() to see if any items have been added, removed, or moved.
15774       * - The `listener` is called whenever anything within the `obj` has changed. Examples include
15775       *   adding, removing, and moving items belonging to an object or array.
15776       *
15777       *
15778       * # Example
15779       * ```js
15780          $scope.names = ['igor', 'matias', 'misko', 'james'];
15781          $scope.dataCount = 4;
15782
15783          $scope.$watchCollection('names', function(newNames, oldNames) {
15784            $scope.dataCount = newNames.length;
15785          });
15786
15787          expect($scope.dataCount).toEqual(4);
15788          $scope.$digest();
15789
15790          //still at 4 ... no changes
15791          expect($scope.dataCount).toEqual(4);
15792
15793          $scope.names.pop();
15794          $scope.$digest();
15795
15796          //now there's been a change
15797          expect($scope.dataCount).toEqual(3);
15798       * ```
15799       *
15800       *
15801       * @param {string|function(scope)} obj Evaluated as {@link guide/expression expression}. The
15802       *    expression value should evaluate to an object or an array which is observed on each
15803       *    {@link ng.$rootScope.Scope#$digest $digest} cycle. Any shallow change within the
15804       *    collection will trigger a call to the `listener`.
15805       *
15806       * @param {function(newCollection, oldCollection, scope)} listener a callback function called
15807       *    when a change is detected.
15808       *    - The `newCollection` object is the newly modified data obtained from the `obj` expression
15809       *    - The `oldCollection` object is a copy of the former collection data.
15810       *      Due to performance considerations, the`oldCollection` value is computed only if the
15811       *      `listener` function declares two or more arguments.
15812       *    - The `scope` argument refers to the current scope.
15813       *
15814       * @returns {function()} Returns a de-registration function for this listener. When the
15815       *    de-registration function is executed, the internal watch operation is terminated.
15816       */
15817      $watchCollection: function(obj, listener) {
15818        $watchCollectionInterceptor.$stateful = true;
15819
15820        var self = this;
15821        // the current value, updated on each dirty-check run
15822        var newValue;
15823        // a shallow copy of the newValue from the last dirty-check run,
15824        // updated to match newValue during dirty-check run
15825        var oldValue;
15826        // a shallow copy of the newValue from when the last change happened
15827        var veryOldValue;
15828        // only track veryOldValue if the listener is asking for it
15829        var trackVeryOldValue = (listener.length > 1);
15830        var changeDetected = 0;
15831        var changeDetector = $parse(obj, $watchCollectionInterceptor);
15832        var internalArray = [];
15833        var internalObject = {};
15834        var initRun = true;
15835        var oldLength = 0;
15836
15837        function $watchCollectionInterceptor(_value) {
15838          newValue = _value;
15839          var newLength, key, bothNaN, newItem, oldItem;
15840
15841          // If the new value is undefined, then return undefined as the watch may be a one-time watch
15842          if (isUndefined(newValue)) return;
15843
15844          if (!isObject(newValue)) { // if primitive
15845            if (oldValue !== newValue) {
15846              oldValue = newValue;
15847              changeDetected++;
15848            }
15849          } else if (isArrayLike(newValue)) {
15850            if (oldValue !== internalArray) {
15851              // we are transitioning from something which was not an array into array.
15852              oldValue = internalArray;
15853              oldLength = oldValue.length = 0;
15854              changeDetected++;
15855            }
15856
15857            newLength = newValue.length;
15858
15859            if (oldLength !== newLength) {
15860              // if lengths do not match we need to trigger change notification
15861              changeDetected++;
15862              oldValue.length = oldLength = newLength;
15863            }
15864            // copy the items to oldValue and look for changes.
15865            for (var i = 0; i < newLength; i++) {
15866              oldItem = oldValue[i];
15867              newItem = newValue[i];
15868
15869              bothNaN = (oldItem !== oldItem) && (newItem !== newItem);
15870              if (!bothNaN && (oldItem !== newItem)) {
15871                changeDetected++;
15872                oldValue[i] = newItem;
15873              }
15874            }
15875          } else {
15876            if (oldValue !== internalObject) {
15877              // we are transitioning from something which was not an object into object.
15878              oldValue = internalObject = {};
15879              oldLength = 0;
15880              changeDetected++;
15881            }
15882            // copy the items to oldValue and look for changes.
15883            newLength = 0;
15884            for (key in newValue) {
15885              if (hasOwnProperty.call(newValue, key)) {
15886                newLength++;
15887                newItem = newValue[key];
15888                oldItem = oldValue[key];
15889
15890                if (key in oldValue) {
15891                  bothNaN = (oldItem !== oldItem) && (newItem !== newItem);
15892                  if (!bothNaN && (oldItem !== newItem)) {
15893                    changeDetected++;
15894                    oldValue[key] = newItem;
15895                  }
15896                } else {
15897                  oldLength++;
15898                  oldValue[key] = newItem;
15899                  changeDetected++;
15900                }
15901              }
15902            }
15903            if (oldLength > newLength) {
15904              // we used to have more keys, need to find them and destroy them.
15905              changeDetected++;
15906              for (key in oldValue) {
15907                if (!hasOwnProperty.call(newValue, key)) {
15908                  oldLength--;
15909                  delete oldValue[key];
15910                }
15911              }
15912            }
15913          }
15914          return changeDetected;
15915        }
15916
15917        function $watchCollectionAction() {
15918          if (initRun) {
15919            initRun = false;
15920            listener(newValue, newValue, self);
15921          } else {
15922            listener(newValue, veryOldValue, self);
15923          }
15924
15925          // make a copy for the next time a collection is changed
15926          if (trackVeryOldValue) {
15927            if (!isObject(newValue)) {
15928              //primitive
15929              veryOldValue = newValue;
15930            } else if (isArrayLike(newValue)) {
15931              veryOldValue = new Array(newValue.length);
15932              for (var i = 0; i < newValue.length; i++) {
15933                veryOldValue[i] = newValue[i];
15934              }
15935            } else { // if object
15936              veryOldValue = {};
15937              for (var key in newValue) {
15938                if (hasOwnProperty.call(newValue, key)) {
15939                  veryOldValue[key] = newValue[key];
15940                }
15941              }
15942            }
15943          }
15944        }
15945
15946        return this.$watch(changeDetector, $watchCollectionAction);
15947      },
15948
15949      /**
15950       * @ngdoc method
15951       * @name $rootScope.Scope#$digest
15952       * @kind function
15953       *
15954       * @description
15955       * Processes all of the {@link ng.$rootScope.Scope#$watch watchers} of the current scope and
15956       * its children. Because a {@link ng.$rootScope.Scope#$watch watcher}'s listener can change
15957       * the model, the `$digest()` keeps calling the {@link ng.$rootScope.Scope#$watch watchers}
15958       * until no more listeners are firing. This means that it is possible to get into an infinite
15959       * loop. This function will throw `'Maximum iteration limit exceeded.'` if the number of
15960       * iterations exceeds 10.
15961       *
15962       * Usually, you don't call `$digest()` directly in
15963       * {@link ng.directive:ngController controllers} or in
15964       * {@link ng.$compileProvider#directive directives}.
15965       * Instead, you should call {@link ng.$rootScope.Scope#$apply $apply()} (typically from within
15966       * a {@link ng.$compileProvider#directive directive}), which will force a `$digest()`.
15967       *
15968       * If you want to be notified whenever `$digest()` is called,
15969       * you can register a `watchExpression` function with
15970       * {@link ng.$rootScope.Scope#$watch $watch()} with no `listener`.
15971       *
15972       * In unit tests, you may need to call `$digest()` to simulate the scope life cycle.
15973       *
15974       * # Example
15975       * ```js
15976           var scope = ...;
15977           scope.name = 'misko';
15978           scope.counter = 0;
15979
15980           expect(scope.counter).toEqual(0);
15981           scope.$watch('name', function(newValue, oldValue) {
15982             scope.counter = scope.counter + 1;
15983           });
15984           expect(scope.counter).toEqual(0);
15985
15986           scope.$digest();
15987           // the listener is always called during the first $digest loop after it was registered
15988           expect(scope.counter).toEqual(1);
15989
15990           scope.$digest();
15991           // but now it will not be called unless the value changes
15992           expect(scope.counter).toEqual(1);
15993
15994           scope.name = 'adam';
15995           scope.$digest();
15996           expect(scope.counter).toEqual(2);
15997       * ```
15998       *
15999       */
16000      $digest: function() {
16001        var watch, value, last,
16002            watchers,
16003            length,
16004            dirty, ttl = TTL,
16005            next, current, target = this,
16006            watchLog = [],
16007            logIdx, logMsg, asyncTask;
16008
16009        beginPhase('$digest');
16010        // Check for changes to browser url that happened in sync before the call to $digest
16011        $browser.$$checkUrlChange();
16012
16013        if (this === $rootScope && applyAsyncId !== null) {
16014          // If this is the root scope, and $applyAsync has scheduled a deferred $apply(), then
16015          // cancel the scheduled $apply and flush the queue of expressions to be evaluated.
16016          $browser.defer.cancel(applyAsyncId);
16017          flushApplyAsync();
16018        }
16019
16020        lastDirtyWatch = null;
16021
16022        do { // "while dirty" loop
16023          dirty = false;
16024          current = target;
16025
16026          while (asyncQueue.length) {
16027            try {
16028              asyncTask = asyncQueue.shift();
16029              asyncTask.scope.$eval(asyncTask.expression, asyncTask.locals);
16030            } catch (e) {
16031              $exceptionHandler(e);
16032            }
16033            lastDirtyWatch = null;
16034          }
16035
16036          traverseScopesLoop:
16037          do { // "traverse the scopes" loop
16038            if ((watchers = current.$$watchers)) {
16039              // process our watches
16040              length = watchers.length;
16041              while (length--) {
16042                try {
16043                  watch = watchers[length];
16044                  // Most common watches are on primitives, in which case we can short
16045                  // circuit it with === operator, only when === fails do we use .equals
16046                  if (watch) {
16047                    if ((value = watch.get(current)) !== (last = watch.last) &&
16048                        !(watch.eq
16049                            ? equals(value, last)
16050                            : (typeof value === 'number' && typeof last === 'number'
16051                               && isNaN(value) && isNaN(last)))) {
16052                      dirty = true;
16053                      lastDirtyWatch = watch;
16054                      watch.last = watch.eq ? copy(value, null) : value;
16055                      watch.fn(value, ((last === initWatchVal) ? value : last), current);
16056                      if (ttl < 5) {
16057                        logIdx = 4 - ttl;
16058                        if (!watchLog[logIdx]) watchLog[logIdx] = [];
16059                        watchLog[logIdx].push({
16060                          msg: isFunction(watch.exp) ? 'fn: ' + (watch.exp.name || watch.exp.toString()) : watch.exp,
16061                          newVal: value,
16062                          oldVal: last
16063                        });
16064                      }
16065                    } else if (watch === lastDirtyWatch) {
16066                      // If the most recently dirty watcher is now clean, short circuit since the remaining watchers
16067                      // have already been tested.
16068                      dirty = false;
16069                      break traverseScopesLoop;
16070                    }
16071                  }
16072                } catch (e) {
16073                  $exceptionHandler(e);
16074                }
16075              }
16076            }
16077
16078            // Insanity Warning: scope depth-first traversal
16079            // yes, this code is a bit crazy, but it works and we have tests to prove it!
16080            // this piece should be kept in sync with the traversal in $broadcast
16081            if (!(next = ((current.$$watchersCount && current.$$childHead) ||
16082                (current !== target && current.$$nextSibling)))) {
16083              while (current !== target && !(next = current.$$nextSibling)) {
16084                current = current.$parent;
16085              }
16086            }
16087          } while ((current = next));
16088
16089          // `break traverseScopesLoop;` takes us to here
16090
16091          if ((dirty || asyncQueue.length) && !(ttl--)) {
16092            clearPhase();
16093            throw $rootScopeMinErr('infdig',
16094                '{0} $digest() iterations reached. Aborting!\n' +
16095                'Watchers fired in the last 5 iterations: {1}',
16096                TTL, watchLog);
16097          }
16098
16099        }
vendor: 5,032 bytes, lines 16099-16225
16099 while (dirty || asyncQueue.length);
16100
16101        clearPhase();
16102
16103        while (postDigestQueue.length) {
16104          try {
16105            postDigestQueue.shift()();
16106          } catch (e) {
16107            $exceptionHandler(e);
16108          }
16109        }
16110      },
16111
16112
16113      /**
16114       * @ngdoc event
16115       * @name $rootScope.Scope#$destroy
16116       * @eventType broadcast on scope being destroyed
16117       *
16118       * @description
16119       * Broadcasted when a scope and its children are being destroyed.
16120       *
16121       * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to
16122       * clean up DOM bindings before an element is removed from the DOM.
16123       */
16124
16125      /**
16126       * @ngdoc method
16127       * @name $rootScope.Scope#$destroy
16128       * @kind function
16129       *
16130       * @description
16131       * Removes the current scope (and all of its children) from the parent scope. Removal implies
16132       * that calls to {@link ng.$rootScope.Scope#$digest $digest()} will no longer
16133       * propagate to the current scope and its children. Removal also implies that the current
16134       * scope is eligible for garbage collection.
16135       *
16136       * The `$destroy()` is usually used by directives such as
16137       * {@link ng.directive:ngRepeat ngRepeat} for managing the
16138       * unrolling of the loop.
16139       *
16140       * Just before a scope is destroyed, a `$destroy` event is broadcasted on this scope.
16141       * Application code can register a `$destroy` event handler that will give it a chance to
16142       * perform any necessary cleanup.
16143       *
16144       * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to
16145       * clean up DOM bindings before an element is removed from the DOM.
16146       */
16147      $destroy: function() {
16148        // We can't destroy a scope that has been already destroyed.
16149        if (this.$$destroyed) return;
16150        var parent = this.$parent;
16151
16152        this.$broadcast('$destroy');
16153        this.$$destroyed = true;
16154
16155        if (this === $rootScope) {
16156          //Remove handlers attached to window when $rootScope is removed
16157          $browser.$$applicationDestroyed();
16158        }
16159
16160        incrementWatchersCount(this, -this.$$watchersCount);
16161        for (var eventName in this.$$listenerCount) {
16162          decrementListenerCount(this, this.$$listenerCount[eventName], eventName);
16163        }
16164
16165        // sever all the references to parent scopes (after this cleanup, the current scope should
16166        // not be retained by any of our references and should be eligible for garbage collection)
16167        if (parent && parent.$$childHead == this) parent.$$childHead = this.$$nextSibling;
16168        if (parent && parent.$$childTail == this) parent.$$childTail = this.$$prevSibling;
16169        if (this.$$prevSibling) this.$$prevSibling.$$nextSibling = this.$$nextSibling;
16170        if (this.$$nextSibling) this.$$nextSibling.$$prevSibling = this.$$prevSibling;
16171
16172        // Disable listeners, watchers and apply/digest methods
16173        this.$destroy = this.$digest = this.$apply = this.$evalAsync = this.$applyAsync = noop;
16174        this.$on = this.$watch = this.$watchGroup = function() { return noop; };
16175        this.$$listeners = {};
16176
16177        // All of the code below is bogus code that works around V8's memory leak via optimized code
16178        // and inline caches.
16179        //
16180        // see:
16181        // - https://code.google.com/p/v8/issues/detail?id=2073#c26
16182        // - https://github.com/angular/angular.js/issues/6794#issuecomment-38648909
16183        // - https://github.com/angular/angular.js/issues/1313#issuecomment-10378451
16184
16185        this.$parent = this.$$nextSibling = this.$$prevSibling = this.$$childHead =
16186            this.$$childTail = this.$root = this.$$watchers = null;
16187      },
16188
16189      /**
16190       * @ngdoc method
16191       * @name $rootScope.Scope#$eval
16192       * @kind function
16193       *
16194       * @description
16195       * Executes the `expression` on the current scope and returns the result. Any exceptions in
16196       * the expression are propagated (uncaught). This is useful when evaluating Angular
16197       * expressions.
16198       *
16199       * # Example
16200       * ```js
16201           var scope = ng.$rootScope.Scope();
16202           scope.a = 1;
16203           scope.b = 2;
16204
16205           expect(scope.$eval('a+b')).toEqual(3);
16206           expect(scope.$eval(function(scope){ return scope.a + scope.b; })).toEqual(3);
16207       * ```
16208       *
16209       * @param {(string|function())=} expression An angular expression to be executed.
16210       *
16211       *    - `string`: execute using the rules as defined in  {@link guide/expression expression}.
16212       *    - `function(scope)`: execute the function with the current `scope` parameter.
16213       *
16214       * @param {(object)=} locals Local variables object, useful for overriding values in scope.
16215       * @returns {*} The result of evaluating the expression.
16216       */
16217      $eval: function(expr, locals) {
16218        return $parse(expr)(this, locals);
16219      },
16220
16221      /**
16222       * @ngdoc method
16223       * @name $rootScope.Scope#$evalAsync
16224       * @kind function
16225       *
16226       * @description
16227       * Executes the expression on the current scope at a later point in time.
16228       *
16229       * The `$evalAsync` makes no guarantees as to when the `expression` will be executed, only
16230       * that:
16231       *
16232       *   - it will execute after the function that scheduled the evaluation (preferably before DOM
16233       *     rendering).
16234       *   - at least one {@link ng.$rootScope.Scope#$digest $digest cycle} will be performed after
16235       *     `expression` execution.
16236       *
16237       * Any exceptions from the execution of the expression are forwarded to the
16238       * {@link ng.$exceptionHandler $exceptionHandler} service.
16239       *
16240       * __Note:__ if this function is called outside of a `$digest` cycle, a new `$digest` cycle
16241       * will be scheduled. However, it is encouraged to always call code that changes the model
16242       * from within an `$apply` call. That includes code evaluated via `$evalAsync`.
16243       *
16244       * @param {(string|function())=} expression An angular expression to be executed.
16245       *
16246       *    - `string`: execute using the rules as defined in {@link guide/expression expression}.
16247       *    - `function(scope)`: execute the function with the current `scope` parameter.
16248       *
16249       * @param {(object)=} locals Local variables object, useful for overriding values in scope.
16250       */
16251      $evalAsync: function(expr, locals) {
16252        // if we are outside of an $digest loop and this is the first time we are scheduling async
16253        // task also schedule async auto-flush
16254        if (!$rootScope.$$phase && !asyncQueue.length) {
16255          $browser.defer(function() {
16256            if (asyncQueue.length) {
16257              $rootScope.$digest();
16258            }
16259          });
16260        }
16261
16262        asyncQueue.push({scope: this, expression: expr, locals: locals});
16263      },
16264
16265      $$postDigest: function(fn) {
16266        postDigestQueue.push(fn);
16267      },
16268
16269      /**
16270       * @ngdoc method
16271       * @name $rootScope.Scope#$apply
16272       * @kind function
16273       *
16274       * @description
16275       * `$apply()` is used to execute an expression in angular from outside of the angular
16276       * framework. (For example from browser DOM events, setTimeout, XHR or third party libraries).
16277       * Because we are calling into the angular framework we need to perform proper scope life
16278       * cycle of {@link ng.$exceptionHandler exception handling},
16279       * {@link ng.$rootScope.Scope#$digest executing watches}.
16280       *
16281       * ## Life cycle
16282       *
16283       * # Pseudo-Code of `$apply()`
16284       * ```js
16285           function $apply(expr) {
16286             try {
16287               return $eval(expr);
16288             } catch (e) {
16289               $exceptionHandler(e);
16290             } finally {
16291               $root.$digest();
16292             }
16293           }
16294       * ```
16295       *
16296       *
16297       * Scope's `$apply()` method transitions through the following stages:
16298       *
16299       * 1. The {@link guide/expression expression} is executed using the
16300       *    {@link ng.$rootScope.Scope#$eval $eval()} method.
16301       * 2. Any exceptions from the execution of the expression are forwarded to the
16302       *    {@link ng.$exceptionHandler $exceptionHandler} service.
16303       * 3. The {@link ng.$rootScope.Scope#$watch watch} listeners are fired immediately after the
16304       *    expression was executed using the {@link ng.$rootScope.Scope#$digest $digest()} method.
16305       *
16306       *
16307       * @param {(string|function())=} exp An angular expression to be executed.
16308       *
16309       *    - `string`: execute using the rules as defined in {@link guide/expression expression}.
16310       *    - `function(scope)`: execute the function with current `scope` parameter.
16311       *
16312       * @returns {*} The result of evaluating the expression.
16313       */
16314      $apply: function(expr) {
16315        try {
16316          beginPhase('$apply');
16317          try {
16318            return this.$eval(expr);
16319          } finally {
16320            clearPhase();
16321          }
16322        } catch (e) {
16323          $exceptionHandler(e);
16324        } finally {
16325          try {
16326            $rootScope.$digest();
16327          } catch (e) {
16328            $exceptionHandler(e);
16329            throw e;
16330          }
16331        }
16332      },
16333
16334      /**
16335       * @ngdoc method
16336       * @name $rootScope.Scope#$applyAsync
16337       * @kind function
16338       *
16339       * @description
16340       * Schedule the invocation of $apply to occur at a later time. The actual time difference
16341       * varies across browsers, but is typically around ~10 milliseconds.
16342       *
16343       * This can be used to queue up multiple expressions which need to be evaluated in the same
16344       * digest.
16345       *
16346       * @param {(string|function())=} exp An angular expression to be executed.
16347       *
16348       *    - `string`: execute using the rules as defined in {@link guide/expression expression}.
16349       *    - `function(scope)`: execute the function with current `scope` parameter.
16350       */
16351      $applyAsync: function(expr) {
16352        var scope = this;
16353        expr && applyAsyncQueue.push($applyAsyncExpression);
16354        scheduleApplyAsync();
16355
16356        function $applyAsyncExpression() {
16357          scope.$eval(expr);
16358        }
16359      },
16360
16361      /**
16362       * @ngdoc method
16363       * @name $rootScope.Scope#$on
16364       * @kind function
16365       *
16366       * @description
16367       * Listens on events of a given type. See {@link ng.$rootScope.Scope#$emit $emit} for
16368       * discussion of event life cycle.
16369       *
16370       * The event listener function format is: `function(event, args...)`. The `event` object
16371       * passed into the listener has the following attributes:
16372       *
16373       *   - `targetScope` - `{Scope}`: the scope on which the event was `$emit`-ed or
16374       *     `$broadcast`-ed.
16375       *   - `currentScope` - `{Scope}`: the scope that is currently handling the event. Once the
16376       *     event propagates through the scope hierarchy, this property is set to null.
16377       *   - `name` - `{string}`: name of the event.
16378       *   - `stopPropagation` - `{function=}`: calling `stopPropagation` function will cancel
16379       *     further event propagation (available only for events that were `$emit`-ed).
16380       *   - `preventDefault` - `{function}`: calling `preventDefault` sets `defaultPrevented` flag
16381       *     to true.
16382       *   - `defaultPrevented` - `{boolean}`: true if `preventDefault` was called.
16383       *
16384       * @param {string} name Event name to listen on.
16385       * @param {function(event, ...args)} listener Function to call when the event is emitted.
16386       * @returns {function()} Returns a deregistration function for this listener.
16387       */
16388      $on: function(name, listener) {
16389        var namedListeners = this.$$listeners[name];
16390        if (!namedListeners) {
16391          this.$$listeners[name] = namedListeners = [];
16392        }
16393        namedListeners.push(listener);
16394
16395        var current = this;
16396        do {
16397          if (!current.$$listenerCount[name]) {
16398            current.$$listenerCount[name] = 0;
16399          }
16400          current.$$listenerCount[name]++;
16401        } while ((current = current.$parent));
16402
16403        var self = this;
16404        return function() {
16405          var indexOfListener = namedListeners.indexOf(listener);
16406          if (indexOfListener !== -1) {
16407            namedListeners[indexOfListener] = null;
16408            decrementListenerCount(self, 1, name);
16409          }
16410        };
16411      },
16412
16413
16414      /**
16415       * @ngdoc method
16416       * @name $rootScope.Scope#$emit
16417       * @kind function
16418       *
16419       * @description
16420       * Dispatches an event `name` upwards through the scope hierarchy notifying the
16421       * registered {@link ng.$rootScope.Scope#$on} listeners.
16422       *
16423       * The event life cycle starts at the scope on which `$emit` was called. All
16424       * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get
16425       * notified. Afterwards, the event traverses upwards toward the root scope and calls all
16426       * registered listeners along the way. The event will stop propagating if one of the listeners
16427       * cancels it.
16428       *
16429       * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
16430       * onto the {@link ng.$exceptionHandler $exceptionHandler} service.
16431       *
16432       * @param {string} name Event name to emit.
16433       * @param {...*} args Optional one or more arguments which will be passed onto the event listeners.
16434       * @return {Object} Event object (see {@link ng.$rootScope.Scope#$on}).
16435       */
16436      $emit: function(name, args) {
16437        var empty = [],
16438            namedListeners,
16439            scope = this,
16440            stopPropagation = false,
16441            event = {
16442              name: name,
16443              targetScope: scope,
16444              stopPropagation: function() {stopPropagation = true;},
16445              preventDefault: function() {
16446                event.defaultPrevented = true;
16447              },
16448              defaultPrevented: false
16449            },
16450            listenerArgs = concat([event], arguments, 1),
16451            i, length;
16452
16453        do {
16454          namedListeners = scope.$$listeners[name] || empty;
16455          event.currentScope = scope;
16456          for (i = 0, length = namedListeners.length; i < length; i++) {
16457
16458            // if listeners were deregistered, defragment the array
16459            if (!namedListeners[i]) {
16460              namedListeners.splice(i, 1);
16461              i--;
16462              length--;
16463              continue;
16464            }
16465            try {
16466              //allow all listeners attached to the current scope to run
16467              namedListeners[i].apply(null, listenerArgs);
16468            } catch (e) {
16469              $exceptionHandler(e);
16470            }
16471          }
16472          //if any listener on the current scope stops propagation, prevent bubbling
16473          if (stopPropagation) {
16474            event.currentScope = null;
16475            return event;
16476          }
16477          //traverse upwards
16478          scope = scope.$parent;
16479        } while (scope);
16480
16481        event.currentScope = null;
16482
16483        return event;
16484      },
16485
16486
16487      /**
16488       * @ngdoc method
16489       * @name $rootScope.Scope#$broadcast
16490       * @kind function
16491       *
16492       * @description
16493       * Dispatches an event `name` downwards to all child scopes (and their children) notifying the
16494       * registered {@link ng.$rootScope.Scope#$on} listeners.
16495       *
16496       * The event life cycle starts at the scope on which `$broadcast` was called. All
16497       * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get
16498       * notified. Afterwards, the event propagates to all direct and indirect scopes of the current
16499       * scope and calls all registered listeners along the way. The event cannot be canceled.
16500       *
16501       * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
16502       * onto the {@link ng.$exceptionHandler $exceptionHandler} service.
16503       *
16504       * @param {string} name Event name to broadcast.
16505       * @param {...*} args Optional one or more arguments which will be passed onto the event listeners.
16506       * @return {Object} Event object, see {@link ng.$rootScope.Scope#$on}
16507       */
16508      $broadcast: function(name, args) {
16509        var target = this,
16510            current = target,
16511            next = target,
16512            event = {
16513              name: name,
16514              targetScope: target,
16515              preventDefault: function() {
16516                event.defaultPrevented = true;
16517              },
16518              defaultPrevented: false
16519            };
16520
16521        if (!target.$$listenerCount[name]) return event;
16522
16523        var listenerArgs = concat([event], arguments, 1),
16524            listeners, i, length;
16525
16526        //down while you can, then up and next sibling or up and next sibling until back at root
16527        while ((current = next)) {
16528          event.currentScope = current;
16529          listeners = current.$$listeners[name] || [];
16530          for (i = 0, length = listeners.length; i < length; i++) {
16531            // if listeners were deregistered, defragment the array
16532            if (!listeners[i]) {
16533              listeners.splice(i, 1);
16534              i--;
16535              length--;
16536              continue;
16537            }
16538
16539            try {
16540              listeners[i].apply(null, listenerArgs);
16541            } catch (e) {
16542              $exceptionHandler(e);
16543            }
16544          }
16545
16546          // Insanity Warning: scope depth-first traversal
16547          // yes, this code is a bit crazy, but it works and we have tests to prove it!
16548          // this piece should be kept in sync with the traversal in $digest
16549          // (though it differs due to having the extra check for $$listenerCount)
16550          if (!(next = ((current.$$listenerCount[name] && current.$$childHead) ||
16551              (current !== target && current.$$nextSibling)))) {
16552            while (current !== target && !(next = current.$$nextSibling)) {
16553              current = current.$parent;
16554            }
16555          }
16556        }
16557
16558        event.currentScope = null;
16559        return event;
16560      }
16561    };
16562
16563    var $rootScope = new Scope();
16564
16565    //The internal queues. Expose them on the $rootScope for debugging/testing purposes.
16566    var asyncQueue = $rootScope.$$asyncQueue = [];
16567    var postDigestQueue = $rootScope.$$postDigestQueue = [];
16568    var applyAsyncQueue = $rootScope.$$applyAsyncQueue = [];
16569
16570    return $rootScope;
16571
16572
16573    function beginPhase(phase) {
16574      if ($rootScope.$$phase) {
16575        throw $rootScopeMinErr('inprog', '{0} already in progress', $rootScope.$$phase);
16576      }
16577
16578      $rootScope.$$phase = phase;
16579    }
16580
16581    function clearPhase() {
16582      $rootScope.$$phase = null;
16583    }
16584
16585    function incrementWatchersCount(current, count) {
16586      do {
16587        current.$$watchersCount += count;
16588      } while ((current = current.$parent));
16589    }
16590
16591    function decrementListenerCount(current, count, name) {
16592      do {
16593        current.$$listenerCount[name] -= count;
16594
16595        if (current.$$listenerCount[name] === 0) {
16596          delete current.$$listenerCount[name];
16597        }
16598      } while ((current = current.$parent));
16599    }
16600
16601    /**
16602     * function used as an initial value for watchers.
16603     * because it's unique we can easily tell it apart from other values
16604     */
16605    function initWatchVal() {}
16606
16607    function flushApplyAsync() {
vendor: 4,838 bytes, lines 16608-16735
16608      while (applyAsyncQueue.length) {
16609        try {
16610          applyAsyncQueue.shift()();
16611        } catch (e) {
16612          $exceptionHandler(e);
16613        }
16614      }
16615      applyAsyncId = null;
16616    }
16617
16618    function scheduleApplyAsync() {
16619      if (applyAsyncId === null) {
16620        applyAsyncId = $browser.defer(function() {
16621          $rootScope.$apply(flushApplyAsync);
16622        });
16623      }
16624    }
16625  }];
16626}
16627
16628/**
16629 * @description
16630 * Private service to sanitize uris for links and images. Used by $compile and $sanitize.
16631 */
16632function $$SanitizeUriProvider() {
16633  var aHrefSanitizationWhitelist = /^\s*(https?|ftp|mailto|tel|file):/,
16634    imgSrcSanitizationWhitelist = /^\s*((https?|ftp|file|blob):|data:image\/)/;
16635
16636  /**
16637   * @description
16638   * Retrieves or overrides the default regular expression that is used for whitelisting of safe
16639   * urls during a[href] sanitization.
16640   *
16641   * The sanitization is a security measure aimed at prevent XSS attacks via html links.
16642   *
16643   * Any url about to be assigned to a[href] via data-binding is first normalized and turned into
16644   * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist`
16645   * regular expression. If a match is found, the original url is written into the dom. Otherwise,
16646   * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
16647   *
16648   * @param {RegExp=} regexp New regexp to whitelist urls with.
16649   * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
16650   *    chaining otherwise.
16651   */
16652  this.aHrefSanitizationWhitelist = function(regexp) {
16653    if (isDefined(regexp)) {
16654      aHrefSanitizationWhitelist = regexp;
16655      return this;
16656    }
16657    return aHrefSanitizationWhitelist;
16658  };
16659
16660
16661  /**
16662   * @description
16663   * Retrieves or overrides the default regular expression that is used for whitelisting of safe
16664   * urls during img[src] sanitization.
16665   *
16666   * The sanitization is a security measure aimed at prevent XSS attacks via html links.
16667   *
16668   * Any url about to be assigned to img[src] via data-binding is first normalized and turned into
16669   * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist`
16670   * regular expression. If a match is found, the original url is written into the dom. Otherwise,
16671   * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
16672   *
16673   * @param {RegExp=} regexp New regexp to whitelist urls with.
16674   * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
16675   *    chaining otherwise.
16676   */
16677  this.imgSrcSanitizationWhitelist = function(regexp) {
16678    if (isDefined(regexp)) {
16679      imgSrcSanitizationWhitelist = regexp;
16680      return this;
16681    }
16682    return imgSrcSanitizationWhitelist;
16683  };
16684
16685  this.$get = function() {
16686    return function sanitizeUri(uri, isImage) {
16687      var regex = isImage ? imgSrcSanitizationWhitelist : aHrefSanitizationWhitelist;
16688      var normalizedVal;
16689      normalizedVal = urlResolve(uri).href;
16690      if (normalizedVal !== '' && !normalizedVal.match(regex)) {
16691        return 'unsafe:' + normalizedVal;
16692      }
16693      return uri;
16694    };
16695  };
16696}
16697
16698/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
16699 *     Any commits to this file should be reviewed with security in mind.  *
16700 *   Changes to this file can potentially create security vulnerabilities. *
16701 *          An approval from 2 Core members with history of modifying      *
16702 *                         this file is required.                          *
16703 *                                                                         *
16704 *  Does the change somehow allow for arbitrary javascript to be executed? *
16705 *    Or allows for someone to change the prototype of built-in objects?   *
16706 *     Or gives undesired access to variables likes document or window?    *
16707 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
16708
16709var $sceMinErr = minErr('$sce');
16710
16711var SCE_CONTEXTS = {
16712  HTML: 'html',
16713  CSS: 'css',
16714  URL: 'url',
16715  // RESOURCE_URL is a subtype of URL used in contexts where a privileged resource is sourced from a
16716  // url.  (e.g. ng-include, script src, templateUrl)
16717  RESOURCE_URL: 'resourceUrl',
16718  JS: 'js'
16719};
16720
16721// Helper functions follow.
16722
16723function adjustMatcher(matcher) {
16724  if (matcher === 'self') {
16725    return matcher;
16726  } else if (isString(matcher)) {
16727    // Strings match exactly except for 2 wildcards - '*' and '**'.
16728    // '*' matches any character except those from the set ':/.?&'.
16729    // '**' matches any character (like .* in a RegExp).
16730    // More than 2 *'s raises an error as it's ill defined.
16731    if (matcher.indexOf('***') > -1) {
16732      throw $sceMinErr('iwcard',
16733          'Illegal sequence *** in string matcher.  String: {0}', matcher);
16734    }
16735    matcher = escapeForRegexp(matcher).
16736                  replace('\\*\\*', '.*').
16737                  replace('\\*', '[^:/.?&;]*');
16738    return new RegExp('^' + matcher + '$');
16739  } else if (isRegExp(matcher)) {
16740    // The only other type of matcher allowed is a Regexp.
16741    // Match entire URL / disallow partial matches.
16742    // Flags are reset (i.e. no global, ignoreCase or multiline)
16743    return new RegExp('^' + matcher.source + '$');
16744  } else {
16745    throw $sceMinErr('imatcher',
16746        'Matchers may only be "self", string patterns or RegExp objects');
16747  }
16748}
16749
16750
16751function adjustMatchers(matchers) {
16752  var adjustedMatchers = [];
16753  if (isDefined(matchers)) {
16754    forEach(matchers, function(matcher) {
16755      adjustedMatchers.push(adjustMatcher(matcher));
16756    });
16757  }
16758  return adjustedMatchers;
16759}
16760
16761
16762/**
16763 * @ngdoc service
16764 * @name $sceDelegate
16765 * @kind function
16766 *
16767 * @description
16768 *
16769 * `$sceDelegate` is a service that is used by the `$sce` service to provide {@link ng.$sce Strict
16770 * Contextual Escaping (SCE)} services to AngularJS.
16771 *
16772 * Typically, you would configure or override the {@link ng.$sceDelegate $sceDelegate} instead of
16773 * the `$sce` service to customize the way Strict Contextual Escaping works in AngularJS.  This is
16774 * because, while the `$sce` provides numerous shorthand methods, etc., you really only need to
16775 * override 3 core functions (`trustAs`, `getTrusted` and `valueOf`) to replace the way things
16776 * work because `$sce` delegates to `$sceDelegate` for these operations.
16777 *
16778 * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} to configure this service.
16779 *
16780 * The default instance of `$sceDelegate` should work out of the box with little pain.  While you
16781 * can override it completely to change the behavior of `$sce`, the common case would
16782 * involve configuring the {@link ng.$sceDelegateProvider $sceDelegateProvider} instead by setting
16783 * your own whitelists and blacklists for trusting URLs used for loading AngularJS resources such as
16784 * templates.  Refer {@link ng.$sceDelegateProvider#resourceUrlWhitelist
16785 * $sceDelegateProvider.resourceUrlWhitelist} and {@link
16786 * ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist}
16787 */
16788
16789/**
16790 * @ngdoc provider
16791 * @name $sceDelegateProvider
16792 * @description
16793 *
16794 * The `$sceDelegateProvider` provider allows developers to configure the {@link ng.$sceDelegate
16795 * $sceDelegate} service.  This allows one to get/set the whitelists and blacklists used to ensure
16796 * that the URLs used for sourcing Angular templates are safe.  Refer {@link
16797 * ng.$sceDelegateProvider#resourceUrlWhitelist $sceDelegateProvider.resourceUrlWhitelist} and
16798 * {@link ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist}
16799 *
16800 * For the general details about this service in Angular, read the main page for {@link ng.$sce
16801 * Strict Contextual Escaping (SCE)}.
16802 *
16803 * **Example**:  Consider the following case. <a name="example"></a>
16804 *
16805 * - your app is hosted at url `http://myapp.example.com/`
16806 * - but some of your templates are hosted on other domains you control such as
16807 *   `http://srv01.assets.example.com/`,  `http://srv02.assets.example.com/`, etc.
16808 * - and you have an open redirect at `http://myapp.example.com/clickThru?...`.
16809 *
16810 * Here is what a secure configuration for this scenario might look like:
16811 *
16812 * ```
16813 *  angular.module('myApp', []).config(function($sceDelegateProvider) {
16814 *    $sceDelegateProvider.resourceUrlWhitelist([
16815 *      // Allow same origin resource loads.
16816 *      'self',
16817 *      // Allow loading from our assets domain.  Notice the difference between * and **.
16818 *      'http://srv*.assets.example.com/**'
16819 *    ]);
16820 *
16821 *    // The blacklist overrides the whitelist so the open redirect here is blocked.
16822 *    $sceDelegateProvider.resourceUrlBlacklist([
16823 *      'http://myapp.example.com/clickThru**'
16824 *    ]);
16825 *  });
16826 * ```
16827 */
16828
16829function $SceDelegateProvider() {
16830  this.SCE_CONTEXTS = SCE_CONTEXTS;
16831
16832  // Resource URLs can also be trusted by policy.
16833  var resourceUrlWhitelist = ['self'],
16834      resourceUrlBlacklist = [];
16835
16836  /**
16837   * @ngdoc method
16838   * @name $sceDelegateProvider#resourceUrlWhitelist
16839   * @kind function
16840   *
16841   * @param {Array=} whitelist When provided, replaces the resourceUrlWhitelist with the value
16842   *     provided.  This must be an array or null.  A snapshot of this array is used so further
16843   *     changes to the array are ignored.
16844   *
16845   *     Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
16846   *     allowed in this array.
vendor: 4,148 bytes, lines 16847-16971
16847   *
16848   *     Note: **an empty whitelist array will block all URLs**!
16849   *
16850   * @return {Array} the currently set whitelist array.
16851   *
16852   * The **default value** when no whitelist has been explicitly set is `['self']` allowing only
16853   * same origin resource requests.
16854   *
16855   * @description
16856   * Sets/Gets the whitelist of trusted resource URLs.
16857   */
16858  this.resourceUrlWhitelist = function(value) {
16859    if (arguments.length) {
16860      resourceUrlWhitelist = adjustMatchers(value);
16861    }
16862    return resourceUrlWhitelist;
16863  };
16864
16865  /**
16866   * @ngdoc method
16867   * @name $sceDelegateProvider#resourceUrlBlacklist
16868   * @kind function
16869   *
16870   * @param {Array=} blacklist When provided, replaces the resourceUrlBlacklist with the value
16871   *     provided.  This must be an array or null.  A snapshot of this array is used so further
16872   *     changes to the array are ignored.
16873   *
16874   *     Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
16875   *     allowed in this array.
16876   *
16877   *     The typical usage for the blacklist is to **block
16878   *     [open redirects](http://cwe.mitre.org/data/definitions/601.html)** served by your domain as
16879   *     these would otherwise be trusted but actually return content from the redirected domain.
16880   *
16881   *     Finally, **the blacklist overrides the whitelist** and has the final say.
16882   *
16883   * @return {Array} the currently set blacklist array.
16884   *
16885   * The **default value** when no whitelist has been explicitly set is the empty array (i.e. there
16886   * is no blacklist.)
16887   *
16888   * @description
16889   * Sets/Gets the blacklist of trusted resource URLs.
16890   */
16891
16892  this.resourceUrlBlacklist = function(value) {
16893    if (arguments.length) {
16894      resourceUrlBlacklist = adjustMatchers(value);
16895    }
16896    return resourceUrlBlacklist;
16897  };
16898
16899  this.$get = ['$injector', function($injector) {
16900
16901    var htmlSanitizer = function htmlSanitizer(html) {
16902      throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.');
16903    };
16904
16905    if ($injector.has('$sanitize')) {
16906      htmlSanitizer = $injector.get('$sanitize');
16907    }
16908
16909
16910    function matchUrl(matcher, parsedUrl) {
16911      if (matcher === 'self') {
16912        return urlIsSameOrigin(parsedUrl);
16913      } else {
16914        // definitely a regex.  See adjustMatchers()
16915        return !!matcher.exec(parsedUrl.href);
16916      }
16917    }
16918
16919    function isResourceUrlAllowedByPolicy(url) {
16920      var parsedUrl = urlResolve(url.toString());
16921      var i, n, allowed = false;
16922      // Ensure that at least one item from the whitelist allows this url.
16923      for (i = 0, n = resourceUrlWhitelist.length; i < n; i++) {
16924        if (matchUrl(resourceUrlWhitelist[i], parsedUrl)) {
16925          allowed = true;
16926          break;
16927        }
16928      }
16929      if (allowed) {
16930        // Ensure that no item from the blacklist blocked this url.
16931        for (i = 0, n = resourceUrlBlacklist.length; i < n; i++) {
16932          if (matchUrl(resourceUrlBlacklist[i], parsedUrl)) {
16933            allowed = false;
16934            break;
16935          }
16936        }
16937      }
16938      return allowed;
16939    }
16940
16941    function generateHolderType(Base) {
16942      var holderType = function TrustedValueHolderType(trustedValue) {
16943        this.$$unwrapTrustedValue = function() {
16944          return trustedValue;
16945        };
16946      };
16947      if (Base) {
16948        holderType.prototype = new Base();
16949      }
16950      holderType.prototype.valueOf = function sceValueOf() {
16951        return this.$$unwrapTrustedValue();
16952      };
16953      holderType.prototype.toString = function sceToString() {
16954        return this.$$unwrapTrustedValue().toString();
16955      };
16956      return holderType;
16957    }
16958
16959    var trustedValueHolderBase = generateHolderType(),
16960        byType = {};
16961
16962    byType[SCE_CONTEXTS.HTML] = generateHolderType(trustedValueHolderBase);
16963    byType[SCE_CONTEXTS.CSS] = generateHolderType(trustedValueHolderBase);
16964    byType[SCE_CONTEXTS.URL] = generateHolderType(trustedValueHolderBase);
16965    byType[SCE_CONTEXTS.JS] = generateHolderType(trustedValueHolderBase);
16966    byType[SCE_CONTEXTS.RESOURCE_URL] = generateHolderType(byType[SCE_CONTEXTS.URL]);
16967
16968    /**
16969     * @ngdoc method
16970     * @name $sceDelegate#trustAs
16971     *
16972     * @description
16973     * Returns an object that is trusted by angular for use in specified strict
16974     * contextual escaping contexts (such as ng-bind-html, ng-include, any src
16975     * attribute interpolation, any dom event binding attribute interpolation
16976     * such as for onclick,  etc.) that uses the provided value.
16977     * See {@link ng.$sce $sce} for enabling strict contextual escaping.
16978     *
16979     * @param {string} type The kind of context in which this value is safe for use.  e.g. url,
16980     *   resourceUrl, html, js and css.
16981     * @param {*} value The value that that should be considered trusted/safe.
16982     * @returns {*} A value that can be used to stand in for the provided `value` in places
16983     * where Angular expects a $sce.trustAs() return value.
16984     */
16985    function trustAs(type, trustedValue) {
16986      var Constructor = (byType.hasOwnProperty(type) ? byType[type] : null);
16987      if (!Constructor) {
16988        throw $sceMinErr('icontext',
16989            'Attempted to trust a value in invalid context. Context: {0}; Value: {1}',
16990            type, trustedValue);
16991      }
16992      if (trustedValue === null || isUndefined(trustedValue) || trustedValue === '') {
16993        return trustedValue;
16994      }
16995      // All the current contexts in SCE_CONTEXTS happen to be strings.  In order to avoid trusting
16996      // mutable objects, we ensure here that the value passed in is actually a string.
16997      if (typeof trustedValue !== 'string') {
16998        throw $sceMinErr('itype',
16999            'Attempted to trust a non-string value in a content requiring a string: Context: {0}',
17000            type);
17001      }
17002      return new Constructor(trustedValue);
17003    }
17004
17005    /**
17006     * @ngdoc method
17007     * @name $sceDelegate#valueOf
17008     *
17009     * @description
17010     * If the passed parameter had been returned by a prior call to {@link ng.$sceDelegate#trustAs
17011     * `$sceDelegate.trustAs`}, returns the value that had been passed to {@link
17012     * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}.
17013     *
17014     * If the passed parameter is not a value that had been returned by {@link
17015     * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}, returns it as-is.
17016     *
17017     * @param {*} value The result of a prior {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}
17018     *      call or anything else.
17019     * @returns {*} The `value` that was originally provided to {@link ng.$sceDelegate#trustAs
17020     *     `$sceDelegate.trustAs`} if `value` is the result of such a call.  Otherwise, returns
17021     *     `value` unchanged.
17022     */
17023    function valueOf(maybeTrusted) {
17024      if (maybeTrusted instanceof trustedValueHolderBase) {
17025        return maybeTrusted.$$unwrapTrustedValue();
17026      } else {
17027        return maybeTrusted;
17028      }
17029    }
17030
17031    /**
17032     * @ngdoc method
17033     * @name $sceDelegate#getTrusted
17034     *
17035     * @description
17036     * Takes the result of a {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`} call and
17037     * returns the originally supplied value if the queried context type is a supertype of the
17038     * created type.  If this condition isn't satisfied, throws an exception.
17039     *
17040     * @param {string} type The kind of context in which this value is to be used.
17041     * @param {*} maybeTrusted The result of a prior {@link ng.$sceDelegate#trustAs
17042     *     `$sceDelegate.trustAs`} call.
17043     * @returns {*} The value the was originally provided to {@link ng.$sceDelegate#trustAs
17044     *     `$sceDelegate.trustAs`} if valid in this context.  Otherwise, throws an exception.
17045     */
17046    function getTrusted(type, maybeTrusted) {
17047      if (maybeTrusted === null || isUndefined(maybeTrusted) || maybeTrusted === '') {
17048        return maybeTrusted;
17049      }
17050      var constructor = (byType.hasOwnProperty(type) ? byType[type] : null);
17051      if (constructor && maybeTrusted instanceof constructor) {
17052        return maybeTrusted.$$unwrapTrustedValue();
17053      }
17054      // If we get here, then we may only take one of two actions.
17055      // 1. sanitize the value for the requested type, or
17056      // 2. throw an exception.
17057      if (type === SCE_CONTEXTS.RESOURCE_URL) {
17058        if (isResourceUrlAllowedByPolicy(maybeTrusted)) {
17059          return maybeTrusted;
17060        } else {
17061          throw $sceMinErr('insecurl',
17062              'Blocked loading resource from url not allowed by $sceDelegate policy.  URL: {0}',
17063              maybeTrusted.toString());
17064        }
17065      } else if (type === SCE_CONTEXTS.HTML) {
17066        return htmlSanitizer(maybeTrusted);
17067      }
17068      throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.');
17069    }
17070
17071    return { trustAs: trustAs,
17072             getTrusted: getTrusted,
17073             valueOf: valueOf };
17074  }];
17075}
17076
17077
17078/**
17079 * @ngdoc provider
17080 * @name $sceProvider
17081 * @description
17082 *
17083 * The $sceProvider provider allows developers to configure the {@link ng.$sce $sce} service.
17084 * -   enable/disable Strict Contextual Escaping (SCE) in a module
17085 * -   override the default implementation with a custom delegate
17086 *
17087 * Read more about {@link ng.$sce Strict Contextual Escaping (SCE)}.
17088 */
17089
17090/* jshint maxlen: false*/
17091
17092/**
17093 * @ngdoc service
17094 * @name $sce
17095 * @kind function
17096 *
17097 * @description
17098 *
17099 * `$sce` is a service that provides Strict Contextual Escaping services to AngularJS.
17100 *
17101 * # Strict Contextual Escaping
17102 *
17103 * Strict Contextual Escaping (SCE) is a mode in which AngularJS requires bindings in certain
17104 * contexts to result in a value that is marked as safe to use for that context.  One example of
17105 * such a context is binding arbitrary html controlled by the user via `ng-bind-html`.  We refer
17106 * to these contexts as privileged or SCE contexts.
17107 *
17108 * As of version 1.2, Angular ships with SCE enabled by default.
17109 *
17110 * Note:  When enabled (the default), IE<11 in quirks mode is not supported.  In this mode, IE<11 allow
17111 * one to execute arbitrary javascript by the use of the expression() syntax.  Refer
17112 * <http://blogs.msdn.com/b/ie/archive/2008/10/16/ending-expressions.aspx> to learn more about them.
17113 * You can ensure your document is in standards mode and not quirks mode by adding `<!doctype html>`
17114 * to the top of your HTML document.
17115 *
17116 * SCE assists in writing code in way that (a) is secure by default and (b) makes auditing for
17117 * security vulnerabilities such as XSS, clickjacking, etc. a lot easier.
17118 *
17119 * Here's an example of a binding in a privileged context:
17120 *
17121 * ```
17122 * <input ng-model="userHtml" aria-label="User input">
17123 * <div ng-bind-html="userHtml"></div>
17124 * ```
17125 *
17126 * Notice that `ng-bind-html` is bound to `userHtml` controlled by the user.  With SCE
17127 * disabled, this application allows the user to render arbitrary HTML into the DIV.
17128 * In a more realistic example, one may be rendering user comments, blog articles, etc. via
17129 * bindings.  (HTML is just one example of a context where rendering user controlled input creates
17130 * security vulnerabilities.)
17131 *
17132 * For the case of HTML, you might use a library, either on the client side, or on the server side,
17133 * to sanitize unsafe HTML before binding to the value and rendering it in the document.
17134 *
17135 * How would you ensure that every place that used these types of bindings was bound to a value that
17136 * was sanitized by your library (or returned as safe for rendering by your server?)  How can you
17137 * ensure that you didn't accidentally delete the line that sanitized the value, or renamed some
17138 * properties/fields and forgot to update the binding to the sanitized value?
17139 *
17140 * To be secure by default, you want to ensure that any such bindings are disallowed unless you can
17141 * determine that something explicitly says it's safe to use a value for binding in that
17142 * context.  You can then audit your code (a simple grep would do) to ensure that this is only done
17143 * for those values that you can easily tell are safe - because they were received from your server,
17144 * sanitized by your library, etc.  You can organize your codebase to help with this - perhaps
17145 * allowing only the files in a specific directory to do this.  Ensuring that the internal API
17146 * exposed by that code doesn't markup arbitrary values as safe then becomes a more manageable task.
17147 *
17148 * In the case of AngularJS' SCE service, one uses {@link ng.$sce#trustAs $sce.trustAs}
17149 * (and shorthand methods such as {@link ng.$sce#trustAsHtml $sce.trustAsHtml}, etc.) to
17150 * obtain values that will be accepted by SCE / privileged contexts.
17151 *
17152 *
17153 * ## How does it work?
17154 *
17155 * In privileged contexts, directives and code will bind to the result of {@link ng.$sce#getTrusted
17156 * $sce.getTrusted(context, value)} rather than to the value directly.  Directives use {@link
17157 * ng.$sce#parseAs $sce.parseAs} rather than `$parse` to watch attribute bindings, which performs the
17158 * {@link ng.$sce#getTrusted $sce.getTrusted} behind the scenes on non-constant literals.
17159 *
17160 * As an example, {@link ng.directive:ngBindHtml ngBindHtml} uses {@link
17161 * ng.$sce#parseAsHtml $sce.parseAsHtml(binding expression)}.  Here's the actual code (slightly
17162 * simplified):
17163 *
17164 * ```
17165 * var ngBindHtmlDirective = ['$sce', function($sce) {
17166 *   return function(scope, element, attr) {
17167 *     scope.$watch($sce.parseAsHtml(attr.ngBindHtml), function(value) {
17168 *       element.html(value || '');
17169 *     });
17170 *   };
17171 * }];
17172 * ```
17173 *
17174 * ## Impact on loading templates
17175 *
17176 * This applies both to the {@link ng.directive:ngInclude `ng-include`} directive as well as
17177 * `templateUrl`'s specified by {@link guide/directive directives}.
17178 *
17179 * By default, Angular only loads templates from the same domain and protocol as the application
17180 * document.  This is done by calling {@link ng.$sce#getTrustedResourceUrl
17181 * $sce.getTrustedResourceUrl} on the template URL.  To load templates from other domains and/or
17182 * protocols, you may either either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist
17183 * them} or {@link ng.$sce#trustAsResourceUrl wrap it} into a trusted value.
17184 *
17185 * *Please note*:
17186 * The browser's
17187 * [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest)
17188 * and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/)
17189 * policy apply in addition to this and may further restrict whether the template is successfully
17190 * loaded.  This means that without the right CORS policy, loading templates from a different domain
17191 * won't work on all browsers.  Also, loading templates from `file://` URL does not work on some
17192 * browsers.
17193 *
17194 * ## This feels like too much overhead
17195 *
17196 * It's important to remember that SCE only applies to interpolation expressions.
17197 *
17198 * If your expressions are constant literals, they're automatically trusted and you don't need to
17199 * call `$sce.trustAs` on them (remember to include the `ngSanitize` module) (e.g.
17200 * `<div ng-bind-html="'<b>implicitly trusted</b>'"></div>`) just works.
17201 *
17202 * Additionally, `a[href]` and `img[src]` automatically sanitize their URLs and do not pass them
17203 * through {@link ng.$sce#getTrusted $sce.getTrusted}.  SCE doesn't play a role here.
17204 *
17205 * The included {@link ng.$sceDelegate $sceDelegate} comes with sane defaults to allow you to load
17206 * templates in `ng-include` from your application's domain without having to even know about SCE.
17207 * It blocks loading templates from other domains or loading templates over http from an https
17208 * served document.  You can change these by setting your own custom {@link
17209 * ng.$sceDelegateProvider#resourceUrlWhitelist whitelists} and {@link
17210 * ng.$sceDelegateProvider#resourceUrlBlacklist blacklists} for matching such URLs.
17211 *
17212 * This significantly reduces the overhead.  It is far easier to pay the small overhead and have an
17213 * application that's secure and can be audited to verify that with much more ease than bolting
17214 * security onto an application later.
17215 *
17216 * <a name="contexts"></a>
17217 * ## What trusted context types are supported?
17218 *
17219 * | Context             | Notes          |
17220 * |---------------------|----------------|
17221 * | `$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. |
17222 * | `$sce.CSS`          | For CSS that's safe to source into the application.  Currently unused.  Feel free to use it in your own directives. |
17223 * | `$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. |
17224 * | `$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. |
17225 * | `$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. |
17226 *
17227 * ## Format of items in {@link ng.$sceDelegateProvider#resourceUrlWhitelist resourceUrlWhitelist}/{@link ng.$sceDelegateProvider#resourceUrlBlacklist Blacklist} <a name="resourceUrlPatternItem"></a>
17228 *
17229 *  Each element in these arrays must be one of the following:
17230 *
17231 *  - **'self'**
17232 *    - The special **string**, `'self'`, can be used to match against all URLs of the **same
17233 *      domain** as the application document using the **same protocol**.
17234 *  - **String** (except the special value `'self'`)
17235 *    - The string is matched against the full *normalized / absolute URL* of the resource
17236 *      being tested (substring matches are not good enough.)
17237 *    - There are exactly **two wildcard sequences** - `*` and `**`.  All other characters
17238 *      match themselves.
17239 *    - `*`: matches zero or more occurrences of any character other than one of the following 6
17240 *      characters: '`:`', '`/`', '`.`', '`?`', '`&`' and '`;`'.  It's a useful wildcard for use
17241 *      in a whitelist.
17242 *    - `**`: matches zero or more occurrences of *any* character.  As such, it's not
17243 *      appropriate for use in a scheme, domain, etc. as it would match too much.  (e.g.
17244 *      http://**.example.com/ would match http://evil.com/?ignore=.example.com/ and that might
17245 *      not have been the intention.)  Its usage at the very end of the path is ok.  (e.g.
17246 *      http://foo.example.com/templates/**).
17247 *  - **RegExp** (*see caveat below*)
17248 *    - *Caveat*:  While regular expressions are powerful and offer great flexibility,  their syntax
17249 *      (and all the inevitable escaping) makes them *harder to maintain*.  It's easy to
17250 *      accidentally introduce a bug when one updates a complex expression (imho, all regexes should
17251 *      have good test coverage).  For instance, the use of `.` in the regex is correct only in a
17252 *      small number of cases.  A `.` character in the regex used when matching the scheme or a
17253 *      subdomain could be matched against a `:` or literal `.` that was likely not intended.   It
17254 *      is highly recommended to use the string patterns and only fall back to regular expressions
17255 *      as a last resort.
17256 *    - The regular expression must be an instance of RegExp (i.e. not a string.)  It is
17257 *      matched against the **entire** *normalized / absolute URL* of the resource being tested
17258 *      (even when the RegExp did not have the `^` and `$` codes.)  In addition, any flags
17259 *      present on the RegExp (such as multiline, global, ignoreCase) are ignored.
17260 *    - If you are generating your JavaScript from some other templating engine (not
17261 *      recommended, e.g. in issue [#4006](https://github.com/angular/angular.js/issues/4006)),
17262 *      remember to escape your regular expression (and be aware that you might need more than
17263 *      one level of escaping depending on your templating engine and the way you interpolated
17264 *      the value.)  Do make use of your platform's escaping mechanism as it might be good
17265 *      enough before coding your own.  E.g. Ruby has
17266 *      [Regexp.escape(str)](http://www.ruby-doc.org/core-2.0.0/Regexp.html#method-c-escape)
17267 *      and Python has [re.escape](http://docs.python.org/library/re.html#re.escape).
17268 *      Javascript lacks a similar built in function for escaping.  Take a look at Google
17269 *      Closure library's [goog.string.regExpEscape(s)](
17270 *      http://docs.closure-library.googlecode.com/git/closure_goog_string_string.js.source.html#line962).
17271 *
17272 * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} for an example.
17273 *
17274 * ## Show me an example using SCE.
17275 *
17276 * <example module="mySceApp" deps="angular-sanitize.js">
17277 * <file name="index.html">
17278 *   <div ng-controller="AppController as myCtrl">
17279 *     <i ng-bind-html="myCtrl.explicitlyTrustedHtml" id="explicitlyTrustedHtml"></i><br><br>
17280 *     <b>User comments</b><br>
17281 *     By default, HTML that isn't explicitly trusted (e.g. Alice's comment) is sanitized when
17282 *     $sanitize is available.  If $sanitize isn't available, this results in an error instead of an
17283 *     exploit.
17284 *     <div class="well">
17285 *       <div ng-repeat="userComment in myCtrl.userComments">
17286 *         <b>{{userComment.name}}</b>:
17287 *         <span ng-bind-html="userComment.htmlComment" class="htmlComment"></span>
17288 *         <br>
17289 *       </div>
17290 *     </div>
17291 *   </div>
17292 * </file>
17293 *
17294 * <file name="script.js">
17295 *   angular.module('mySceApp', ['ngSanitize'])
17296 *     .controller('AppController', ['$http', '$templateCache', '$sce',
17297 *       function($http, $templateCache, $sce) {
17298 *         var self = this;
17299 *         $http.get("test_data.json", {cache: $templateCache}).success(function(userComments) {
17300 *           self.userComments = userComments;
17301 *         });
17302 *         self.explicitlyTrustedHtml = $sce.trustAsHtml(
17303 *             '<span onmouseover="this.textContent=&quot;Explicitly trusted HTML bypasses ' +
17304 *             'sanitization.&quot;">Hover over this text.</span>');
17305 *       }]);
17306 * </file>
17307 *
17308 * <file name="test_data.json">
17309 * [
17310 *   { "name": "Alice",
17311 *     "htmlComment":
17312 *         "<span onmouseover='this.textContent=\"PWN3D!\"'>Is <i>anyone</i> reading this?</span>"
17313 *   },
17314 *   { "name": "Bob",
17315 *     "htmlComment": "<i>Yes!</i>  Am I the only other one?"
17316 *   }
17317 * ]
17318 * </file>
17319 *
17320 * <file name="protractor.js" type="protractor">
17321 *   describe('SCE doc demo', function() {
17322 *     it('should sanitize untrusted values', function() {
17323 *       expect(element.all(by.css('.htmlComment')).first().getInnerHtml())
17324 *           .toBe('<span>Is <i>anyone</i> reading this?</span>');
17325 *     });
17326 *
17327 *     it('should NOT sanitize explicitly trusted values', function() {
17328 *       expect(element(by.id('explicitlyTrustedHtml')).getInnerHtml()).toBe(
17329 *           '<span onmouseover="this.textContent=&quot;Explicitly trusted HTML bypasses ' +
17330 *           'sanitization.&quot;">Hover over this text.</span>');
17331 *     });
17332 *   });
17333 * </file>
17334 * </example>
17335 *
17336 *
17337 *
17338 * ## Can I disable SCE completely?
17339 *
17340 * Yes, you can.  However, this is strongly discouraged.  SCE gives you a lot of security benefits
17341 * for little coding overhead.  It will be much harder to take an SCE disabled application and
17342 * either secure it on your own or enable SCE at a later stage.  It might make sense to disable SCE
17343 * for cases where you have a lot of existing code that was written before SCE was introduced and
17344 * you're migrating them a module at a time.
17345 *
17346 * That said, here's how you can completely disable SCE:
17347 *
17348 * ```
17349 * angular.module('myAppWithSceDisabledmyApp', []).config(function($sceProvider) {
17350 *   // Completely disable SCE.  For demonstration purposes only!
17351 *   // Do not use in new projects.
17352 *   $sceProvider.enabled(false);
17353 * });
17354 * ```
17355 *
17356 */
17357/* jshint maxlen: 100 */
17358
17359function $SceProvider() {
17360  var enabled = true;
17361
17362  /**
17363   * @ngdoc method
17364   * @name $sceProvider#enabled
17365   * @kind function
17366   *
17367   * @param {boolean=} value If provided, then enables/disables SCE.
17368   * @return {boolean} true if SCE is enabled, false otherwise.
17369   *
17370   * @description
17371   * Enables/disables SCE and returns the current value.
17372   */
17373  this.enabled = function(value) {
17374    if (arguments.length) {
17375      enabled = !!value;
17376    }
17377    return enabled;
17378  };
17379
17380
17381  /* Design notes on the default implementation for SCE.
17382   *
17383   * The API contract for the SCE delegate
17384   * -------------------------------------
17385   * The SCE delegate object must provide the following 3 methods:
17386   *
17387   * - trustAs(contextEnum, value)
17388   *     This method is used to tell the SCE service that the provided value is OK to use in the
17389   *     contexts specified by contextEnum.  It must return an object that will be accepted by
17390   *     getTrusted() for a compatible contextEnum and return this value.
17391   *
17392   * - valueOf(value)
17393   *     For values that were not produced by trustAs(), return them as is.  For values that were
17394   *     produced by trustAs(), return the corresponding input value to trustAs.  Basically, if
17395   *     trustAs is wrapping the given values into some type, this operation unwraps it when given
17396   *     such a value.
17397   *
17398   * - getTrusted(contextEnum, value)
17399   *     This function should return the a value that is safe to use in the context specified by
17400   *     contextEnum or throw and exception otherwise.
17401   *
17402   * NOTE: This contract deliberately does NOT state that values returned by trustAs() must be
17403   * opaque or wrapped in some holder object.  That happens to be an implementation detail.  For
17404   * instance, an implementation could maintain a registry of all trusted objects by context.  In
17405   * such a case, trustAs() would return the same object that was passed in.  getTrusted() would
17406   * return the same object passed in if it was found in the registry under a compatible context or
17407   * throw an exception otherwise.  An implementation might only wrap values some of the time based
17408   * on some criteria.  getTrusted() might return a value and not throw an exception for special
17409   * constants or objects even if not wrapped.  All such implementations fulfill this contract.
17410   *
17411   *
17412   * A note on the inheritance model for SCE contexts
17413   * ------------------------------------------------
17414   * I've used inheritance and made RESOURCE_URL wrapped types a subtype of URL wrapped types.  This
17415   * is purely an implementation details.
17416   *
17417   * The contract is simply this:
17418   *
17419   *     getTrusted($sce.RESOURCE_URL, value) succeeding implies that getTrusted($sce.URL, value)
17420   *     will also succeed.
17421   *
17422   * Inheritance happens to capture this in a natural way.  In some future, we
17423   * may not use inheritance anymore.  That is OK because no code outside of
17424   * sce.js and sceSpecs.js would need to be aware of this detail.
17425   */
17426
17427  this.$get = ['$parse', '$sceDelegate', function(
17428                $parse,   $sceDelegate) {
17429    // Prereq: Ensure that we're not running in IE<11 quirks mode.  In that mode, IE < 11 allow
17430    // the "expression(javascript expression)" syntax which is insecure.
17431    if (enabled && msie < 8) {
17432      throw $sceMinErr('iequirks',
17433        'Strict Contextual Escaping does not support Internet Explorer version < 11 in quirks ' +
17434        'mode.  You can fix this by adding the text <!doctype html> to the top of your HTML ' +
17435        'document.  See http://docs.angularjs.org/api/ng.$sce for more information.');
17436    }
17437
17438    var sce = shallowCopy(SCE_CONTEXTS);
17439
17440    /**
17441     * @ngdoc method
17442     * @name $sce#isEnabled
17443     * @kind function
17444     *
17445     * @return {Boolean} true if SCE is enabled, false otherwise.  If you want to set the value, you
17446     * have to do it at module config time on {@link ng.$sceProvider $sceProvider}.
17447     *
17448     * @description
17449     * Returns a boolean indicating if SCE is enabled.
17450     */
17451    sce.isEnabled = function() {
17452      return enabled;
17453    };
17454    sce.trustAs = $sceDelegate.trustAs;
17455    sce.getTrusted = $sceDelegate.getTrusted;
17456    sce.valueOf = $sceDelegate.valueOf;
17457
17458    if (!enabled) {
17459      sce.trustAs = sce.getTrusted = function(type, value) { return value; };
17460      sce.valueOf = identity;
17461    }
17462
17463    /**
17464     * @ngdoc method
17465     * @name $sce#parseAs
17466     *
17467     * @description
17468     * Converts Angular {@link guide/expression expression} into a function.  This is like {@link
17469     * ng.$parse $parse} and is identical when the expression is a literal constant.  Otherwise, it
17470     * wraps the expression in a call to {@link ng.$sce#getTrusted $sce.getTrusted(*type*,
17471     * *result*)}
17472     *
17473     * @param {string} type The kind of SCE context in which this result will be used.
17474     * @param {string} expression String expression to compile.
17475     * @returns {function(context, locals)} a function which represents the compiled expression:
17476     *
17477     *    * `context` – `{object}` – an object against which any expressions embedded in the strings
17478     *      are evaluated against (typically a scope object).
17479     *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
17480     *      `context`.
17481     */
17482    sce.parseAs = function sceParseAs(type, expr) {
17483      var parsed = $parse(expr);
17484      if (parsed.literal && parsed.constant) {
17485        return parsed;
17486      } else {
17487        return $parse(expr, function(value) {
17488          return sce.getTrusted(type, value);
17489        });
17490      }
17491    };
17492
17493    /**
17494     * @ngdoc method
17495     * @name $sce#trustAs
17496     *
17497     * @description
17498     * Delegates to {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}.  As such,
17499     * returns an object that is trusted by angular for use in specified strict contextual
17500     * escaping contexts (such as ng-bind-html, ng-include, any src attribute
17501     * interpolation, any dom event binding attribute interpolation such as for onclick,  etc.)
17502     * that uses the provided value.  See * {@link ng.$sce $sce} for enabling strict contextual
17503     * escaping.
17504     *
17505     * @param {string} type The kind of context in which this value is safe for use.  e.g. url,
17506     *   resourceUrl, html, js and css.
17507     * @param {*} value The value that that should be considered trusted/safe.
17508     * @returns {*} A value that can be used to stand in for the provided `value` in places
17509     * where Angular expects a $sce.trustAs() return value.
17510     */
17511
17512    /**
17513     * @ngdoc method
17514     * @name $sce#trustAsHtml
17515     *
17516     * @description
17517     * Shorthand method.  `$sce.trustAsHtml(value)` →
17518     *     {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.HTML, value)`}
17519     *
17520     * @param {*} value The value to trustAs.
17521     * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedHtml
17522     *     $sce.getTrustedHtml(value)} to obtain the original value.  (privileged directives
17523     *     only accept expressions that are either literal constants or are the
17524     *     return value of {@link ng.$sce#trustAs $sce.trustAs}.)
17525     */
17526
17527    /**
17528     * @ngdoc method
17529     * @name $sce#trustAsUrl
17530     *
17531     * @description
17532     * Shorthand method.  `$sce.trustAsUrl(value)` →
17533     *     {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.URL, value)`}
17534     *
17535     * @param {*} value The value to trustAs.
17536     * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedUrl
17537     *     $sce.getTrustedUrl(value)} to obtain the original value.  (privileged directives
17538     *     only accept expressions that are either literal constants or are the
17539     *     return value of {@link ng.$sce#trustAs $sce.trustAs}.)
17540     */
17541
17542    /**
17543     * @ngdoc method
17544     * @name $sce#trustAsResourceUrl
17545     *
17546     * @description
17547     * Shorthand method.  `$sce.trustAsResourceUrl(value)` →
17548     *     {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.RESOURCE_URL, value)`}
17549     *
17550     * @param {*} value The value to trustAs.
17551     * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedResourceUrl
17552     *     $sce.getTrustedResourceUrl(value)} to obtain the original value.  (privileged directives
17553     *     only accept expressions that are either literal constants or are the return
17554     *     value of {@link ng.$sce#trustAs $sce.trustAs}.)
17555     */
17556
17557    /**
17558     * @ngdoc method
17559     * @name $sce#trustAsJs
17560     *
17561     * @description
17562     * Shorthand method.  `$sce.trustAsJs(value)` →
17563     *     {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.JS, value)`}
17564     *
17565     * @param {*} value The value to trustAs.
17566     * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedJs
17567     *     $sce.getTrustedJs(value)} to obtain the original value.  (privileged directives
17568     *     only accept expressions that are either literal constants or are the
17569     *     return value of {@link ng.$sce#trustAs $sce.trustAs}.)
17570     */
17571
17572    /**
17573     * @ngdoc method
17574     * @name $sce#getTrusted
17575     *
17576     * @description
17577     * Delegates to {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted`}.  As such,
17578     * takes the result of a {@link ng.$sce#trustAs `$sce.trustAs`}() call and returns the
17579     * originally supplied value if the queried context type is a supertype of the created type.
17580     * If this condition isn't satisfied, throws an exception.
17581     *
17582     * @param {string} type The kind of context in which this value is to be used.
17583     * @param {*} maybeTrusted The result of a prior {@link ng.$sce#trustAs `$sce.trustAs`}
17584     *                         call.
17585     * @returns {*} The value the was originally provided to
17586     *              {@link ng.$sce#trustAs `$sce.trustAs`} if valid in this context.
17587     *              Otherwise, throws an exception.
17588     */
17589
17590    /**
17591     * @ngdoc method
17592     * @name $sce#getTrustedHtml
17593     *
17594     * @description
17595     * Shorthand method.  `$sce.getTrustedHtml(value)` →
17596     *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.HTML, value)`}
17597     *
17598     * @param {*} value The value to pass to `$sce.getTrusted`.
17599     * @returns {*} The return value of `$sce.getTrusted($sce.HTML, value)`
17600     */
17601
17602    /**
17603     * @ngdoc method
17604     * @name $sce#getTrustedCss
17605     *
17606     * @description
17607     * Shorthand method.  `$sce.getTrustedCss(value)` →
17608     *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.CSS, value)`}
17609     *
17610     * @param {*} value The value to pass to `$sce.getTrusted`.
17611     * @returns {*} The return value of `$sce.getTrusted($sce.CSS, value)`
17612     */
17613
17614    /**
17615     * @ngdoc method
17616     * @name $sce#getTrustedUrl
17617     *
17618     * @description
17619     * Shorthand method.  `$sce.getTrustedUrl(value)` →
17620     *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.URL, value)`}
17621     *
17622     * @param {*} value The value to pass to `$sce.getTrusted`.
17623     * @returns {*} The return value of `$sce.getTrusted($sce.URL, value)`
17624     */
17625
17626    /**
17627     * @ngdoc method
17628     * @name $sce#getTrustedResourceUrl
17629     *
17630     * @description
17631     * Shorthand method.  `$sce.getTrustedResourceUrl(value)` →
17632     *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.RESOURCE_URL, value)`}
17633     *
17634     * @param {*} value The value to pass to `$sceDelegate.getTrusted`.
17635     * @returns {*} The return value of `$sce.getTrusted($sce.RESOURCE_URL, value)`
17636     */
17637
17638    /**
17639     * @ngdoc method
17640     * @name $sce#getTrustedJs
17641     *
17642     * @description
17643     * Shorthand method.  `$sce.getTrustedJs(value)` →
17644     *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.JS, value)`}
17645     *
17646     * @param {*} value The value to pass to `$sce.getTrusted`.
17647     * @returns {*} The return value of `$sce.getTrusted($sce.JS, value)`
17648     */
17649
17650    /**
17651     * @ngdoc method
17652     * @name $sce#parseAsHtml
17653     *
17654     * @description
17655     * Shorthand method.  `$sce.parseAsHtml(expression string)` →
17656     *     {@link ng.$sce#parseAs `$sce.parseAs($sce.HTML, value)`}
17657     *
17658     * @param {string} expression String expression to compile.
17659     * @returns {function(context, locals)} a function which represents the compiled expression:
17660     *
17661     *    * `context` – `{object}` – an object against which any expressions embedded in the strings
17662     *      are evaluated against (typically a scope object).
17663     *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
17664     *      `context`.
17665     */
17666
17667    /**
17668     * @ngdoc method
17669     * @name $sce#parseAsCss
17670     *
17671     * @description
17672     * Shorthand method.  `$sce.parseAsCss(value)` →
17673     *     {@link ng.$sce#parseAs `$sce.parseAs($sce.CSS, value)`}
17674     *
17675     * @param {string} expression String expression to compile.
17676     * @returns {function(context, locals)} a function which represents the compiled expression:
17677     *
17678     *    * `context` – `{object}` – an object against which any expressions embedded in the strings
17679     *      are evaluated against (typically a scope object).
17680     *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
17681     *      `context`.
17682     */
17683
17684    /**
17685     * @ngdoc method
17686     * @name $sce#parseAsUrl
17687     *
17688     * @description
17689     * Shorthand method.  `$sce.parseAsUrl(value)` →
17690     *     {@link ng.$sce#parseAs `$sce.parseAs($sce.URL, value)`}
17691     *
17692     * @param {string} expression String expression to compile.
17693     * @returns {function(context, locals)} a function which represents the compiled expression:
17694     *
17695     *    * `context` – `{object}` – an object against which any expressions embedded in the strings
17696     *      are evaluated against (typically a scope object).
17697     *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
17698     *      `context`.
17699     */
17700
17701    /**
17702     * @ngdoc method
17703     * @name $sce#parseAsResourceUrl
17704     *
17705     * @description
17706     * Shorthand method.  `$sce.parseAsResourceUrl(value)` →
17707     *     {@link ng.$sce#parseAs `$sce.parseAs($sce.RESOURCE_URL, value)`}
17708     *
17709     * @param {string} expression String expression to compile.
17710     * @returns {function(context, locals)} a function which represents the compiled expression:
17711     *
17712     *    * `context` – `{object}` – an object against which any expressions embedded in the strings
17713     *      are evaluated against (typically a scope object).
17714     *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
17715     *      `context`.
17716     */
17717
17718    /**
17719     * @ngdoc method
17720     * @name $sce#parseAsJs
17721     *
17722     * @description
17723     * Shorthand method.  `$sce.parseAsJs(value)` →
17724     *     {@link ng.$sce#parseAs `$sce.parseAs($sce.JS, value)`}
17725     *
17726     * @param {string} expression String expression to compile.
17727     * @returns {function(context, locals)} a function which represents the compiled expression:
17728     *
17729     *    * `context` – `{object}` – an object against which any expressions embedded in the strings
17730     *      are evaluated against (typically a scope object).
17731     *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
17732     *      `context`.
17733     */
17734
17735    // Shorthand delegations.
17736    var parse = sce.parseAs,
17737        getTrusted = sce.getTrusted,
17738        trustAs = sce.trustAs;
17739
17740    forEach(SCE_CONTEXTS, function(enumValue, name) {
17741      var lName = lowercase(name);
17742      sce[camelCase("parse_as_" + lName)] = function(expr) {
17743        return parse(enumValue, expr);
17744      };
17745      sce[camelCase("get_trusted_" + lName)] = function(value) {
17746        return getTrusted(enumValue, value);
17747      };
17748      sce[camelCase("trust_as_" + lName)] = function(value) {
17749        return trustAs(enumValue, value);
17750      };
17751    });
17752
17753    return sce;
17754  }];
17755}
17756
17757/**
17758 * !!! This is an undocumented "private" service !!!
17759 *
17760 * @name $sniffer
17761 * @requires $window
17762 * @requires $document
17763 *
17764 * @property {boolean} history Does the browser support html5 history api ?
17765 * @property {boolean} transitions Does the browser support CSS transition events ?
17766 * @property {boolean} animations Does the browser support CSS animation events ?
17767 *
17768 * @description
17769 * This is very simple implementation of testing browser's features.
17770 */
17771function $SnifferProvider() {
17772  this.$get = ['$window', '$document', function($window, $document) {
17773    var eventSupport = {},
17774        android =
17775          toInt((/android (\d+)/.exec(lowercase(($window.navigator || {}).userAgent)) || [])[1]),
17776        boxee = /Boxee/i.test(($window.navigator || {}).userAgent),
17777        document = $document[0] || {},
17778        vendorPrefix,
17779        vendorRegex = /^(Moz|webkit|ms)(?=[A-Z])/,
17780        bodyStyle = document.body && document.body.style,
17781        transitions = false,
17782        animations = false,
17783        match;
17784
17785    if (bodyStyle) {
17786      for (var prop in bodyStyle) {
17787        if (match = vendorRegex.exec(prop)) {
17788          vendorPrefix = match[0];
17789          vendorPrefix = vendorPrefix.substr(0, 1).toUpperCase() + vendorPrefix.substr(1);
17790          break;
17791        }
17792      }
17793
17794      if (!vendorPrefix) {
17795        vendorPrefix = ('WebkitOpacity' in bodyStyle) && 'webkit';
17796      }
17797
17798      transitions = !!(('transition' in bodyStyle) || (vendorPrefix + 'Transition' in bodyStyle));
17799      animations  = !!(('animation' in bodyStyle) || (vendorPrefix + 'Animation' in bodyStyle));
17800
17801      if (android && (!transitions ||  !animations)) {
17802        transitions = isString(bodyStyle.webkitTransition);
17803        animations = isString(bodyStyle.webkitAnimation);
17804      }
17805    }
17806
17807
17808    return {
17809      // Android has history.pushState, but it does not update location correctly
17810      // so let's not use the history API at all.
17811      // http://code.google.com/p/android/issues/detail?id=17471
17812      // https://github.com/angular/angular.js/issues/904
17813
17814      // older webkit browser (533.9) on Boxee box has exactly the same problem as Android has
17815      // so let's not use the history API also
17816      // We are purposefully using `!(android < 4)` to cover the case when `android` is undefined
17817      // jshint -W018
17818      history: !!($window.history && $window.history.pushState && !(android < 4) && !boxee),
17819      // jshint +W018
17820      hasEvent: function(event) {
17821        // IE9 implements 'input' event it's so fubared that we rather pretend that it doesn't have
17822        // it. In particular the event is not fired when backspace or delete key are pressed or
17823        // when cut operation is performed.
17824        // IE10+ implements 'input' event but it erroneously fires under various situations,
17825        // e.g. when placeholder changes, or a form is focused.
17826        if (event === 'input' && msie <= 11) return false;
17827
17828        if (isUndefined(eventSupport[event])) {
17829          var divElm = document.createElement('div');
17830          eventSupport[event] = 'on' + event in divElm;
17831        }
17832
17833        return eventSupport[event];
17834      },
17835      csp: csp(),
17836      vendorPrefix: vendorPrefix,
17837      transitions: transitions,
17838      animations: animations,
17839      android: android
17840    };
17841  }];
17842}
17843
17844var $compileMinErr = minErr('$compile');
17845
17846/**
17847 * @ngdoc service
17848 * @name $templateRequest
17849 *
17850 * @description
17851 * The `$templateRequest` service runs security checks then downloads the provided template using
17852 * `$http` and, upon success, stores the contents inside of `$templateCache`. If the HTTP request
17853 * fails or the response data of the HTTP request is empty, a `$compile` error will be thrown (the
17854 * exception can be thwarted by setting the 2nd parameter of the function to true). Note that the
17855 * contents of `$templateCache` are trusted, so the call to `$sce.getTrustedUrl(tpl)` is omitted
17856 * when `tpl` is of type string and `$templateCache` has the matching entry.
17857 *
17858 * @param {string|TrustedResourceUrl} tpl The HTTP request template URL
17859 * @param {boolean=} ignoreRequestError Whether or not to ignore the exception when the request fails or the template is empty
17860 *
17861 * @return {Promise} a promise for the HTTP response data of the given URL.
17862 *
17863 * @property {number} totalPendingRequests total amount of pending template requests being downloaded.
17864 */
17865function $TemplateRequestProvider() {
17866  this.$get = ['$templateCache', '$http', '$q', '$sce', function($templateCache, $http, $q, $sce) {
17867    function handleRequestFn(tpl, ignoreRequestError) {
17868      handleRequestFn.totalPendingRequests++;
17869
17870      // We consider the template cache holds only trusted templates, so
17871      // there's no need to go through whitelisting again for keys that already
17872      // are included in there. This also makes Angular accept any script
17873      // directive, no matter its name. However, we still need to unwrap trusted
17874      // types.
17875      if (!isString(tpl) || !$templateCache.get(tpl)) {
17876        tpl = $sce.getTrustedResourceUrl(tpl);
17877      }
17878
17879      var transformResponse = $http.defaults && $http.defaults.transformResponse;
17880
17881      if (isArray(transformResponse)) {
17882        transformResponse = transformResponse.filter(function(transformer) {
17883          return transformer !== defaultHttpResponseTransform;
17884        });
17885      } else if (transformResponse === defaultHttpResponseTransform) {
17886        transformResponse = null;
17887      }
17888
17889      var httpOptions = {
17890        cache: $templateCache,
17891        transformResponse: transformResponse
17892      };
17893
17894      return $http.get(tpl, httpOptions)
17895        ['finally'](function() {
17896          handleRequestFn.totalPendingRequests--;
17897        })
17898        .then(function(response) {
17899          $templateCache.put(tpl, response.data);
17900          return response.data;
17901        }, handleError);
17902
17903      function handleError(resp) {
17904        if (!ignoreRequestError) {
17905          throw $compileMinErr('tpload', 'Failed to load template: {0} (HTTP status: {1} {2})',
17906            tpl, resp.status, resp.statusText);
17907        }
17908        return $q.reject(resp);
17909      }
17910    }
17911
17912    handleRequestFn.totalPendingRequests = 0;
17913
17914    return handleRequestFn;
17915  }];
17916}
17917
vendor: 5,440 bytes, lines 17918-18071
17918function $$TestabilityProvider() {
17919  this.$get = ['$rootScope', '$browser', '$location',
17920       function($rootScope,   $browser,   $location) {
17921
17922    /**
17923     * @name $testability
17924     *
17925     * @description
17926     * The private $$testability service provides a collection of methods for use when debugging
17927     * or by automated test and debugging tools.
17928     */
17929    var testability = {};
17930
17931    /**
17932     * @name $$testability#findBindings
17933     *
17934     * @description
17935     * Returns an array of elements that are bound (via ng-bind or {{}})
17936     * to expressions matching the input.
17937     *
17938     * @param {Element} element The element root to search from.
17939     * @param {string} expression The binding expression to match.
17940     * @param {boolean} opt_exactMatch If true, only returns exact matches
17941     *     for the expression. Filters and whitespace are ignored.
17942     */
17943    testability.findBindings = function(element, expression, opt_exactMatch) {
17944      var bindings = element.getElementsByClassName('ng-binding');
17945      var matches = [];
17946      forEach(bindings, function(binding) {
17947        var dataBinding = angular.element(binding).data('$binding');
17948        if (dataBinding) {
17949          forEach(dataBinding, function(bindingName) {
17950            if (opt_exactMatch) {
17951              var matcher = new RegExp('(^|\\s)' + escapeForRegexp(expression) + '(\\s|\\||$)');
17952              if (matcher.test(bindingName)) {
17953                matches.push(binding);
17954              }
17955            } else {
17956              if (bindingName.indexOf(expression) != -1) {
17957                matches.push(binding);
17958              }
17959            }
17960          });
17961        }
17962      });
17963      return matches;
17964    };
17965
17966    /**
17967     * @name $$testability#findModels
17968     *
17969     * @description
17970     * Returns an array of elements that are two-way found via ng-model to
17971     * expressions matching the input.
17972     *
17973     * @param {Element} element The element root to search from.
17974     * @param {string} expression The model expression to match.
17975     * @param {boolean} opt_exactMatch If true, only returns exact matches
17976     *     for the expression.
17977     */
17978    testability.findModels = function(element, expression, opt_exactMatch) {
17979      var prefixes = ['ng-', 'data-ng-', 'ng\\:'];
17980      for (var p = 0; p < prefixes.length; ++p) {
17981        var attributeEquals = opt_exactMatch ? '=' : '*=';
17982        var selector = '[' + prefixes[p] + 'model' + attributeEquals + '"' + expression + '"]';
17983        var elements = element.querySelectorAll(selector);
17984        if (elements.length) {
17985          return elements;
17986        }
17987      }
17988    };
17989
17990    /**
17991     * @name $$testability#getLocation
17992     *
17993     * @description
17994     * Shortcut for getting the location in a browser agnostic way. Returns
17995     *     the path, search, and hash. (e.g. /path?a=b#hash)
17996     */
17997    testability.getLocation = function() {
17998      return $location.url();
17999    };
18000
18001    /**
18002     * @name $$testability#setLocation
18003     *
18004     * @description
18005     * Shortcut for navigating to a location without doing a full page reload.
18006     *
18007     * @param {string} url The location url (path, search and hash,
18008     *     e.g. /path?a=b#hash) to go to.
18009     */
18010    testability.setLocation = function(url) {
18011      if (url !== $location.url()) {
18012        $location.url(url);
18013        $rootScope.$digest();
18014      }
18015    };
18016
18017    /**
18018     * @name $$testability#whenStable
18019     *
18020     * @description
18021     * Calls the callback when $timeout and $http requests are completed.
18022     *
18023     * @param {function} callback
18024     */
18025    testability.whenStable = function(callback) {
18026      $browser.notifyWhenNoOutstandingRequests(callback);
18027    };
18028
18029    return testability;
18030  }];
18031}
18032
18033function $TimeoutProvider() {
18034  this.$get = ['$rootScope', '$browser', '$q', '$$q', '$exceptionHandler',
18035       function($rootScope,   $browser,   $q,   $$q,   $exceptionHandler) {
18036
18037    var deferreds = {};
18038
18039
18040     /**
18041      * @ngdoc service
18042      * @name $timeout
18043      *
18044      * @description
18045      * Angular's wrapper for `window.setTimeout`. The `fn` function is wrapped into a try/catch
18046      * block and delegates any exceptions to
18047      * {@link ng.$exceptionHandler $exceptionHandler} service.
18048      *
18049      * The return value of calling `$timeout` is a promise, which will be resolved when
18050      * the delay has passed and the timeout function, if provided, is executed.
18051      *
18052      * To cancel a timeout request, call `$timeout.cancel(promise)`.
18053      *
18054      * In tests you can use {@link ngMock.$timeout `$timeout.flush()`} to
18055      * synchronously flush the queue of deferred functions.
18056      *
18057      * If you only want a promise that will be resolved after some specified delay
18058      * then you can call `$timeout` without the `fn` function.
18059      *
18060      * @param {function()=} fn A function, whose execution should be delayed.
18061      * @param {number=} [delay=0] Delay in milliseconds.
18062      * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
18063      *   will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
18064      * @param {...*=} Pass additional parameters to the executed function.
18065      * @returns {Promise} Promise that will be resolved when the timeout is reached. The value this
18066      *   promise will be resolved with is the return value of the `fn` function.
18067      *
18068      */
18069    function timeout(fn, delay, invokeApply) {
18070      if (!isFunction(fn)) {
18071        invokeApply = delay;
18072        delay = fn;
18073        fn = noop;
18074      }
18075
18076      var args = sliceArgs(arguments, 3),
18077          skipApply = (isDefined(invokeApply) && !invokeApply),
18078          deferred = (skipApply ? $$q : $q).defer(),
18079          promise = deferred.promise,
18080          timeoutId;
18081
18082      timeoutId = $browser.defer(function() {
18083        try {
18084          deferred.resolve(fn.apply(null, args));
18085        } catch (e) {
18086          deferred.reject(e);
18087          $exceptionHandler(e);
18088        }
18089        finally {
18090          delete deferreds[promise.$$timeoutId];
18091        }
18092
18093        if (!skipApply) $rootScope.$apply();
18094      }, delay);
18095
18096      promise.$$timeoutId = timeoutId;
18097      deferreds[timeoutId] = deferred;
18098
18099      return promise;
18100    }
18101
18102
18103     /**
18104      * @ngdoc method
18105      * @name $timeout#cancel
18106      *
18107      * @description
18108      * Cancels a task associated with the `promise`. As a result of this, the promise will be
18109      * resolved with a rejection.
18110      *
18111      * @param {Promise=} promise Promise returned by the `$timeout` function.
18112      * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully
18113      *   canceled.
18114      */
18115    timeout.cancel = function(promise) {
18116      if (promise && promise.$$timeoutId in deferreds) {
18117        deferreds[promise.$$timeoutId].reject('canceled');
18118        delete deferreds[promise.$$timeoutId];
18119        return $browser.defer.cancel(promise.$$timeoutId);
18120      }
18121      return false;
18122    };
18123
18124    return timeout;
18125  }];
18126}
18127
18128// NOTE:  The usage of window and document instead of $window and $document here is
18129// deliberate.  This service depends on the specific behavior of anchor nodes created by the
18130// browser (resolving and parsing URLs) that is unlikely to be provided by mock objects and
18131// cause us to break tests.  In addition, when the browser resolves a URL for XHR, it
18132// doesn't know about mocked locations and resolves URLs to the real document - which is
18133// exactly the behavior needed here.  There is little value is mocking these out for this
18134// service.
18135var urlParsingNode = document.createElement("a");
18136var originUrl = urlResolve(window.location.href);
18137
18138
18139/**
18140 *
18141 * Implementation Notes for non-IE browsers
18142 * ----------------------------------------
18143 * Assigning a URL to the href property of an anchor DOM node, even one attached to the DOM,
18144 * results both in the normalizing and parsing of the URL.  Normalizing means that a relative
18145 * URL will be resolved into an absolute URL in the context of the application document.
18146 * Parsing means that the anchor node's host, hostname, protocol, port, pathname and related
18147 * properties are all populated to reflect the normalized URL.  This approach has wide
18148 * compatibility - Safari 1+, Mozilla 1+, Opera 7+,e etc.  See
18149 * http://www.aptana.com/reference/html/api/HTMLAnchorElement.html
18150 *
18151 * Implementation Notes for IE
18152 * ---------------------------
18153 * IE <= 10 normalizes the URL when assigned to the anchor node similar to the other
18154 * browsers.  However, the parsed components will not be set if the URL assigned did not specify
18155 * them.  (e.g. if you assign a.href = "foo", then a.protocol, a.host, etc. will be empty.)  We
18156 * work around that by performing the parsing in a 2nd step by taking a previously normalized
18157 * URL (e.g. by assigning to a.href) and assigning it a.href again.  This correctly populates the
18158 * properties such as protocol, hostname, port, etc.
18159 *
18160 * References:
18161 *   http://developer.mozilla.org/en-US/docs/Web/API/HTMLAnchorElement
18162 *   http://www.aptana.com/reference/html/api/HTMLAnchorElement.html
18163 *   http://url.spec.whatwg.org/#urlutils
18164 *   https://github.com/angular/angular.js/pull/2902
18165 *   http://james.padolsey.com/javascript/parsing-urls-with-the-dom/
18166 *
18167 * @kind function
18168 * @param {string} url The URL to be parsed.
18169 * @description Normalizes and parses a URL.
18170 * @returns {object} Returns the normalized URL as a dictionary.
18171 *
18172 *   | member name   | Description    |
18173 *   |---------------|----------------|
18174 *   | href          | A normalized version of the provided URL if it was not an absolute URL |
18175 *   | protocol      | The protocol including the trailing colon                              |
18176 *   | host          | The host and port (if the port is non-default) of the normalizedUrl    |
18177 *   | search        | The search params, minus the question mark                             |
18178 *   | hash          | The hash string, minus the hash symbol
18179 *   | hostname      | The hostname
18180 *   | port          | The port, without ":"
18181 *   | pathname      | The pathname, beginning with "/"
18182 *
18183 */
18184function urlResolve(url) {
18185  var href = url;
18186
18187  if (msie) {
18188    // Normalize before parse.  Refer Implementation Notes on why this is
18189    // done in two steps on IE.
18190    urlParsingNode.setAttribute("href", href);
18191    href = urlParsingNode.href;
18192  }
18193
18194  urlParsingNode.setAttribute('href', href);
18195
18196  // urlParsingNode provides the UrlUtils interface - http://url.spec.whatwg.org/#urlutils
18197  return {
18198    href: urlParsingNode.href,
18199    protocol: urlParsingNode.protocol ? urlParsingNode.protocol.replace(/:$/, '') : '',
18200    host: urlParsingNode.host,
18201    search: urlParsingNode.search ? urlParsingNode.search.replace(/^\?/, '') : '',
18202    hash: urlParsingNode.hash ? urlParsingNode.hash.replace(/^#/, '') : '',
18203    hostname: urlParsingNode.hostname,
18204    port: urlParsingNode.port,
18205    pathname: (urlParsingNode.pathname.charAt(0) === '/')
18206      ? urlParsingNode.pathname
18207      : '/' + urlParsingNode.pathname
18208  };
18209}
18210
18211/**
18212 * Parse a request URL and determine whether this is a same-origin request as the application document.
18213 *
18214 * @param {string|object} requestUrl The url of the request as a string that will be resolved
18215 * or a parsed URL object.
18216 * @returns {boolean} Whether the request is for the same origin as the application document.
18217 */
18218function urlIsSameOrigin(requestUrl) {
18219  var parsed = (isString(requestUrl)) ? urlResolve(requestUrl) : requestUrl;
18220  return (parsed.protocol === originUrl.protocol &&
18221          parsed.host === originUrl.host);
18222}
18223
18224/**
18225 * @ngdoc service
18226 * @name $window
18227 *
18228 * @description
18229 * A reference to the browser's `window` object. While `window`
18230 * is globally available in JavaScript, it causes testability problems, because
18231 * it is a global variable. In angular we always refer to it through the
18232 * `$window` service, so it may be overridden, removed or mocked for testing.
18233 *
18234 * Expressions, like the one defined for the `ngClick` directive in the example
18235 * below, are evaluated with respect to the current scope.  Therefore, there is
18236 * no risk of inadvertently coding in a dependency on a global value in such an
18237 * expression.
18238 *
18239 * @example
18240   <example module="windowExample">
18241     <file name="index.html">
18242       <script>
18243         angular.module('windowExample', [])
18244           .controller('ExampleController', ['$scope', '$window', function($scope, $window) {
18245             $scope.greeting = 'Hello, World!';
18246             $scope.doGreeting = function(greeting) {
18247               $window.alert(greeting);
18248             };
18249           }]);
18250       </script>
18251       <div ng-controller="ExampleController">
18252         <input type="text" ng-model="greeting" aria-label="greeting" />
18253         <button ng-click="doGreeting(greeting)">ALERT</button>
18254       </div>
18255     </file>
18256     <file name="protractor.js" type="protractor">
18257      it('should display the greeting in the input box', function() {
18258       element(by.model('greeting')).sendKeys('Hello, E2E Tests');
18259       // If we click the button it will block the test runner
18260       // element(':button').click();
18261      });
18262     </file>
18263   </example>
18264 */
18265function $WindowProvider() {
18266  this.$get = valueFn(window);
18267}
18268
18269/**
18270 * @name $$cookieReader
18271 * @requires $document
18272 *
18273 * @description
18274 * This is a private service for reading cookies used by $http and ngCookies
18275 *
18276 * @return {Object} a key/value map of the current cookies
18277 */
18278function $$CookieReader($document) {
18279  var rawDocument = $document[0] || {};
18280  var lastCookies = {};
18281  var lastCookieString = '';
18282
18283  function safeDecodeURIComponent(str) {
18284    try {
18285      return decodeURIComponent(str);
18286    } catch (e) {
18287      return str;
18288    }
18289  }
18290
18291  return function() {
18292    var cookieArray, cookie, i, index, name;
18293    var currentCookieString = rawDocument.cookie || '';
18294
18295    if (currentCookieString !== lastCookieString) {
18296      lastCookieString = currentCookieString;
18297      cookieArray = lastCookieString.split('; ');
18298      lastCookies = {};
18299
18300      for (i = 0; i < cookieArray.length; i++) {
18301        cookie = cookieArray[i];
18302        index = cookie.indexOf('=');
18303        if (index > 0) { //ignore nameless cookies
18304          name = safeDecodeURIComponent(cookie.substring(0, index));
18305          // the first value that is seen for a cookie is the most
18306          // specific one.  values for the same cookie name that
18307          // follow are for less specific paths.
18308          if (isUndefined(lastCookies[name])) {
18309            lastCookies[name] = safeDecodeURIComponent(cookie.substring(index + 1));
18310          }
18311        }
18312      }
18313    }
18314    return lastCookies;
18315  };
18316}
18317
18318$$CookieReader.$inject = ['$document'];
18319
18320function $$CookieReaderProvider() {
18321  this.$get = $$CookieReader;
18322}
18323
18324/* global currencyFilter: true,
18325 dateFilter: true,
18326 filterFilter: true,
18327 jsonFilter: true,
18328 limitToFilter: true,
18329 lowercaseFilter: true,
18330 numberFilter: true,
18331 orderByFilter: true,
18332 uppercaseFilter: true,
18333 */
18334
18335/**
18336 * @ngdoc provider
18337 * @name $filterProvider
18338 * @description
18339 *
18340 * Filters are just functions which transform input to an output. However filters need to be
18341 * Dependency Injected. To achieve this a filter definition consists of a factory function which is
18342 * annotated with dependencies and is responsible for creating a filter function.
18343 *
18344 * <div class="alert alert-warning">
18345 * **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`.
18346 * Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace
18347 * your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
18348 * (`myapp_subsection_filterx`).
18349 * </div>
18350 *
18351 * ```js
18352 *   // Filter registration
18353 *   function MyModule($provide, $filterProvider) {
18354 *     // create a service to demonstrate injection (not always needed)
18355 *     $provide.value('greet', function(name){
18356 *       return 'Hello ' + name + '!';
18357 *     });
18358 *
18359 *     // register a filter factory which uses the
18360 *     // greet service to demonstrate DI.
18361 *     $filterProvider.register('greet', function(greet){
18362 *       // return the filter function which uses the greet service
18363 *       // to generate salutation
18364 *       return function(text) {
18365 *         // filters need to be forgiving so check input validity
18366 *         return text && greet(text) || text;
18367 *       };
18368 *     });
18369 *   }
18370 * ```
18371 *
18372 * The filter function is registered with the `$injector` under the filter name suffix with
18373 * `Filter`.
18374 *
18375 * ```js
18376 *   it('should be the same instance', inject(
18377 *     function($filterProvider) {
18378 *       $filterProvider.register('reverse', function(){
18379 *         return ...;
18380 *       });
18381 *     },
18382 *     function($filter, reverseFilter) {
18383 *       expect($filter('reverse')).toBe(reverseFilter);
18384 *     });
18385 * ```
18386 *
18387 *
18388 * For more information about how angular filters work, and how to create your own filters, see
18389 * {@link guide/filter Filters} in the Angular Developer Guide.
18390 */
18391
18392/**
18393 * @ngdoc service
18394 * @name $filter
18395 * @kind function
18396 * @description
18397 * Filters are used for formatting data displayed to the user.
18398 *
18399 * The general syntax in templates is as follows:
18400 *
18401 *         {{ expression [| filter_name[:parameter_value] ... ] }}
18402 *
18403 * @param {String} name Name of the filter function to retrieve
18404 * @return {Function} the filter function
18405 * @example
18406   <example name="$filter" module="filterExample">
18407     <file name="index.html">
18408       <div ng-controller="MainCtrl">
18409        <h3>{{ originalText }}</h3>
18410        <h3>{{ filteredText }}</h3>
18411       </div>
18412     </file>
18413
18414     <file name="script.js">
18415      angular.module('filterExample', [])
18416      .controller('MainCtrl', function($scope, $filter) {
18417        $scope.originalText = 'hello';
18418        $scope.filteredText = $filter('uppercase')($scope.originalText);
18419      });
18420     </file>
18421   </example>
18422  */
18423$FilterProvider.$inject = ['$provide'];
18424function $FilterProvider($provide) {
18425  var suffix = 'Filter';
18426
18427  /**
18428   * @ngdoc method
18429   * @name $filterProvider#register
18430   * @param {string|Object} name Name of the filter function, or an object map of filters where
18431   *    the keys are the filter names and the values are the filter factories.
18432   *
18433   *    <div class="alert alert-warning">
18434   *    **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`.
18435   *    Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace
18436   *    your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
18437   *    (`myapp_subsection_filterx`).
18438   *    </div>
18439    * @param {Function} factory If the first argument was a string, a factory function for the filter to be registered.
18440   * @returns {Object} Registered filter instance, or if a map of filters was provided then a map
18441   *    of the registered filter instances.
18442   */
18443  function register(name, factory) {
18444    if (isObject(name)) {
18445      var filters = {};
18446      forEach(name, function(filter, key) {
18447        filters[key] = register(key, filter);
18448      });
18449      return filters;
18450    } else {
18451      return $provide.factory(name + suffix, factory);
18452    }
18453  }
18454  this.register = register;
18455
18456  this.$get = ['$injector', function($injector) {
18457    return function(name) {
18458      return $injector.get(name + suffix);
18459    };
18460  }];
18461
18462  ////////////////////////////////////////
18463
18464  /* global
18465    currencyFilter: false,
18466    dateFilter: false,
18467    filterFilter: false,
18468    jsonFilter: false,
18469    limitToFilter: false,
18470    lowercaseFilter: false,
18471    numberFilter: false,
18472    orderByFilter: false,
18473    uppercaseFilter: false,
18474  */
18475
18476  register('currency', currencyFilter);
18477  register('date', dateFilter);
18478  register('filter', filterFilter);
18479  register('json', jsonFilter);
18480  register('limitTo', limitToFilter);
18481  register('lowercase', lowercaseFilter);
18482  register('number', numberFilter);
18483  register('orderBy', orderByFilter);
18484  register('uppercase', uppercaseFilter);
18485}
18486
18487/**
18488 * @ngdoc filter
18489 * @name filter
18490 * @kind function
18491 *
18492 * @description
18493 * Selects a subset of items from `array` and returns it as a new array.
18494 *
18495 * @param {Array} array The source array.
18496 * @param {string|Object|function()} expression The predicate to be used for selecting items from
18497 *   `array`.
18498 *
18499 *   Can be one of:
18500 *
18501 *   - `string`: The string is used for matching against the contents of the `array`. All strings or
18502 *     objects with string properties in `array` that match this string will be returned. This also
18503 *     applies to nested object properties.
18504 *     The predicate can be negated by prefixing the string with `!`.
18505 *
18506 *   - `Object`: A pattern object can be used to filter specific properties on objects contained
18507 *     by `array`. For example `{name:"M", phone:"1"}` predicate will return an array of items
18508 *     which have property `name` containing "M" and property `phone` containing "1". A special
18509 *     property name `$` can be used (as in `{$:"text"}`) to accept a match against any
18510 *     property of the object or its nested object properties. That's equivalent to the simple
18511 *     substring match with a `string` as described above. The predicate can be negated by prefixing
18512 *     the string with `!`.
18513 *     For example `{name: "!M"}` predicate will return an array of items which have property `name`
18514 *     not containing "M".
18515 *
18516 *     Note that a named property will match properties on the same level only, while the special
18517 *     `$` property will match properties on the same level or deeper. E.g. an array item like
18518 *     `{name: {first: 'John', last: 'Doe'}}` will **not** be matched by `{name: 'John'}`, but
18519 *     **will** be matched by `{$: 'John'}`.
18520 *
18521 *   - `function(value, index, array)`: A predicate function can be used to write arbitrary filters.
18522 *     The function is called for each element of the array, with the element, its index, and
18523 *     the entire array itself as arguments.
18524 *
18525 *     The final result is an array of those elements that the predicate returned true for.
18526 *
18527 * @param {function(actual, expected)|true|undefined} comparator Comparator which is used in
18528 *     determining if the expected value (from the filter expression) and actual value (from
18529 *     the object in the array) should be considered a match.
18530 *
18531 *   Can be one of:
18532 *
18533 *   - `function(actual, expected)`:
18534 *     The function will be given the object value and the predicate value to compare and
18535 *     should return true if both values should be considered equal.
18536 *
18537 *   - `true`: A shorthand for `function(actual, expected) { return angular.equals(actual, expected)}`.
18538 *     This is essentially strict comparison of expected and actual.
18539 *
18540 *   - `false|undefined`: A short hand for a function which will look for a substring match in case
18541 *     insensitive way.
18542 *
18543 *     Primitive values are converted to strings. Objects are not compared against primitives,
18544 *     unless they have a custom `toString` method (e.g. `Date` objects).
18545 *
18546 * @example
18547   <example>
18548     <file name="index.html">
18549       <div ng-init="friends = [{name:'John', phone:'555-1276'},
18550                                {name:'Mary', phone:'800-BIG-MARY'},
18551                                {name:'Mike', phone:'555-4321'},
18552                                {name:'Adam', phone:'555-5678'},
18553                                {name:'Julie', phone:'555-8765'},
18554                                {name:'Juliette', phone:'555-5678'}]"></div>
18555
18556       <label>Search: <input ng-model="searchText"></label>
18557       <table id="searchTextResults">
18558         <tr><th>
18558Name</th><th>Phone</th></tr>
18559         <tr ng-repeat="friend in friends | filter:searchText">
18560           <td>{{friend.name}}</td>
18561           <td>{{friend.phone}}</td>
18562         </tr>
18563       </table>
18564       <hr>
18565       <label>Any: <input ng-model="search.$"></label> <br>
18566       <label>Name only <input ng-model="search.name"></label><br>
18567       <label>Phone only <input ng-model="search.phone"></label><br>
18568       <label>Equality <input type="checkbox" ng-model="strict"></label><br>
18569       <table id="searchObjResults">
18570         <tr><th>Name</th><th>Phone</th></tr>
18571         <tr ng-repeat="friendObj in friends | filter:search:strict">
18572           <td>{{friendObj.name}}</td>
18573           <td>{{friendObj.phone}}</td>
18574         </tr>
18575       </table>
18576     </file>
18577     <file name="protractor.js" type="protractor">
18578       var expectFriendNames = function(expectedNames, key) {
18579         element.all(by.repeater(key + ' in friends').column(key + '.name')).then(function(arr) {
18580           arr.forEach(function(wd, i) {
18581             expect(wd.getText()).toMatch(expectedNames[i]);
18582           });
18583         });
18584       };
18585
18586       it('should search across all fields when filtering with a string', function() {
18587         var searchText = element(by.model('searchText'));
18588         searchText.clear();
18589         searchText.sendKeys('m');
18590         expectFriendNames(['Mary', 'Mike', 'Adam'], 'friend');
18591
18592         searchText.clear();
18593         searchText.sendKeys('76');
18594         expectFriendNames(['John', 'Julie'], 'friend');
18595       });
18596
18597       it('should search in specific fields when filtering with a predicate object', function() {
18598         var searchAny = element(by.model('search.$'));
18599         searchAny.clear();
18600         searchAny.sendKeys('i');
18601         expectFriendNames(['Mary', 'Mike', 'Julie', 'Juliette'], 'friendObj');
18602       });
18603       it('should use a equal comparison when comparator is true', function() {
18604         var searchName = element(by.model('search.name'));
18605         var strict = element(by.model('strict'));
18606         searchName.clear();
18607         searchName.sendKeys('Julie');
18608         strict.click();
18609         expectFriendNames(['Julie'], 'friendObj');
18610       });
18611     </file>
18612   </example>
18613 */
18614function filterFilter() {
18615  return function(array, expression, comparator) {
18616    if (!isArrayLike(array)) {
18617      if (array == null) {
18618        return array;
18619      } else {
18620        throw minErr('filter')('notarray', 'Expected array but received: {0}', array);
18621      }
18622    }
18623
18624    var expressionType = getTypeForFilter(expression);
18625    var predicateFn;
18626    var matchAgainstAnyProp;
18627
18628    switch (expressionType) {
18629      case 'function':
18630        predicateFn = expression;
18631        break;
18632      case 'boolean':
18633      case 'null':
18634      case 'number':
18635      case 'string':
18636        matchAgainstAnyProp = true;
18637        //jshint -W086
18638      case 'object':
18639        //jshint +W086
18640        predicateFn = createPredicateFn(expression, comparator, matchAgainstAnyProp);
18641        break;
18642      default:
18643        return array;
18644    }
18645
18646    return Array.prototype.filter.call(array, predicateFn);
18647  };
18648}
18649
18650// Helper functions for `filterFilter`
18651function createPredicateFn(expression, comparator, matchAgainstAnyProp) {
18652  var shouldMatchPrimitives = isObject(expression) && ('$' in expression);
18653  var predicateFn;
18654
18655  if (comparator === true) {
18656    comparator = equals;
18657  } else if (!isFunction(comparator)) {
18658    comparator = function(actual, expected) {
18659      if (isUndefined(actual)) {
18660        // No substring matching against `undefined`
18661        return false;
18662      }
18663      if ((actual === null) || (expected === null)) {
18664        // No substring matching against `null`; only match against `null`
18665        return actual === expected;
18666      }
18667      if (isObject(expected) || (isObject(actual) && !hasCustomToString(actual))) {
18668        // Should not compare primitives against objects, unless they have custom `toString` method
18669        return false;
18670      }
18671
18672      actual = lowercase('' + actual);
18673      expected = lowercase('' + expected);
18674      return actual.indexOf(expected) !== -1;
18675    };
18676  }
18677
18678  predicateFn = function(item) {
18679    if (shouldMatchPrimitives && !isObject(item)) {
18680      return deepCompare(item, expression.$, comparator, false);
18681    }
18682    return deepCompare(item, expression, comparator, matchAgainstAnyProp);
18683  };
18684
18685  return predicateFn;
18686}
18687
18688function deepCompare(actual, expected, comparator, matchAgainstAnyProp, dontMatchWholeObject) {
18689  var actualType = getTypeForFilter(actual);
18690  var expectedType = getTypeForFilter(expected);
18691
18692  if ((expectedType === 'string') && (expected.charAt(0) === '!')) {
18693    return !deepCompare(actual, expected.substring(1), comparator, matchAgainstAnyProp);
18694  } else if (isArray(actual)) {
18695    // In case `actual` is an array, consider it a match
18696    // if ANY of it's items matches `expected`
18697    return actual.some(function(item) {
18698      return deepCompare(item, expected, comparator, matchAgainstAnyProp);
18699    });
18700  }
18701
18702  switch (actualType) {
18703    case 'object':
18704      var key;
18705      if (matchAgainstAnyProp) {
18706        for (key in actual) {
18707          if ((key.charAt(0) !== '$') && deepCompare(actual[key], expected, comparator, true)) {
18708            return true;
18709          }
18710        }
18711        return dontMatchWholeObject ? false : deepCompare(actual, expected, comparator, false);
18712      } else if (expectedType === 'object') {
18713        for (key in expected) {
18714          var expectedVal = expected[key];
18715          if (isFunction(expectedVal) || isUndefined(expectedVal)) {
18716            continue;
18717          }
18718
18719          var matchAnyProperty = key === '$';
18720          var actualVal = matchAnyProperty ? actual : actual[key];
18721          if (!deepCompare(actualVal, expectedVal, comparator, matchAnyProperty, matchAnyProperty)) {
18722            return false;
18723          }
18724        }
18725        return true;
18726      } else {
18727        return comparator(actual, expected);
18728      }
18729      break;
18730    case 'function':
18731      return false;
18732    default:
18733      return comparator(actual, expected);
18734  }
18735}
18736
18737// Used for easily differentiating between `null` and actual `object`
18738function getTypeForFilter(val) {
18739  return (val === null) ? 'null' : typeof val;
18740}
18741
18742/**
18743 * @ngdoc filter
18744 * @name currency
18745 * @kind function
18746 *
18747 * @description
18748 * Formats a number as a currency (ie $1,234.56). When no currency symbol is provided, default
18749 * symbol for current locale is used.
18750 *
18751 * @param {number} amount Input to filter.
18752 * @param {string=} symbol Currency symbol or identifier to be displayed.
18753 * @param {number=} fractionSize Number of decimal places to round the amount to, defaults to default max fraction size for current locale
18754 * @returns {string} Formatted number.
18755 *
18756 *
18757 * @example
18758   <example module="currencyExample">
18759     <file name="index.html">
18760       <script>
18761         angular.module('currencyExample', [])
18762           .controller('ExampleController', ['$scope', function($scope) {
18763             $scope.amount = 1234.56;
18764           }]);
18765       </script>
18766       <div ng-controller="ExampleController">
18767         <input type="number" ng-model="amount" aria-label="amount"> <br>
18768         default currency symbol ($): <span id="currency-default">{{amount | currency}}</span><br>
18769         custom currency identifier (USD$): <span id="currency-custom">{{amount | currency:"USD$"}}</span>
18770         no fractions (0): <span id="currency-no-fractions">{{amount | currency:"USD$":0}}</span>
18771       </div>
18772     </file>
18773     <file name="protractor.js" type="protractor">
18774       it('should init with 1234.56', function() {
18775         expect(element(by.id('currency-default')).getText()).toBe('$1,234.56');
18776         expect(element(by.id('currency-custom')).getText()).toBe('USD$1,234.56');
18777         expect(element(by.id('currency-no-fractions')).getText()).toBe('USD$1,235');
18778       });
18779       it('should update', function() {
18780         if (browser.params.browser == 'safari') {
18781           // Safari does not understand the minus key. See
18782           // https://github.com/angular/protractor/issues/481
18783           return;
18784         }
18785         element(by.model('amount')).clear();
18786         element(by.model('amount')).sendKeys('-1234');
18787         expect(element(by.id('currency-default')).getText()).toBe('-$1,234.00');
18788         expect(element(by.id('currency-custom')).getText()).toBe('-USD$1,234.00');
18789         expect(element(by.id('currency-no-fractions')).getText()).toBe('-USD$1,234');
18790       });
18791     </file>
18792   </example>
18793 */
18794currencyFilter.$inject = ['$locale'];
18795function currencyFilter($locale) {
18796  var formats = $locale.NUMBER_FORMATS;
18797  return function(amount, currencySymbol, fractionSize) {
18798    if (isUndefined(currencySymbol)) {
18799      currencySymbol = formats.CURRENCY_SYM;
18800    }
18801
18802    if (isUndefined(fractionSize)) {
18803      fractionSize = formats.PATTERNS[1].maxFrac;
18804    }
18805
18806    // if null or undefined pass it through
18807    return (amount == null)
18808        ? amount
18809        : formatNumber(amount, formats.PATTERNS[1], formats.GROUP_SEP, formats.DECIMAL_SEP, fractionSize).
18810            replace(/\u00A4/g, currencySymbol);
18811  };
18812}
18813
18814/**
18815 * @ngdoc filter
18816 * @name number
18817 * @kind function
18818 *
18819 * @description
18820 * Formats a number as text.
18821 *
18822 * If the input is null or undefined, it will just be returned.
18823 * If the input is infinite (Infinity/-Infinity) the Infinity symbol '∞' is returned.
18824 * If the input is not a number an empty string is returned.
18825 *
18826 *
18827 * @param {number|string} number Number to format.
18828 * @param {(number|string)=} fractionSize Number of decimal places to round the number to.
18829 * If this is not provided then the fraction size is computed from the current locale's number
18830 * formatting pattern. In the case of the default locale, it will be 3.
18831 * @returns {string} Number rounded to decimalPlaces and places a “,” after each third digit.
18832 *
18833 * @example
18834   <example module="numberFilterExample">
18835     <file name="index.html">
18836       <script>
18837         angular.module('numberFilterExample', [])
18838           .controller('ExampleController', ['$scope', function($scope) {
18839             $scope.val = 1234.56789;
18840           }]);
18841       </script>
18842       <div ng-controller="ExampleController">
18843         <label>Enter number: <input ng-model='val'></label><br>
18844         Default formatting: <span id='number-default'>{{val | number}}</span><br>
18845         No fractions: <span>{{val | number:0}}</span><br>
18846         Negative number: <span>{{-val | number:4}}</span>
18847       </div>
18848     </file>
18849     <file name="protractor.js" type="protractor">
18850       it('should format numbers', function() {
18851         expect(element(by.id('number-default')).getText()).toBe('1,234.568');
18852         expect(element(by.binding('val | number:0')).getText()).toBe('1,235');
18853         expect(element(by.binding('-val | number:4')).getText()).toBe('-1,234.5679');
18854       });
18855
18856       it('should update', function() {
18857         element(by.model('val')).clear();
18858         element(by.model('val')).sendKeys('3374.333');
18859         expect(element(by.id('number-default')).getText()).toBe('3,374.333');
18860         expect(element(by.binding('val | number:0')).getText()).toBe('3,374');
18861         expect(element(by.binding('-val | number:4')).getText()).toBe('-3,374.3330');
18862      });
18863     </file>
18864   </example>
18865 */
18866
18867
18868numberFilter.$inject = ['$locale'];
18869function numberFilter($locale) {
18870  var formats = $locale.NUMBER_FORMATS;
18871  return function(number, fractionSize) {
18872
18873    // if null or undefined pass it through
18874    return (number == null)
18875        ? number
18876        : formatNumber(number, formats.PATTERNS[0], formats.GROUP_SEP, formats.DECIMAL_SEP,
18877                       fractionSize);
18878  };
18879}
18880
18881var DECIMAL_SEP = '.';
18882function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) {
18883  if (isObject(number)) return '';
18884
18885  var isNegative = number < 0;
18886  number = Math.abs(number);
18887
18888  var isInfinity = number === Infinity;
18889  if (!isInfinity && !isFinite(number)) return '';
18890
18891  var numStr = number + '',
18892      formatedText = '',
18893      hasExponent = false,
18894      parts = [];
18895
18896  if (isInfinity) formatedText = '\u221e';
18897
18898  if (!isInfinity && numStr.indexOf('e') !== -1) {
18899    var match = numStr.match(/([\d\.]+)e(-?)(\d+)/);
18900    if (match && match[2] == '-' && match[3] > fractionSize + 1) {
18901      number = 0;
18902    } else {
18903      formatedText = numStr;
18904      hasExponent = true;
18905    }
18906  }
18907
18908  if (!isInfinity && !hasExponent) {
18909    var fractionLen = (numStr.split(DECIMAL_SEP)[1] || '').length;
18910
18911    // determine fractionSize if it is not specified
18912    if (isUndefined(fractionSize)) {
18913      fractionSize = Math.min(Math.max(pattern.minFrac, fractionLen), pattern.maxFrac);
18914    }
18915
18916    // safely round numbers in JS without hitting imprecisions of floating-point arithmetics
18917    // inspired by:
18918    // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round
18919    number = +(Math.round(+(number.toString() + 'e' + fractionSize)).toString() + 'e' + -fractionSize);
18920
18921    var fraction = ('' + number).split(DECIMAL_SEP);
18922    var whole = fraction[0];
18923    fraction = fraction[1] || '';
18924
18925    var i, pos = 0,
18926        lgroup = pattern.lgSize,
18927        group = pattern.gSize;
18928
18929    if (whole.length >= (lgroup + group)) {
18930      pos = whole.length - lgroup;
18931      for (i = 0; i < pos; i++) {
18932        if ((pos - i) % group === 0 && i !== 0) {
18933          formatedText += groupSep;
18934        }
18935        formatedText += whole.charAt(i);
18936      }
18937    }
18938
18939    for (i = pos; i < whole.length; i++) {
18940      if ((whole.length - i) % lgroup === 0 && i !== 0) {
18941        formatedText += groupSep;
18942      }
18943      formatedText += whole.charAt(i);
18944    }
18945
18946    // format fraction part.
18947    while (fraction.length < fractionSize) {
18948      fraction += '0';
18949    }
18950
18951    if (fractionSize && fractionSize !== "0") formatedText += decimalSep + fraction.substr(0, fractionSize);
18952  } else {
18953    if (fractionSize > 0 && number < 1) {
18954      formatedText = number.toFixed(fractionSize);
18955      number = parseFloat(formatedText);
18956      formatedText = formatedText.replace(DECIMAL_SEP, decimalSep);
18957    }
18958  }
18959
18960  if (number === 0) {
18961    isNegative = false;
18962  }
18963
18964  parts.push(isNegative ? pattern.negPre : pattern.posPre,
18965             formatedText,
18966             isNegative ? pattern.negSuf : pattern.posSuf);
18967  return parts.join('');
18968}
18969
18970function padNumber(num, digits, trim) {
18971  var neg = '';
18972  if (num < 0) {
18973    neg =  '-';
18974    num = -num;
18975  }
18976  num = '' + num;
18977  while (num.length < digits) num = '0' + num;
18978  if (trim) {
18979    num = num.substr(num.length - digits);
18980  }
18981  return neg + num;
18982}
18983
18984
18985function dateGetter(name, size, offset, trim) {
18986  offset = offset || 0;
18987  return function(date) {
18988    var value = date['get' + name]();
18989    if (offset > 0 || value > -offset) {
18990      value += offset;
18991    }
18992    if (value === 0 && offset == -12) value = 12;
18993    return padNumber(value, size, trim);
18994  };
18995}
18996
18997function dateStrGetter(name, shortForm) {
18998  return function(date, formats) {
18999    var value = date['get' + name]();
19000    var get = uppercase(shortForm ? ('SHORT' + name) : name);
19001
19002    return formats[get][value];
19003  };
19004}
19005
19006function timeZoneGetter(date, formats, offset) {
19007  var zone = -1 * offset;
19008  var paddedZone = (zone >= 0) ? "+" : "";
19009
19010  paddedZone += padNumber(Math[zone > 0 ? 'floor' : 'ceil'](zone / 60), 2) +
19011                padNumber(Math.abs(zone % 60), 2);
19012
19013  return paddedZone;
19014}
19015
19016function getFirstThursdayOfYear(year) {
19017    // 0 = index of January
19018    var dayOfWeekOnFirst = (new Date(year, 0, 1)).getDay();
19019    // 4 = index of Thursday (+1 to account for 1st = 5)
19020    // 11 = index of *next* Thursday (+1 account for 1st = 12)
19021    return new Date(year, 0, ((dayOfWeekOnFirst <= 4) ? 5 : 12) - dayOfWeekOnFirst);
19022}
19023
19024function getThursdayThisWeek(datetime) {
19025    return new Date(datetime.getFullYear(), datetime.getMonth(),
19026      // 4 = index of Thursday
19027      datetime.getDate() + (4 - datetime.getDay()));
19028}
19029
19030function weekGetter(size) {
19031   return function(date) {
19032      var firstThurs = getFirstThursdayOfYear(date.getFullYear()),
19033         thisThurs = getThursdayThisWeek(date);
19034
19035      var diff = +thisThurs - +firstThurs,
19036         result = 1 + Math.round(diff / 6.048e8); // 6.048e8 ms per week
19037
19038      return padNumber(result, size);
19039   };
19040}
19041
19042function ampmGetter(date, formats) {
19043  return date.getHours() < 12 ? formats.AMPMS[0] : formats.AMPMS[1];
19044}
19045
19046function eraGetter(date, formats) {
vendor: 5,157 bytes, lines 19047-19159
19047  return date.getFullYear() <= 0 ? formats.ERAS[0] : formats.ERAS[1];
19048}
19049
19050function longEraGetter(date, formats) {
19051  return date.getFullYear() <= 0 ? formats.ERANAMES[0] : formats.ERANAMES[1];
19052}
19053
19054var DATE_FORMATS = {
19055  yyyy: dateGetter('FullYear', 4),
19056    yy: dateGetter('FullYear', 2, 0, true),
19057     y: dateGetter('FullYear', 1),
19058  MMMM: dateStrGetter('Month'),
19059   MMM: dateStrGetter('Month', true),
19060    MM: dateGetter('Month', 2, 1),
19061     M: dateGetter('Month', 1, 1),
19062    dd: dateGetter('Date', 2),
19063     d: dateGetter('Date', 1),
19064    HH: dateGetter('Hours', 2),
19065     H: dateGetter('Hours', 1),
19066    hh: dateGetter('Hours', 2, -12),
19067     h: dateGetter('Hours', 1, -12),
19068    mm: dateGetter('Minutes', 2),
19069     m: dateGetter('Minutes', 1),
19070    ss: dateGetter('Seconds', 2),
19071     s: dateGetter('Seconds', 1),
19072     // while ISO 8601 requires fractions to be prefixed with `.` or `,`
19073     // we can be just safely rely on using `sss` since we currently don't support single or two digit fractions
19074   sss: dateGetter('Milliseconds', 3),
19075  EEEE: dateStrGetter('Day'),
19076   EEE: dateStrGetter('Day', true),
19077     a: ampmGetter,
19078     Z: timeZoneGetter,
19079    ww: weekGetter(2),
19080     w: weekGetter(1),
19081     G: eraGetter,
19082     GG: eraGetter,
19083     GGG: eraGetter,
19084     GGGG: longEraGetter
19085};
19086
19087var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZEwG']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z|G+|w+))(.*)/,
19088    NUMBER_STRING = /^\-?\d+$/;
19089
19090/**
19091 * @ngdoc filter
19092 * @name date
19093 * @kind function
19094 *
19095 * @description
19096 *   Formats `date` to a string based on the requested `format`.
19097 *
19098 *   `format` string can be composed of the following elements:
19099 *
19100 *   * `'yyyy'`: 4 digit representation of year (e.g. AD 1 => 0001, AD 2010 => 2010)
19101 *   * `'yy'`: 2 digit representation of year, padded (00-99). (e.g. AD 2001 => 01, AD 2010 => 10)
19102 *   * `'y'`: 1 digit representation of year, e.g. (AD 1 => 1, AD 199 => 199)
19103 *   * `'MMMM'`: Month in year (January-December)
19104 *   * `'MMM'`: Month in year (Jan-Dec)
19105 *   * `'MM'`: Month in year, padded (01-12)
19106 *   * `'M'`: Month in year (1-12)
19107 *   * `'dd'`: Day in month, padded (01-31)
19108 *   * `'d'`: Day in month (1-31)
19109 *   * `'EEEE'`: Day in Week,(Sunday-Saturday)
19110 *   * `'EEE'`: Day in Week, (Sun-Sat)
19111 *   * `'HH'`: Hour in day, padded (00-23)
19112 *   * `'H'`: Hour in day (0-23)
19113 *   * `'hh'`: Hour in AM/PM, padded (01-12)
19114 *   * `'h'`: Hour in AM/PM, (1-12)
19115 *   * `'mm'`: Minute in hour, padded (00-59)
19116 *   * `'m'`: Minute in hour (0-59)
19117 *   * `'ss'`: Second in minute, padded (00-59)
19118 *   * `'s'`: Second in minute (0-59)
19119 *   * `'sss'`: Millisecond in second, padded (000-999)
19120 *   * `'a'`: AM/PM marker
19121 *   * `'Z'`: 4 digit (+sign) representation of the timezone offset (-1200-+1200)
19122 *   * `'ww'`: Week of year, padded (00-53). Week 01 is the week with the first Thursday of the year
19123 *   * `'w'`: Week of year (0-53). Week 1 is the week with the first Thursday of the year
19124 *   * `'G'`, `'GG'`, `'GGG'`: The abbreviated form of the era string (e.g. 'AD')
19125 *   * `'GGGG'`: The long form of the era string (e.g. 'Anno Domini')
19126 *
19127 *   `format` string can also be one of the following predefined
19128 *   {@link guide/i18n localizable formats}:
19129 *
19130 *   * `'medium'`: equivalent to `'MMM d, y h:mm:ss a'` for en_US locale
19131 *     (e.g. Sep 3, 2010 12:05:08 PM)
19132 *   * `'short'`: equivalent to `'M/d/yy h:mm a'` for en_US  locale (e.g. 9/3/10 12:05 PM)
19133 *   * `'fullDate'`: equivalent to `'EEEE, MMMM d, y'` for en_US  locale
19134 *     (e.g. Friday, September 3, 2010)
19135 *   * `'longDate'`: equivalent to `'MMMM d, y'` for en_US  locale (e.g. September 3, 2010)
19136 *   * `'mediumDate'`: equivalent to `'MMM d, y'` for en_US  locale (e.g. Sep 3, 2010)
19137 *   * `'shortDate'`: equivalent to `'M/d/yy'` for en_US locale (e.g. 9/3/10)
19138 *   * `'mediumTime'`: equivalent to `'h:mm:ss a'` for en_US locale (e.g. 12:05:08 PM)
19139 *   * `'shortTime'`: equivalent to `'h:mm a'` for en_US locale (e.g. 12:05 PM)
19140 *
19141 *   `format` string can contain literal values. These need to be escaped by surrounding with single quotes (e.g.
19142 *   `"h 'in the morning'"`). In order to output a single quote, escape it - i.e., two single quotes in a sequence
19143 *   (e.g. `"h 'o''clock'"`).
19144 *
19145 * @param {(Date|number|string)} date Date to format either as Date object, milliseconds (string or
19146 *    number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.sssZ and its
19147 *    shorter versions like yyyy-MM-ddTHH:mmZ, yyyy-MM-dd or yyyyMMddTHHmmssZ). If no timezone is
19148 *    specified in the string input, the time is considered to be in the local timezone.
19149 * @param {string=} format Formatting rules (see Description). If not specified,
19150 *    `mediumDate` is used.
19151 * @param {string=} timezone Timezone to be used for formatting. It understands UTC/GMT and the
19152 *    continental US time zone abbreviations, but for general use, use a time zone offset, for
19153 *    example, `'+0430'` (4 hours, 30 minutes east of the Greenwich meridian)
19154 *    If not specified, the timezone of the browser will be used.
19155 * @returns {string} Formatted string or the input if input is not recognized as date/millis.
19156 *
19157 * @example
19158   <example>
19159     <file name="index.html">
vendor: 4,078 bytes, lines 19160-19272
19160       <span ng-non-bindable>{{1288323623006 | date:'medium'}}</span>:
19161           <span>{{1288323623006 | date:'medium'}}</span><br>
19162       <span ng-non-bindable>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span>:
19163          <span>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span><br>
19164       <span ng-non-bindable>{{1288323623006 | date:'MM/dd/yyyy @ h:mma'}}</span>:
19165          <span>{{'1288323623006' | date:'MM/dd/yyyy @ h:mma'}}</span><br>
19166       <span ng-non-bindable>{{1288323623006 | date:"MM/dd/yyyy 'at' h:mma"}}</span>:
19167          <span>{{'1288323623006' | date:"MM/dd/yyyy 'at' h:mma"}}</span><br>
19168     </file>
19169     <file name="protractor.js" type="protractor">
19170       it('should format date', function() {
19171         expect(element(by.binding("1288323623006 | date:'medium'")).getText()).
19172            toMatch(/Oct 2\d, 2010 \d{1,2}:\d{2}:\d{2} (AM|PM)/);
19173         expect(element(by.binding("1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'")).getText()).
19174            toMatch(/2010\-10\-2\d \d{2}:\d{2}:\d{2} (\-|\+)?\d{4}/);
19175         expect(element(by.binding("'1288323623006' | date:'MM/dd/yyyy @ h:mma'")).getText()).
19176            toMatch(/10\/2\d\/2010 @ \d{1,2}:\d{2}(AM|PM)/);
19177         expect(element(by.binding("'1288323623006' | date:\"MM/dd/yyyy 'at' h:mma\"")).getText()).
19178            toMatch(/10\/2\d\/2010 at \d{1,2}:\d{2}(AM|PM)/);
19179       });
19180     </file>
19181   </example>
19182 */
19183dateFilter.$inject = ['$locale'];
19184function dateFilter($locale) {
19185
19186
19187  var R_ISO8601_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/;
19188                     // 1        2       3         4          5          6          7          8  9     10      11
19189  function jsonStringToDate(string) {
19190    var match;
19191    if (match = string.match(R_ISO8601_STR)) {
19192      var date = new Date(0),
19193          tzHour = 0,
19194          tzMin  = 0,
19195          dateSetter = match[8] ? date.setUTCFullYear : date.setFullYear,
19196          timeSetter = match[8] ? date.setUTCHours : date.setHours;
19197
19198      if (match[9]) {
19199        tzHour = toInt(match[9] + match[10]);
19200        tzMin = toInt(match[9] + match[11]);
19201      }
19202      dateSetter.call(date, toInt(match[1]), toInt(match[2]) - 1, toInt(match[3]));
19203      var h = toInt(match[4] || 0) - tzHour;
19204      var m = toInt(match[5] || 0) - tzMin;
19205      var s = toInt(match[6] || 0);
19206      var ms = Math.round(parseFloat('0.' + (match[7] || 0)) * 1000);
19207      timeSetter.call(date, h, m, s, ms);
19208      return date;
19209    }
19210    return string;
19211  }
19212
19213
19214  return function(date, format, timezone) {
19215    var text = '',
19216        parts = [],
19217        fn, match;
19218
19219    format = format || 'mediumDate';
19220    format = $locale.DATETIME_FORMATS[format] || format;
19221    if (isString(date)) {
19222      date = NUMBER_STRING.test(date) ? toInt(date) : jsonStringToDate(date);
19223    }
19224
19225    if (isNumber(date)) {
19226      date = new Date(date);
19227    }
19228
19229    if (!isDate(date) || !isFinite(date.getTime())) {
19230      return date;
19231    }
19232
19233    while (format) {
19234      match = DATE_FORMATS_SPLIT.exec(format);
19235      if (match) {
19236        parts = concat(parts, match, 1);
19237        format = parts.pop();
19238      } else {
19239        parts.push(format);
19240        format = null;
19241      }
19242    }
19243
19244    var dateTimezoneOffset = date.getTimezoneOffset();
19245    if (timezone) {
19246      dateTimezoneOffset = timezoneToOffset(timezone, date.getTimezoneOffset());
19247      date = convertTimezoneToLocal(date, timezone, true);
19248    }
19249    forEach(parts, function(value) {
19250      fn = DATE_FORMATS[value];
19251      text += fn ? fn(date, $locale.DATETIME_FORMATS, dateTimezoneOffset)
19252                 : value.replace(/(^'|'$)/g, '').replace(/''/g, "'");
19253    });
19254
19255    return text;
19256  };
19257}
19258
19259
19260/**
19261 * @ngdoc filter
19262 * @name json
19263 * @kind function
19264 *
19265 * @description
19266 *   Allows you to convert a JavaScript object into JSON string.
19267 *
19268 *   This filter is mostly useful for debugging. When using the double curly {{value}} notation
19269 *   the binding is automatically converted to JSON.
19270 *
19271 * @param {*} object Any JavaScript object (including arrays and primitive types) to filter.
19272 * @param {number=}
19272 spacing The number of spaces to use per indentation, defaults to 2.
19273 * @returns {string} JSON string.
19274 *
19275 *
19276 * @example
19277   <example>
19278     <file name="index.html">
19279       <pre id="default-spacing">{{ {'name':'value'} | json }}</pre>
19280       <pre id="custom-spacing">{{ {'name':'value'} | json:4 }}</pre>
19281     </file>
19282     <file name="protractor.js" type="protractor">
19283       it('should jsonify filtered objects', function() {
19284         expect(element(by.id('default-spacing')).getText()).toMatch(/\{\n  "name": ?"value"\n}/);
19285         expect(element(by.id('custom-spacing')).getText()).toMatch(/\{\n    "name": ?"value"\n}/);
19286       });
19287     </file>
19288   </example>
19289 *
19290 */
19291function jsonFilter() {
19292  return function(object, spacing) {
19293    if (isUndefined(spacing)) {
19294        spacing = 2;
19295    }
19296    return toJson(object, spacing);
19297  };
19298}
19299
19300
19301/**
19302 * @ngdoc filter
19303 * @name lowercase
19304 * @kind function
19305 * @description
19306 * Converts string to lowercase.
19307 * @see angular.lowercase
19308 */
19309var lowercaseFilter = valueFn(lowercase);
19310
19311
19312/**
19313 * @ngdoc filter
19314 * @name uppercase
19315 * @kind function
19316 * @description
19317 * Converts string to uppercase.
19318 * @see angular.uppercase
19319 */
19320var uppercaseFilter = valueFn(uppercase);
19321
19322/**
19323 * @ngdoc filter
19324 * @name limitTo
19325 * @kind function
19326 *
19327 * @description
19328 * Creates a new array or string containing only a specified number of elements. The elements
19329 * are taken from either the beginning or the end of the source array, string or number, as specified by
19330 * the value and sign (positive or negative) of `limit`. If a number is used as input, it is
19331 * converted to a string.
19332 *
19333 * @param {Array|string|number} input Source array, string or number to be limited.
19334 * @param {string|number} limit The length of the returned array or string. If the `limit` number
19335 *     is positive, `limit` number of items from the beginning of the source array/string are copied.
19336 *     If the number is negative, `limit` number  of items from the end of the source array/string
19337 *     are copied. The `limit` will be trimmed if it exceeds `array.length`. If `limit` is undefined,
19338 *     the input will be returned unchanged.
19339 * @param {(string|number)=} begin Index at which to begin limitation. As a negative index, `begin`
19340 *     indicates an offset from the end of `input`. Defaults to `0`.
19341 * @returns {Array|string} A new sub-array or substring of length `limit` or less if input array
19342 *     had less than `limit` elements.
19343 *
19344 * @example
19345   <example module="limitToExample">
19346     <file name="index.html">
19347       <script>
19348         angular.module('limitToExample', [])
19349           .controller('ExampleController', ['$scope', function($scope) {
19350             $scope.numbers = [1,2,3,4,5,6,7,8,9];
19351             $scope.letters = "abcdefghi";
19352             $scope.longNumber = 2345432342;
19353             $scope.numLimit = 3;
19354             $scope.letterLimit = 3;
19355             $scope.longNumberLimit = 3;
19356           }]);
19357       </script>
19358       <div ng-controller="ExampleController">
19359         <label>
19360            Limit {{numbers}} to:
19361            <input type="number" step="1" ng-model="numLimit">
19362         </label>
19363         <p>Output numbers: {{ numbers | limitTo:numLimit }}</p>
19364         <label>
19365            Limit {{letters}} to:
19366            <input type="number" step="1" ng-model="letterLimit">
19367         </label>
19368         <p>Output letters: {{ letters | limitTo:letterLimit }}</p>
19369         <label>
19370            Limit {{longNumber}} to:
19371            <input type="number" step="1" ng-model="longNumberLimit">
19372         </label>
19373         <p>Output long number: {{ longNumber | limitTo:longNumberLimit }}</p>
19374       </div>
19375     </file>
19376     <file name="protractor.js" type="protractor">
19377       var numLimitInput = element(by.model('numLimit'));
19378       var letterLimitInput = element(by.model('letterLimit'));
19379       var longNumberLimitInput = element(by.model('longNumberLimit'));
19380       var limitedNumbers = element(by.binding('numbers | limitTo:numLimit'));
19381       var limitedLetters = element(by.binding('letters | limitTo:letterLimit'));
19382       var limitedLongNumber = element(by.binding('longNumber | limitTo:longNumberLimit'));
19383
19384       it('should limit the number array to first three items', function() {
19385         expect(numLimitInput.getAttribute('value')).toBe('3');
19386         expect(letterLimitInput.getAttribute('value')).toBe('3');
19387         expect(longNumberLimitInput.getAttribute('value')).toBe('3');
19388         expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3]');
19389         expect(limitedLetters.getText()).toEqual('Output letters: abc');
19390         expect(limitedLongNumber.getText()).toEqual('Output long number: 234');
19391       });
19392
19393       // There is a bug in safari and protractor that doesn't like the minus key
19394       // it('should update the output when -3 is entered', function() {
19395       //   numLimitInput.clear();
19396       //   numLimitInput.sendKeys('-3');
19397       //   letterLimitInput.clear();
19398       //   letterLimitInput.sendKeys('-3');
19399       //   longNumberLimitInput.clear();
19400       //   longNumberLimitInput.sendKeys('-3');
19401       //   expect(limitedNumbers.getText()).toEqual('Output numbers: [7,8,9]');
19402       //   expect(limitedLetters.getText()).toEqual('Output letters: ghi');
19403       //   expect(limitedLongNumber.getText()).toEqual('Output long number: 342');
19404       // });
19405
19406       it('should not exceed the maximum size of input array', function() {
19407         numLimitInput.clear();
19408         numLimitInput.sendKeys('100');
19409         letterLimitInput.clear();
19410         letterLimitInput.sendKeys('100');
19411         longNumberLimitInput.clear();
19412         longNumberLimitInput.sendKeys('100');
19413         expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3,4,5,6,7,8,9]');
19414         expect(limitedLetters.getText()).toEqual('Output letters: abcdefghi');
19415         expect(limitedLongNumber.getText()).toEqual('Output long number: 2345432342');
19416       });
19417     </file>
19418   </example>
19419*/
19420function limitToFilter() {
19421  return function(input, limit, begin) {
19422    if (Math.abs(Number(limit)) === Infinity) {
19423      limit = Number(limit);
19424    } else {
19425      limit = toInt(limit);
19426    }
19427    if (isNaN(limit)) return input;
19428
19429    if (isNumber(input)) input = input.toString();
19430    if (!isArray(input) && !isString(input)) return input;
19431
19432    begin = (!begin || isNaN(begin)) ? 0 : toInt(begin);
19433    begin = (begin < 0 && begin >= -input.length) ? input.length + begin : begin;
19434
19435    if (limit >= 0) {
19436      return input.slice(begin, begin + limit);
19437    } else {
19438      if (begin === 0) {
19439        return input.slice(limit, input.length);
19440      } else {
19441        return input.slice(Math.max(0, begin + limit), begin);
19442      }
19443    }
19444  };
19445}
19446
19447/**
19448 * @ngdoc filter
19449 * @name orderBy
19450 * @kind function
19451 *
19452 * @description
19453 * Orders a specified `array` by the `expression` predicate. It is ordered alphabetically
19454 * for strings and numerically for numbers. Note: if you notice numbers are not being sorted
19455 * as expected, make sure they are actually being saved as numbers and not strings.
19456 *
19457 * @param {Array} array The array to sort.
19458 * @param {function(*)|string|Array.<(function(*)|string)>=} expression A predicate to be
19459 *    used by the comparator to determine the order of elements.
19460 *
19461 *    Can be one of:
19462 *
19463 *    - `function`: Getter function. The result of this function will be sorted using the
19464 *      `<`, `===`, `>` operator.
19465 *    - `string`: An Angular expression. The result of this expression is used to compare elements
19466 *      (for example `name` to sort by a property called `name` or `name.substr(0, 3)` to sort by
19467 *      3 first characters of a property called `name`). The result of a constant expression
19468 *      is interpreted as a property name to be used in comparisons (for example `"special name"`
19469 *      to sort object by the value of their `special name` property). An expression can be
19470 *      optionally prefixed with `+` or `-` to control ascending or descending sort order
19471 *      (for example, `+name` or `-name`). If no property is provided, (e.g. `'+'`) then the array
19472 *      element itself is used to compare where sorting.
19473 *    - `Array`: An array of function or string predicates. The first predicate in the array
19474 *      is used for sorting, but when two items are equivalent, the next predicate is used.
19475 *
19476 *    If the predicate is missing or empty then it defaults to `'+'`.
19477 *
19478 * @param {boolean=} reverse Reverse the order of the array.
19479 * @returns {Array} Sorted copy of the source array.
19480 *
19481 *
19482 * @example
19483 * The example below demonstrates a simple ngRepeat, where the data is sorted
19484 * by age in descending order (predicate is set to `'-age'`).
19485 * `reverse` is not set, which means it defaults to `false`.
19486   <example module="orderByExample">
19487     <file name="index.html">
19488       <script>
19489         angular.module('orderByExample', [])
19490           .controller('ExampleController', ['$scope', function($scope) {
19491             $scope.friends =
19492                 [{name:'John', phone:'555-1212', age:10},
19493                  {name:'Mary', phone:'555-9876', age:19},
19494                  {name:'Mike', phone:'555-4321', age:21},
19495                  {name:'Adam', phone:'555-5678', age:35},
19496                  {name:'Julie', phone:'555-8765', age:29}];
19497           }]);
19498       </script>
19499       <div ng-controller="ExampleController">
19500         <table class="friend">
19501           <tr>
19502             <th>Name</th>
19503             <th>Phone Number</th>
19504             <th>Age</th>
19505           </tr>
19506           <tr ng-repeat="friend in friends | orderBy:'-age'">
19507             <td>{{friend.name}}</td>
19508             <td>{{friend.phone}}</td>
19509             <td>{{friend.age}}</td>
19510           </tr>
19511         </table>
19512       </div>
19513     </file>
19514   </example>
19515 *
19516 * The predicate and reverse parameters can be controlled dynamically through scope properties,
19517 * as shown in the next example.
19518 * @example
19519   <example module="orderByExample">
19520     <file name="index.html">
19521       <script>
19522         angular.module('orderByExample', [])
19523           .controller('ExampleController', ['$scope', function($scope) {
19524             $scope.friends =
19525                 [{name:'John', phone:'555-1212', age:10},
19526                  {name:'Mary', phone:'555-9876', age:19},
19527                  {name:'Mike', phone:'555-4321', age:21},
19528                  {name:'Adam', phone:'555-5678', age:35},
19529                  {name:'Julie', phone:'555-8765', age:29}];
19530             $scope.predicate = 'age';
19531             $scope.reverse = true;
19532             $scope.order = function(predicate) {
19533               $scope.reverse = ($scope.predicate === predicate) ? !$scope.reverse : false;
19534               $scope.predicate = predicate;
19535             };
19536           }]);
19537       </script>
19538       <style type="text/css">
19539         .sortorder:after {
19540           content: '\25b2';
19541         }
19542         .sortorder.reverse:after {
19543           content: '\25bc';
19544         }
19545       </style>
19546       <div ng-controller="ExampleController">
19547         <pre>Sorting predicate = {{predicate}}; reverse = {{reverse}}</pre>
19548         <hr/>
19549         [ <a href="" ng-click="predicate=''">unsorted</a> ]
19550         <table class="friend">
19551           <tr>
19552             <th>
19553               <a href="" ng-click="order('name')">Name</a>
19554               <span class="sortorder" ng-show="predicate === 'name'" ng-class="{reverse:reverse}"></span>
19555             </th>
19556             <th>
19557               <a href="" ng-click="order('phone')">Phone Number</a>
19558               <span class="sortorder" ng-show="predicate === 'phone'" ng-class="{reverse:reverse}"></span>
19559             </th>
19560             <th>
19561               <a href="" ng-click="order('age')">Age</a>
19562               <span class="sortorder" ng-show="predicate === 'age'" ng-class="{reverse:reverse}"></span>
19563             </th>
19564           </tr>
19565           <tr ng-repeat="friend in friends | orderBy:predicate:reverse">
19566             <td>{{friend.name}}</td>
19567             <td>{{friend.phone}}</td>
19568             <td>{{friend.age}}</td>
19569           </tr>
19570         </table>
19571       </div>
19572     </file>
19573   </example>
19574 *
19575 * It's also possible to call the orderBy filter manually, by injecting `$filter`, retrieving the
19576 * filter routine with `$filter('orderBy')`, and calling the returned filter routine with the
19577 * desired parameters.
19578 *
19579 * Example:
19580 *
19581 * @example
19582  <example module="orderByExample">
19583    <file name="index.html">
19584      <div ng-controller="ExampleController">
19585        <table class="friend">
19586          <tr>
19587            <th><a href="" ng-click="reverse=false;order('name', false)">Name</a>
19588              (<a href="" ng-click="order('-name',false)">^</a>)</th>
19589            <th><a href="" ng-click="reverse=!reverse;order('phone', reverse)">Phone Number</a></th>
19590            <th><a href="" ng-click="reverse=!reverse;order('age',reverse)">Age</a></th>
19591          </tr>
19592          <tr ng-repeat="friend in friends">
19593            <td>{{friend.name}}</td>
19594            <td>{{friend.phone}}</td>
19595            <td>{{friend.age}}</td>
19596          </tr>
19597        </table>
19598      </div>
19599    </file>
19600
19601    <file name="script.js">
19602      angular.module('orderByExample', [])
19603        .controller('ExampleController', ['$scope', '$filter', function($scope, $filter) {
19604          var orderBy = $filter('orderBy');
19605          $scope.friends = [
19606            { name: 'John',    phone: '555-1212',    age: 10 },
19607            { name: 'Mary',    phone: '555-9876',    age: 19 },
19608            { name: 'Mike',    phone: '555-4321',    age: 21 },
19609            { name: 'Adam',    phone: '555-5678',    age: 35 },
19610            { name: 'Julie',   phone: '555-8765',    age: 29 }
19611          ];
19612          $scope.order = function(predicate, reverse) {
19613            $scope.friends = orderBy($scope.friends, predicate, reverse);
19614          };
19615          $scope.order('-age',false);
19616        }]);
19617    </file>
19618</example>
19619 */
19620orderByFilter.$inject = ['$parse'];
19621function orderByFilter($parse) {
19622  return function(array, sortPredicate, reverseOrder) {
19623
19624    if (!(isArrayLike(array))) return array;
19625
19626    if (!isArray(sortPredicate)) { sortPredicate = [sortPredicate]; }
19627    if (sortPredicate.length === 0) { sortPredicate = ['+']; }
19628
19629    var predicates = processPredicates(sortPredicate, reverseOrder);
19630    // Add a predicate at the end that evaluates to the element index. This makes the
19631    // sort stable as it works as a tie-breaker when all the input predicates cannot
19632    // distinguish between two elements.
19633    predicates.push({ get: function() { return {}; }, descending: reverseOrder ? -1 : 1});
19634
19635    // The next three lines are a version of a Swartzian Transform idiom from Perl
19636    // (sometimes called the Decorate-Sort-Undecorate idiom)
19637    // See https://en.wikipedia.org/wiki/Schwartzian_transform
19638    var compareValues = Array.prototype.map.call(array, getComparisonObject);
19639    compareValues.sort(doComparison);
19640    array = compareValues.map(function(item) { return item.value; });
19641
19642    return array;
19643
19644    function getComparisonObject(value, index) {
19645      return {
19646        value: value,
19647        predicateValues: predicates.map(function(predicate) {
19648          return getPredicateValue(predicate.get(value), index);
19649        })
19650      };
19651    }
19652
19653    function doComparison(v1, v2) {
19654      var result = 0;
19655      for (var index=0, length = predicates.length; index < length; ++index) {
19656        result = compare(v1.predicateValues[index], v2.predicateValues[index]) * predicates[index].descending;
19657        if (result) break;
19658      }
19659      return result;
19660    }
19661  };
19662
19663  function processPredicates(sortPredicate, reverseOrder) {
19664    reverseOrder = reverseOrder ? -1 : 1;
19665    return sortPredicate.map(function(predicate) {
19666      var descending = 1, get = identity;
19667
19668      if (isFunction(predicate)) {
19669        get = predicate;
19670      } else if (isString(predicate)) {
19671        if ((predicate.charAt(0) == '+' || predicate.charAt(0) == '-')) {
19672          descending = predicate.charAt(0) == '-' ? -1 : 1;
19673          predicate = predicate.substring(1);
19674        }
19675        if (predicate !== '') {
19676          get = $parse(predicate);
19677          if (get.constant) {
19678            var key = get();
19679            get = function(value) { return value[key]; };
19680          }
19681        }
19682      }
19683      return { get: get, descending: descending * reverseOrder };
19684    });
19685  }
19686
19687  function isPrimitive(value) {
19688    switch (typeof value) {
19689      case 'number': /* falls through */
19690      case 'boolean': /* falls through */
19691      case 'string':
19692        return true;
19693      default:
19694        return false;
19695    }
19696  }
19697
19698  function objectValue(value, index) {
19699    // If `valueOf` is a valid function use that
19700    if (typeof value.valueOf === 'function') {
19701      value = value.valueOf();
19702      if (isPrimitive(value)) return value;
19703    }
19704    // If `toString` is a valid function and not the one from `Object.prototype` use that
19705    if (hasCustomToString(value)) {
19706      value = value.toString();
19707      if (isPrimitive(value)) return value;
19708    }
19709    // We have a basic object so we use the position of the object in the collection
19710    return index;
19711  }
19712
19713  function getPredicateValue(value, index) {
19714    var type = typeof value;
19715    if (value === null) {
19716      type = 'string';
19717      value = 'null';
19718    } else if (type === 'string') {
19719      value = value.toLowerCase();
19720    } else if (type === 'object') {
19721      value = objectValue(value, index);
19722    }
19723    return { value: value, type: type };
19724  }
19725
19726  function compare(v1, v2) {
19727    var result = 0;
19728    if (v1.type === v2.type) {
19729      if (v1.value !== v2.value) {
19730        result = v1.value < v2.value ? -1 : 1;
19731      }
19732    } else {
19733      result = v1.type < v2.type ? -1 : 1;
19734    }
19735    return result;
19736  }
19737}
19738
19739function ngDirective(directive) {
19740  if (isFunction(directive)) {
19741    directive = {
19742      link: directive
19743    };
19744  }
19745  directive.restrict = directive.restrict || 'AC';
19746  return valueFn(directive);
19747}
19748
19749/**
19750 * @ngdoc directive
19751 * @name a
19752 * @restrict E
19753 *
19754 * @description
19755 * Modifies the default behavior of the html A tag so that the default action is prevented when
19756 * the href attribute is empty.
19757 *
19758 * This change permits the easy creation of action links with the `ngClick` directive
19759 * without changing the location or causing page reloads, e.g.:
19760 * `<a href="" ng-click="list.addItem()">Add Item</a>`
19761 */
19762var htmlAnchorDirective = valueFn({
19763  restrict: 'E',
19764  compile: function(element, attr) {
19765    if (!attr.href && !attr.xlinkHref) {
19766      return function(scope, element) {
19767        // If the linked element is not an anchor tag anymore, do nothing
19768        if (element[0].nodeName.toLowerCase() !== 'a') return;
19769
19770        // SVGAElement does not use the href attribute, but rather the 'xlinkHref' attribute.
19771        var href = toString.call(element.prop('href')) === '[object SVGAnimatedString]' ?
19772                   'xlink:href' : 'href';
19773        element.on('click', function(event) {
19774          // if we have no href url, then don't navigate anywhere.
19775          if (!element.attr(href)) {
19776            event.preventDefault();
19777          }
19778        });
19779      };
19780    }
19781  }
19782});
19783
19784/**
19785 * @ngdoc directive
19786 * @name ngHref
19787 * @restrict A
19788 * @priority 99
19789 *
19790 * @description
19791 * Using Angular markup like `{{hash}}` in an href attribute will
19792 * make the link go to the wrong URL if the user clicks it before
19793 * Angular has a chance to replace the `{{hash}}` markup with its
19794 * value. Until Angular replaces the markup the link will be broken
19795 * and will most likely return a 404 error. The `ngHref` directive
19796 * solves this problem.
19797 *
19798 * The wrong way to write it:
19799 * ```html
19800 * <a href="http://www.gravatar.com/avatar/{{hash}}">link1</a>
19801 * ```
19802 *
19803 * The correct way to write it:
19804 * ```html
19805 * <a ng-href="http://www.gravatar.com/avatar/{{hash}}">link1</a>
19806 * ```
19807 *
19808 * @element A
19809 * @param {template} ngHref any string which can contain `{{}}` markup.
19810 *
19811 * @example
19812 * This example shows various combinations of `href`, `ng-href` and `ng-click` attributes
19813 * in links and their different behaviors:
19814    <example>
19815      <file name="index.html">
19816        <input ng-model="value" /><br />
19817        <a id="link-1" href ng-click="value = 1">link 1</a> (link, don't reload)<br />
19818        <a id="link-2" href="" ng-click="value = 2">link 2</a> (link, don't reload)<br />
19819        <a id="link-3" ng-href="/{{'123'}}">link 3</a> (link, reload!)<br />
19820        <a id="link-4" href="" name="xx" ng-click="value = 4">anchor</a> (link, don't reload)<br />
19821        <a id="link-5" name="xxx" ng-click="value = 5">anchor</a> (no link)<br />
19822        <a id="link-6" ng-href="{{value}}">link</a> (link, change location)
19823      </file>
19824      <file name="protractor.js" type="protractor">
19825        it('should execute ng-click but not reload when href without value', function() {
19826          element(by.id('link-1')).click();
19827          expect(element(by.model('value')).getAttribute('value')).toEqual('1');
19828          expect(element(by.id('link-1')).getAttribute('href')).toBe('');
19829        });
19830
19831        it('should execute ng-click but not reload when href empty string', function() {
19832          element(by.id('link-2')).click();
19833          expect(element(by.model('value')).getAttribute('value')).toEqual('2');
19834          expect(element(by.id('link-2')).getAttribute('href')).toBe('');
19835        });
19836
19837        it('should execute ng-click and change url when ng-href specified', function() {
19838          expect(element(by.id('link-3')).getAttribute('href')).toMatch(/\/123$/);
19839
19840          element(by.id('link-3')).click();
19841
19842          // At this point, we navigate away from an Angular page, so we need
19843          // to use browser.driver to get the base webdriver.
19844
19845          browser.wait(function() {
19846            return browser.driver.getCurrentUrl().then(function(url) {
19847              return url.match(/\/123$/);
19848            });
19849          }, 5000, 'page should navigate to /123');
19850        });
19851
19852        it('should execute ng-click but not reload when href empty string and name specified', function() {
19853          element(by.id('link-4')).click();
19854          expect(element(by.model('value')).getAttribute('value')).toEqual('4');
19855          expect(element(by.id('link-4')).getAttribute('href')).toBe('');
19856        });
19857
19858        it('should execute ng-click but not reload when no href but name specified', function() {
19859          element(by.id('link-5')).click();
19860          expect(element(by.model('value')).getAttribute('value')).toEqual('5');
19861          expect(element(by.id('link-5')).getAttribute('href')).toBe(null);
19862        });
19863
19864        it('should only change url when only ng-href', function() {
19865          element(by.model('value')).clear();
19866          element(by.model('value')).sendKeys('6');
19867          expect(element(by.id('link-6')).getAttribute('href')).toMatch(/\/6$/);
19868
19869          element(by.id('link-6')).click();
19870
19871          // At this point, we navigate away from an Angular page, so we need
19872          // to use browser.driver to get the base webdriver.
19873          browser.wait(function() {
19874            return browser.driver.getCurrentUrl().then(function(url) {
19875              return url.match(/\/6$/);
19876            });
19877          }, 5000, 'page should navigate to /6');
19878        });
19879      </file>
19880    </example>
19881 */
19882
19883/**
19884 * @ngdoc directive
19885 * @name ngSrc
19886 * @restrict A
19887 * @priority 99
19888 *
19889 * @description
19890 * Using Angular markup like `{{hash}}` in a `src` attribute doesn't
19891 * work right: The browser will fetch from the URL with the literal
19892 * text `{{hash}}` until Angular replaces the expression inside
19893 * `{{hash}}`. The `ngSrc` directive solves this problem.
19894 *
19895 * The buggy way to write it:
19896 * ```html
19897 * <img src="http://www.gravatar.com/avatar/{{hash}}" alt="Description"/>
19898 * ```
19899 *
19900 * The correct way to write it:
19901 * ```html
19902 * <img ng-src="http://www.gravatar.com/avatar/{{hash}}" alt="Description" />
19903 * ```
19904 *
19905 * @element IMG
19906 * @param {template} ngSrc any string which can contain `{{}}` markup.
19907 */
19908
19909/**
19910 * @ngdoc directive
19911 * @name ngSrcset
19912 * @restrict A
19913 * @priority 99
19914 *
19915 * @description
19916 * Using Angular markup like `{{hash}}` in a `srcset` attribute doesn't
19917 * work right: The browser will fetch from the URL with the literal
19918 * text `{{hash}}` until Angular replaces the expression inside
19919 * `{{hash}}`. The `ngSrcset` directive solves this problem.
19920 *
19921 * The buggy way to write it:
19922 * ```html
19923 * <img srcset="http://www.gravatar.com/avatar/{{hash}} 2x" alt="Description"/>
19924 * ```
19925 *
19926 * The correct way to write it:
19927 * ```html
19928 * <img ng-srcset="http://www.gravatar.com/avatar/{{hash}} 2x" alt="Description" />
19929 * ```
19930 *
19931 * @element IMG
19932 * @param {template} ngSrcset any string which can contain `{{}}` markup.
19933 */
19934
19935/**
19936 * @ngdoc directive
19937 * @name ngDisabled
19938 * @restrict A
19939 * @priority 100
19940 *
19941 * @description
19942 *
19943 * This directive sets the `disabled` attribute on the element if the
19944 * {@link guide/expression expression} inside `ngDisabled` evaluates to truthy.
19945 *
19946 * A special directive is necessary because we cannot use interpolation inside the `disabled`
19947 * attribute.  The following example would make the button enabled on Chrome/Firefox
19948 * but not on older IEs:
19949 *
19950 * ```html
19951 * <!-- See below for an example of ng-disabled being used correctly -->
19952 * <div ng-init="isDisabled = false">
19953 *  <button disabled="{{isDisabled}}">Disabled</button>
19954 * </div>
19955 * ```
19956 *
19957 * This is because the HTML specification does not require browsers to preserve the values of
19958 * boolean attributes such as `disabled` (Their presence means true and their absence means false.)
19959 * If we put an Angular interpolation expression into such an attribute then the
19960 * binding information would be lost when the browser removes the attribute.
19961 *
19962 * @example
19963    <example>
19964      <file name="index.html">
19965        <label>Click me to toggle: <input type="checkbox" ng-model="checked"></label><br/>
19966        <button ng-model="button" ng-disabled="checked">Button</button>
19967      </file>
19968      <file name="protractor.js" type="protractor">
19969        it('should toggle button', function() {
19970          expect(element(by.css('button')).getAttribute('disabled')).toBeFalsy();
19971          element(by.model('checked')).click();
19972          expect(element(by.css('button')).getAttribute('disabled')).toBeTruthy();
19973        });
19974      </file>
19975    </example>
19976 *
19977 * @element INPUT
19978 * @param {expression} ngDisabled If the {@link guide/expression expression} is truthy,
19979 *     then the `disabled` attribute will be set on the element
19980 */
19981
19982
19983/**
19984 * @ngdoc directive
19985 * @name ngChecked
19986 * @restrict A
19987 * @priority 100
19988 *
19989 * @description
19990 * Sets the `checked` attribute on the element, if the expression inside `ngChecked` is truthy.
19991 *
19992 * Note that this directive should not be used together with {@link ngModel `ngModel`},
19993 * as this can lead to unexpected behavior.
19994 *
19995 * ### Why do we need `ngChecked`?
19996 *
19997 * The HTML specification does not require browsers to preserve the values of boolean attributes
19998 * such as checked. (Their presence means true and their absence means false.)
19999 * If we put an Angular interpolation expression into such an attribute then the
20000 * binding information would be lost when the browser removes the attribute.
20001 * The `ngChecked` directive solves this problem for the `checked` attribute.
20002 * This complementary directive is not removed by the browser and so provides
20003 * a permanent reliable place to store the binding information.
20004 * @example
20005    <example>
20006      <file name="index.html">
20007        <label>Check me to check both: <input type="checkbox" ng-model="master"></label><br/>
20008        <input id="checkSlave" type="checkbox" ng-checked="master" aria-label="Slave input">
20009      </file>
20010      <file name="protractor.js" type="protractor">
20011        it('should check both checkBoxes', function() {
20012          expect(element(by.id('checkSlave')).getAttribute('checked')).toBeFalsy();
20013          element(by.model('master')).click();
20014          expect(element(by.id('checkSlave')).getAttribute('checked')).toBeTruthy();
20015        });
20016      </file>
20017    </example>
20018 *
20019 * @element INPUT
20020 * @param {expression} ngChecked If the {@link guide/expression expression} is truthy,
20021 *     then the `checked` attribute will be set on the element
20022 */
20023
20024
20025/**
20026 * @ngdoc directive
20027 * @name ngReadonly
20028 * @restrict A
20029 * @priority 100
20030 *
20031 * @description
20032 * The HTML specification does not require browsers to preserve the values of boolean attributes
20033 * such as readonly. (Their presence means true and their absence means false.)
20034 * If we put an Angular interpolation expression into such an attribute then the
20035 * binding information would be lost when the browser removes the attribute.
20036 * The `ngReadonly` directive solves this problem for the `readonly` attribute.
20037 * This complementary directive is not removed by the browser and so provides
20038 * a permanent reliable place to store the binding information.
20039 * @example
20040    <example>
20041      <file name="index.html">
20042        <label>Check me to make text readonly: <input type="checkbox" ng-model="checked"></label><br/>
20043        <input type="text" ng-readonly="checked" value="I'm Angular" aria-label="Readonly field" />
20044      </file>
20045      <file name="protractor.js" type="protractor">
20046        it('should toggle readonly attr', function() {
20047          expect(element(by.css('[type="text"]')).getAttribute('readonly')).toBeFalsy();
20048          element(by.model('checked')).click();
20049          expect(element(by.css('[type="text"]')).getAttribute('readonly')).toBeTruthy();
20050        });
20051      </file>
20052    </example>
20053 *
20054 * @element INPUT
20055 * @param {expression} ngReadonly If the {@link guide/expression expression} is truthy,
20056 *     then special attribute "readonly" will be set on the element
20057 */
20058
20059
20060/**
20061 * @ngdoc directive
20062 * @name ngSelected
20063 * @restrict A
20064 * @priority 100
20065 *
20066 * @description
20067 * The HTML specification does not require browsers to preserve the values of boolean attributes
20068 * such as selected. (Their presence means true and their absence means false.)
20069 * If we put an Angular interpolation expression into such an attribute then the
20070 * binding information would be lost when the browser removes the attribute.
20071 * The `ngSelected` directive solves this problem for the `selected` attribute.
20072 * This complementary directive is not removed by the browser and so provides
20073 * a permanent reliable place to store the binding information.
20074 *
20075 * @example
20076    <example>
20077      <file name="index.html">
20078        <label>Check me to select: <input type="checkbox" ng-model="selected"></label><br/>
20079        <select aria-label="ngSelected demo">
20080          <option>Hello!</option>
20081          <option id="greet" ng-selected="selected">Greetings!</option>
20082        </select>
20083      </file>
20084      <file name="protractor.js" type="protractor">
20085        it('should select Greetings!', function() {
20086          expect(element(by.id('greet')).getAttribute('selected')).toBeFalsy();
20087          element(by.model('selected')).click();
20088          expect(element(by.id('greet')).getAttribute('selected')).toBeTruthy();
20089        });
20090      </file>
20091    </example>
20092 *
20093 * @element OPTION
20094 * @param {expression} ngSelected If the {@link guide/expression expression} is truthy,
20095 *     then special attribute "selected" will be set on the element
20096 */
20097
20098/**
20099 * @ngdoc directive
20100 * @name ngOpen
20101 * @restrict A
20102 * @priority 100
20103 *
20104 * @description
20105 * The HTML specification does not require browsers to preserve the values of boolean attributes
20106 * such as open. (Their presence means true and their absence means false.)
20107 * If we put an Angular interpolation expression into such an attribute then the
20108 * binding information would be lost when the browser removes the attribute.
20109 * The `ngOpen` directive solves this problem for the `open` attribute.
20110 * This complementary directive is not removed by the browser and so provides
20111 * a permanent reliable place to store the binding information.
20112 * @example
20113     <example>
20114       <file name="index.html">
20115         <label>Check me check multiple: <input type="checkbox" ng-model="open"></label><br/>
20116         <details id="details" ng-open="open">
20117            <summary>Show/Hide me</summary>
20118         </details>
20119       </file>
20120       <file name="protractor.js" type="protractor">
20121         it('should toggle open', function() {
20122           expect(element(by.id('details')).getAttribute('open')).toBeFalsy();
20123           element(by.model('open')).click();
20124           expect(element(by.id('details')).getAttribute('open')).toBeTruthy();
20125         });
20126       </file>
20127     </example>
20128 *
20129 * @element DETAILS
20130 * @param {expression} ngOpen If the {@link guide/expression expression} is truthy,
20131 *     then special attribute "open" will be set on the element
20132 */
20133
20134var ngAttributeAliasDirectives = {};
20135
20136// boolean attrs are evaluated
20137forEach(BOOLEAN_ATTR, function(propName, attrName) {
20138  // binding to multiple is not supported
20139  if (propName == "multiple") return;
20140
20141  function defaultLinkFn(scope, element, attr) {
20142    scope.$watch(attr[normalized], function ngBooleanAttrWatchAction(value) {
20143      attr.$set(attrName, !!value);
20144    });
20145  }
20146
20147  var normalized = directiveNormalize('ng-' + attrName);
20148  var linkFn = defaultLinkFn;
20149
20150  if (propName === 'checked') {
20151    linkFn = function(scope, element, attr) {
20152      // ensuring ngChecked doesn't interfere with ngModel when both are set on the same input
20153      if (attr.ngModel !== attr[normalized]) {
20154        defaultLinkFn(scope, element, attr);
20155      }
20156    };
20157  }
20158
20159  ngAttributeAliasDirectives[normalized] = function() {
20160    return {
20161      restrict: 'A',
20162      priority: 100,
20163      link: linkFn
20164    };
20165  };
20166});
20167
20168// aliased input attrs are evaluated
20169forEach(ALIASED_ATTR, function(htmlAttr, ngAttr) {
20170  ngAttributeAliasDirectives[ngAttr] = function() {
20171    return {
20172      priority: 100,
20173      link: function(scope, element, attr) {
20174        //special case ngPattern when a literal regular expression value
20175        //is used as the expression (this way we don't have to watch anything).
20176        if (ngAttr === "ngPattern" && attr.ngPattern.charAt(0) == "/") {
20177          var match = attr.ngPattern.match(REGEX_STRING_REGEXP);
20178          if (match) {
20179            attr.$set("ngPattern", new RegExp(match[1], match[2]));
20180            return;
20181          }
20182        }
20183
20184        scope.$watch(attr[ngAttr], function ngAttrAliasWatchAction(value) {
20185          attr.$set(ngAttr, value);
20186        });
20187      }
20188    };
20189  };
20190});
20191
20192// ng-src, ng-srcset, ng-href are interpolated
20193forEach(['src', 'srcset', 'href'], function(attrName) {
20194  var normalized = directiveNormalize('ng-' + attrName);
20195  ngAttributeAliasDirectives[normalized] = function() {
20196    return {
20197      priority: 99, // it needs to run after the attributes are interpolated
20198      link: function(scope, element, attr) {
20199        var propName = attrName,
20200            name = attrName;
20201
20202        if (attrName === 'href' &&
20203            toString.call(element.prop('href')) === '[object SVGAnimatedString]') {
20204          name = 'xlinkHref';
20205          attr.$attr[name] = 'xlink:href';
20206          propName = null;
20207        }
20208
20209        attr.$observe(normalized, function(value) {
20210          if (!value) {
20211            if (attrName === 'href') {
20212              attr.$set(name, null);
20213            }
20214            return;
20215          }
20216
20217          attr.$set(name, value);
20218
20219          // on IE, if "ng:src" directive declaration is used and "src" attribute doesn't exist
20220          // then calling element.setAttribute('src', 'foo') doesn't do anything, so we need
20221          // to set the property as well to achieve the desired effect.
20222          // we use attr[attrName] value since $set can sanitize the url.
20223          if (msie && propName) element.prop(propName, attr[name]);
20224        });
20225      }
20226    };
20227  };
20228});
20229
20230/* global -nullFormCtrl, -SUBMITTED_CLASS, addSetValidityMethod: true
20231 */
20232var nullFormCtrl = {
20233  $addControl: noop,
20234  $$renameControl: nullFormRenameControl,
20235  $removeControl: noop,
20236  $setValidity: noop,
20237  $setDirty: noop,
20238  $setPristine: noop,
20239  $setSubmitted: noop
20240},
20241SUBMITTED_CLASS = 'ng-submitted';
20242
20243function nullFormRenameControl(control, name) {
20244  control.$name = name;
20245}
20246
20247/**
20248 * @ngdoc type
20249 * @name form.FormController
20250 *
20251 * @property {boolean} $pristine True if user has not interacted with the form yet.
20252 * @property {boolean} $dirty True if user has already interacted with the form.
20253 * @property {boolean} $valid True if all of the containing forms and controls are valid.
20254 * @property {boolean} $invalid True if at least one containing control or form is invalid.
20255 * @property {boolean} $pending True if at least one containing control or form is pending.
20256 * @property {boolean} $submitted True if user has submitted the form even if its invalid.
20257 *
20258 * @property {Object} $error Is an object hash, containing references to controls or
20259 *  forms with failing validators, where:
20260 *
20261 *  - keys are validation tokens (error names),
20262 *  - values are arrays of controls or forms that have a failing validator for given error name.
20263 *
20264 *  Built-in validation tokens:
20265 *
20266 *  - `email`
20267 *  - `max`
20268 *  - `maxlength`
20269 *  - `min`
20270 *  - `minlength`
20271 *  - `number`
20272 *  - `pattern`
20273 *  - `required`
20274 *  - `url`
20275 *  - `date`
20276 *  - `datetimelocal`
20277 *  - `time`
20278 *  - `week`
20279 *  - `month`
20280 *
20281 * @description
20282 * `FormController` keeps track of all its controls and nested forms as well as the state of them,
20283 * such as being valid/invalid or dirty/pristine.
20284 *
20285 * Each {@link ng.directive:form form} directive creates an instance
20286 * of `FormController`.
20287 *
20288 */
20289//asks for $scope to fool the BC controller module
20290FormController.$inject = ['$element', '$attrs', '$scope', '$animate', '$interpolate'];
20291function FormController(element, attrs, $scope, $animate, $interpolate) {
20292  var form = this,
20293      controls = [];
20294
20295  // init state
20296  form.$error = {};
20297  form.$$success = {};
20298  form.$pending = undefined;
20299  form.$name = $interpolate(attrs.name || attrs.ngForm || '')($scope);
20300  form.$dirty = false;
20301  form.$pristine = true;
20302  form.$valid = true;
20303  form.$invalid = false;
20304  form.$submitted = false;
20305  form.$$parentForm = nullFormCtrl;
20306
20307  /**
20308   * @ngdoc method
20309   * @name form.FormController#$rollbackViewValue
20310   *
20311   * @description
20312   * Rollback all form controls pending updates to the `$modelValue`.
20313   *
20314   * Updates may be pending by a debounced event or because the input is waiting for a some future
20315   * event defined in `ng-model-options`. This method is typically needed by the reset button of
20316   * a form that uses `ng-model-options` to pend updates.
20317   */
20318  form.$rollbackViewValue = function() {
20319    forEach(controls, function(control) {
20320      control.$rollbackViewValue();
20321    });
20322  };
20323
20324  /**
20325   * @ngdoc method
20326   * @name form.FormController#$commitViewValue
20327   *
20328   * @description
20329   * Commit all form controls pending updates to the `$modelValue`.
20330   *
20331   * Updates may be pending by a debounced event or because the input is waiting for a some future
20332   * event defined in `ng-model-options`. This method is rarely needed as `NgModelController`
20333   * usually handles calling this in response to input events.
20334   */
20335  form.$commitViewValue = function() {
20336    forEach(controls, function(control) {
20337      control.$commitViewValue();
20338    });
20339  };
20340
20341  /**
20342   * @ngdoc method
20343   * @name form.FormController#$addControl
20344   * @param {object} control control object, either a {@link form.FormController} or an
20345   * {@link ngModel.NgModelController}
20346   *
20347   * @description
20348   * Register a control with the form. Input elements using ngModelController do this automatically
20349   * when they are linked.
20350   *
20351   * Note that the current state of the control will not be reflected on the new parent form. This
20352   * is not an issue with normal use, as freshly compiled and linked controls are in a `$pristine`
20353   * state.
20354   *
20355   * However, if the method is used programmatically, for example by adding dynamically created controls,
20356   * or controls that have been previously removed without destroying their corresponding DOM element,
20357   * it's the developers responsiblity to make sure the current state propagates to the parent form.
20358   *
20359   * For example, if an input control is added that is already `$dirty` and has `$error` properties,
20360   * calling `$setDirty()` and `$validate()` afterwards will propagate the state to the parent form.
20361   */
20362  form.$addControl = function(control) {
20363    // Breaking change - before, inputs whose name was "hasOwnProperty" were quietly ignored
20364    // and not added to the scope.  Now we throw an error.
20365    assertNotHasOwnProperty(control.$name, 'input');
20366    controls.push(control);
20367
20368    if (control.$name) {
20369      form[control.$name] = control;
20370    }
20371
20372    control.$$parentForm = form;
20373  };
20374
20375  // Private API: rename a form control
20376  form.$$renameControl = function(control, newName) {
20377    var oldName = control.$name;
20378
20379    if (form[oldName] === control) {
20380      delete form[oldName];
20381    }
20382    form[newName] = control;
20383    control.$name = newName;
20384  };
20385
20386  /**
20387   * @ngdoc method
20388   * @name form.FormController#$removeControl
20389   * @param {object} control control object, either a {@link form.FormController} or an
20390   * {@link ngModel.NgModelController}
20391   *
20392   * @description
20393   * Deregister a control from the form.
20394   *
20395   * Input elements using ngModelController do this automatically when they are destroyed.
20396   *
20397   * Note that only the removed control's validation state (`$errors`etc.) will be removed from the
20398   * form. `$dirty`, `$submitted` states will not be changed, because the expected behavior can be
20399   * different from case to case. For example, removing the only `$dirty` control from a form may or
20400   * may not mean that the form is still `$dirty`.
20401   */
20402  form.$removeControl = function(control) {
20403    if (control.$name && form[control.$name] === control) {
20404      delete form[control.$name];
20405    }
20406    forEach(form.$pending, function(value, name) {
20407      form.$setValidity(name, null, control);
20408    });
20409    forEach(form.$error, function(value, name) {
20410      form.$setValidity(name, null, control);
20411    });
20412    forEach(form.$$success, function(value, name) {
20413      form.$setValidity(name, null, control);
20414    });
20415
20416    arrayRemove(controls, control);
20417    control.$$parentForm = nullFormCtrl;
20418  };
20419
20420
20421  /**
20422   * @ngdoc method
20423   * @name form.FormController#$setValidity
20424   *
20425   * @description
20426   * Sets the validity of a form control.
20427   *
20428   * This method will also propagate to parent forms.
20429   */
20430  addSetValidityMethod({
20431    ctrl: this,
20432    $element: element,
20433    set: function(object, property, controller) {
20434      var list = object[property];
20435      if (!list) {
20436        object[property] = [controller];
20437      } else {
20438        var index = list.indexOf(controller);
20439        if (index === -1) {
20440          list.push(controller);
20441        }
20442      }
20443    },
20444    unset: function(object, property, controller) {
20445      var list = object[property];
20446      if (!list) {
20447        return;
20448      }
20449      arrayRemove(list, controller);
20450      if (list.length === 0) {
20451        delete object[property];
20452      }
20453    },
20454    $animate: $animate
20455  });
20456
20457  /**
20458   * @ngdoc method
20459   * @name form.FormController#$setDirty
20460   *
20461   * @description
20462   * Sets the form to a dirty state.
20463   *
20464   * This method can be called to add the 'ng-dirty' class and set the form to a dirty
20465   * state (ng-dirty class). This method will also propagate to parent forms.
20466   */
20467  form.$setDirty = function() {
20468    $animate.removeClass(element, PRISTINE_CLASS);
20469    $animate.addClass(element, DIRTY_CLASS);
20470    form.$dirty = true;
20471    form.$pristine = false;
20472    form.$$parentForm.$setDirty();
20473  };
20474
20475  /**
20476   * @ngdoc method
20477   * @name form.FormController#$setPristine
20478   *
20479   * @description
20480   * Sets the form to its pristine state.
20481   *
20482   * This method can be called to remove the 'ng-dirty' class and set the form to its pristine
20483   * state (ng-pristine class). This method will also propagate to all the controls contained
20484   * in this form.
20485   *
20486   * Setting a form back to a pristine state is often useful when we want to 'reuse' a form after
20487   * saving or resetting it.
20488   */
20489  form.$setPristine = function() {
20490    $animate.setClass(element, PRISTINE_CLASS, DIRTY_CLASS + ' ' + SUBMITTED_CLASS);
20491    form.$dirty = false;
20492    form.$pristine = true;
20493    form.$submitted = false;
20494    forEach(controls, function(control) {
20495      control.$setPristine();
20496    });
20497  };
20498
20499  /**
20500   * @ngdoc method
20501   * @name form.FormController#$setUntouched
20502   *
20503   * @description
20504   * Sets the form to its untouched state.
20505   *
20506   * This method can be called to remove the 'ng-touched' class and set the form controls to their
20507   * untouched state (ng-untouched class).
20508   *
20509   * Setting a form controls back to their untouched state is often useful when setting the form
20510   * back to its pristine state.
20511   */
20512  form.$setUntouched = function() {
20513    forEach(controls, function(control) {
20514      control.$setUntouched();
20515    });
20516  };
20517
20518  /**
20519   * @ngdoc method
20520   * @name form.FormController#$setSubmitted
20521   *
20522   * @description
20523   * Sets the form to its submitted state.
20524   */
20525  form.$setSubmitted = function() {
20526    $animate.addClass(element, SUBMITTED_CLASS);
20527    form.$submitted = true;
20528    form.$$parentForm.$setSubmitted();
20529  };
20530}
20531
20532/**
20533 * @ngdoc directive
20534 * @name ngForm
20535 * @restrict EAC
20536 *
20537 * @description
20538 * Nestable alias of {@link ng.directive:form `form`} directive. HTML
20539 * does not allow nesting of form elements. It is useful to nest forms, for example if the validity of a
20540 * sub-group of controls needs to be determined.
20541 *
20542 * Note: the purpose of `ngForm` is to group controls,
20543 * but not to be a replacement for the `<form>` tag with all of its capabilities
20544 * (e.g. posting to the server, ...).
20545 *
20546 * @param {string=} ngForm|name Name of the form. If specified, the form controller will be published into
20547 *                       related scope, under this name.
20548 *
20549 */
20550
20551 /**
20552 * @ngdoc directive
20553 * @name form
20554 * @restrict E
20555 *
20556 * @description
20557 * Directive that instantiates
20558 * {@link form.FormController FormController}.
20559 *
20560 * If the `name` attribute is specified, the form controller is published onto the current scope under
20561 * this name.
20562 *
20563 * # Alias: {@link ng.directive:ngForm `ngForm`}
20564 *
20565 * In Angular, forms can be nested. This means that the outer form is valid when all of the child
20566 * forms are valid as well. However, browsers do not allow nesting of `<form>` elements, so
20567 * Angular provides the {@link ng.directive:ngForm `ngForm`} directive which behaves identically to
20568 * `<form>` but can be nested.  This allows you to have nested forms, which is very useful when
20569 * using Angular validation directives in forms that are dynamically generated using the
20570 * {@link ng.directive:ngRepeat `ngRepeat`} directive. Since you cannot dynamically generate the `name`
20571 * attribute of input elements using interpolation, you have to wrap each set of repeated inputs in an
20572 * `ngForm` directive and nest these in an outer `form` element.
20573 *
20574 *
20575 * # CSS classes
20576 *  - `ng-valid` is set if the form is valid.
20577 *  - `ng-invalid` is set if the form is invalid.
20578 *  - `ng-pending` is set if the form is pending.
20579 *  - `ng-pristine` is set if the form is pristine.
20580 *  - `ng-dirty` is set if the form is dirty.
20581 *  - `ng-submitted` is set if the form was submitted.
20582 *
20583 * Keep in mind that ngAnimate can detect each of these classes when added and removed.
20584 *
20585 *
20586 * # Submitting a form and preventing the default action
20587 *
20588 * Since the role of forms in client-side Angular applications is different than in classical
20589 * roundtrip apps, it is desirable for the browser not to translate the form submission into a full
20590 * page reload that sends the data to the server. Instead some javascript logic should be triggered
20591 * to handle the form submission in an application-specific way.
20592 *
20593 * For this reason, Angular prevents the default action (form submission to the server) unless the
20594 * `<form>` element has an `action` attribute specified.
20595 *
20596 * You can use one of the following two ways to specify what javascript method should be called when
20597 * a form is submitted:
20598 *
20599 * - {@link ng.directive:ngSubmit ngSubmit} directive on the form element
20600 * - {@link ng.directive:ngClick ngClick} directive on the first
20601  *  button or input field of type submit (input[type=submit])
20602 *
20603 * To prevent double execution of the handler, use only one of the {@link ng.directive:ngSubmit ngSubmit}
20604 * or {@link ng.directive:ngClick ngClick} directives.
20605 * This is because of the following form submission rules in the HTML specification:
20606 *
20607 * - If a form has only one input field then hitting enter in this field triggers form submit
20608 * (`ngSubmit`)
20609 * - if a form has 2+ input fields and no buttons or input[type=submit] then hitting enter
20610 * doesn't trigger submit
20611 * - if a form has one or more input fields and one or more buttons or input[type=submit] then
20612 * hitting enter in any of the input fields will trigger the click handler on the *first* button or
20613 * input[type=submit] (`ngClick`) *and* a submit handler on the enclosing form (`ngSubmit`)
20614 *
20615 * Any pending `ngModelOptions` changes will take place immediately when an enclosing form is
20616 * submitted. Note that `ngClick` events will occur before the model is updated. Use `ngSubmit`
20617 * to have access to the updated model.
20618 *
20619 * ## Animation Hooks
20620 *
20621 * Animations in ngForm are triggered when any of the associated CSS classes are added and removed.
20622 * These classes are: `.ng-pristine`, `.ng-dirty`, `.ng-invalid` and `.ng-valid` as well as any
20623 * other validations that are performed within the form. Animations in ngForm are similar to how
20624 * they work in ngClass and animations can be hooked into using CSS transitions, keyframes as well
20625 * as JS animations.
20626 *
20627 * The following example shows a simple way to utilize CSS transitions to style a form element
20628 * that has been rendered as invalid after it has been validated:
20629 *
20630 * <pre>
20631 * //be sure to include ngAnimate as a module to hook into more
20632 * //advanced animations
20633 * .my-form {
20634 *   transition:0.5s linear all;
20635 *   background: white;
20636 * }
20637 * .my-form.ng-invalid {
20638 *   background: red;
20639 *   color:white;
20640 * }
20641 * </pre>
20642 *
20643 * @example
20644    <example deps="angular-animate.js" animations="true" fixBase="true" module="formExample">
20645      <file name="index.html">
20646       <script>
20647         angular.module('formExample', [])
20648           .controller('FormController', ['$scope', function($scope) {
20649             $scope.userType = 'guest';
20650           }]);
20651       </script>
20652       <style>
20653        .my-form {
20654          transition:all linear 0.5s;
20655          background: transparent;
20656        }
20657        .my-form.ng-invalid {
20658          background: red;
20659        }
20660       </style>
20661       <form name="myForm" ng-controller="FormController" class="my-form">
20662         userType: <input name="input" ng-model="userType" required>
20663         <span class="error" ng-show="myForm.input.$error.required">Required!</span><br>
20664         <code>userType = {{userType}}</code><br>
20665         <code>myForm.input.$valid = {{myForm.input.$valid}}</code><br>
20666         <code>myForm.input.$error = {{myForm.input.$error}}</code><br>
20667         <code>myForm.$valid = {{myForm.$valid}}</code><br>
20668         <code>myForm.$error.required = {{!!myForm.$error.required}}</code><br>
20669        </form>
20670      </file>
20671      <file name="protractor.js" type="protractor">
20672        it('should initialize to model', function() {
20673          var userType = element(by.binding('userType'));
20674          var valid = element(by.binding('myForm.input.$valid'));
20675
20676          expect(userType.getText()).toContain('guest');
20677          expect(valid.getText()).toContain('true');
20678        });
20679
20680        it('should be invalid if empty', function() {
20681          var userType = element(by.binding('userType'));
20682          var valid = element(by.binding('myForm.input.$valid'));
20683          var userInput = element(by.model('userType'));
20684
20685          userInput.clear();
20686          userInput.sendKeys('');
20687
20688          expect(userType.getText()).toEqual('userType =');
20689          expect(valid.getText()).toContain('false');
20690        });
20691      </file>
20692    </example>
20693 *
20694 * @param {string=} name Name of the form. If specified, the form controller will be published into
20695 *                       related scope, under this name.
20696 */
20697var formDirectiveFactory = function(isNgForm) {
20698  return ['$timeout', '$parse', function($timeout, $parse) {
20699    var formDirective = {
20700      name: 'form',
20701      restrict: isNgForm ? 'EAC' : 'E',
20702      require: ['form', '^^?form'], //first is the form's own ctrl, second is an optional parent form
20703      controller: FormController,
20704      compile: function ngFormCompile(formElement, attr) {
20705        // Setup initial state of the control
20706        formElement.addClass(PRISTINE_CLASS).addClass(VALID_CLASS);
20707
20708        var nameAttr = attr.name ? 'name' : (isNgForm && attr.ngForm ? 'ngForm' : false);
20709
20710        return {
20711          pre: function ngFormPreLink(scope, formElement, attr, ctrls) {
20712            var controller = ctrls[0];
20713
20714            // if `action` attr is not present on the form, prevent the default action (submission)
20715            if (!('action' in attr)) {
20716              // we can't use jq events because if a form is destroyed during submission the default
20717              // action is not prevented. see #1238
20718              //
20719              // IE 9 is not affected because it doesn't fire a submit event and try to do a full
20720              // page reload if the form was destroyed by submission of the form via a click handler
20721              // on a button in the form. Looks like an IE9 specific bug.
20722              var handleFormSubmission = function(event) {
20723                scope.$apply(function() {
20724                  controller.$commitViewValue();
20725                  controller.$setSubmitted();
20726                });
20727
20728                event.preventDefault();
20729              };
20730
20731              addEventListenerFn(formElement[0], 'submit', handleFormSubmission);
20732
20733              // unregister the preventDefault listener so that we don't not leak memory but in a
20734              // way that will achieve the prevention of the default action.
20735              formElement.on('$destroy', function() {
20736                $timeout(function() {
20737                  removeEventListenerFn(formElement[0], 'submit', handleFormSubmission);
20738                }, 0, false);
20739              });
20740            }
20741
20742            var parentFormCtrl = ctrls[1] || controller.$$parentForm;
20743            parentFormCtrl.$addControl(controller);
20744
20745            var setter = nameAttr ? getSetter(controller.$name) : noop;
20746
20747            if (nameAttr) {
20748              setter(scope, controller);
20749              attr.$observe(nameAttr, function(newValue) {
20750                if (controller.$name === newValue) return;
20751                setter(scope, undefined);
20752                controller.$$parentForm.$$renameControl(controller, newValue);
20753                setter = getSetter(controller.$name);
20754                setter(scope, controller);
20755              });
20756            }
20757            formElement.on('$destroy', function() {
20758              controller.$$parentForm.$removeControl(controller);
20759              setter(scope, undefined);
20760              extend(controller, nullFormCtrl); //stop propagating child destruction handlers upwards
20761            });
20762          }
20763        };
20764      }
20765    };
20766
20767    return formDirective;
20768
20769    function getSetter(expression) {
20770      if (expression === '') {
20771        //create an assignable expression, so forms with an empty name can be renamed later
20772        return $parse('this[""]').assign;
20773      }
20774      return $parse(expression).assign || noop;
20775    }
20776  }];
20777};
20778
20779var formDirective = formDirectiveFactory();
20780var ngFormDirective = formDirectiveFactory(true);
20781
20782/* global VALID_CLASS: false,
20783  INVALID_CLASS: false,
20784  PRISTINE_CLASS: false,
20785  DIRTY_CLASS: false,
20786  UNTOUCHED_CLASS: false,
20787  TOUCHED_CLASS: false,
20788  ngModelMinErr: false,
20789*/
20790
20791// Regex code is obtained from SO: https://stackoverflow.com/questions/3143070/javascript-regex-iso-datetime#answer-3143231
20792var 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)/;
20793var URL_REGEXP = /^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/;
20794var EMAIL_REGEXP = /^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i;
20795var NUMBER_REGEXP = /^\s*(\-|\+)?(\d+|(\d*(\.\d*)))([eE][+-]?\d+)?\s*$/;
20796var DATE_REGEXP = /^(\d{4})-(\d{2})-(\d{2})$/;
20797var DATETIMELOCAL_REGEXP = /^(\d{4})-(\d\d)-(\d\d)T(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
20798var WEEK_REGEXP = /^(\d{4})-W(\d\d)$/;
20799var MONTH_REGEXP = /^(\d{4})-(\d\d)$/;
20800var TIME_REGEXP = /^(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
20801
20802var inputType = {
20803
20804  /**
20805   * @ngdoc input
20806   * @name input[text]
20807   *
20808   * @description
20809   * Standard HTML text input with angular data binding, inherited by most of the `input` elements.
20810   *
20811   *
20812   * @param {string} ngModel Assignable angular expression to data-bind to.
20813   * @param {string=} name Property name of the form under which the control is published.
20814   * @param {string=} required Adds `required` validation error key if the value is not entered.
20815   * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
20816   *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
20817   *    `required` when you want to data-bind to the `required` attribute.
20818   * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
20819   *    minlength.
20820   * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
20821   *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
20822   *    any length.
20823   * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
20824   *    that contains the regular expression body that will be converted to a regular expression
20825   *    as in the ngPattern directive.
20826   * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
20827   *    a RegExp found by evaluating the Angular expression given in the attribute value.
20828   *    If the expression evaluates to a RegExp object, then this is used directly.
20829   *    If the expression evaluates to a string, then it will be converted to a RegExp
20830   *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
20831   *    `new RegExp('^abc$')`.<br />
20832   *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
20833   *    start at the index of the last search's match, thus not taking the whole input value into
20834   *    account.
20835   * @param {string=} ngChange Angular expression to be executed when input changes due to user
20836   *    interaction with the input element.
20837   * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
20838   *    This parameter is ignored for input[type=password] controls, which will never trim the
20839   *    input.
20840   *
20841   * @example
20842      <example name="text-input-directive" module="textInputExample">
20843        <file name="index.html">
20844         <script>
20845           angular.module('textInputExample', [])
20846             .controller('ExampleController', ['$scope', function($scope) {
20847               $scope.example = {
20848                 text: 'guest',
20849                 word: /^\s*\w*\s*$/
20850               };
20851             }]);
20852         </script>
20853         <form name="myForm" ng-controller="ExampleController">
20854           <label>Single word:
20855             <input type="text" name="input" ng-model="example.text"
20856                    ng-pattern="example.word" required ng-trim="false">
20857           </label>
20858           <div role="alert">
20859             <span class="error" ng-show="myForm.input.$error.required">
20860               Required!</span>
20861             <span class="error" ng-show="myForm.input.$error.pattern">
20862               Single word only!</span>
20863           </div>
20864           <tt>text = {{example.text}}</tt><br/>
20865           <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
20866           <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
20867           <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
20868           <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
20869          </form>
20870        </file>
20871        <file name="protractor.js" type="protractor">
20872          var text = element(by.binding('example.text'));
20873          var valid = element(by.binding('myForm.input.$valid'));
20874          var input = element(by.model('example.text'));
20875
20876          it('should initialize to model', function() {
20877            expect(text.getText()).toContain('guest');
20878            expect(valid.getText()).toContain('true');
20879          });
20880
20881          it('should be invalid if empty', function() {
20882            input.clear();
20883            input.sendKeys('');
20884
20885            expect(text.getText()).toEqual('text =');
20886            expect(valid.getText()).toContain('false');
20887          });
20888
20889          it('should be invalid if multi word', function() {
20890            input.clear();
20891            input.sendKeys('hello world');
20892
20893            expect(valid.getText()).toContain('false');
20894          });
20895        </file>
20896      </example>
20897   */
20898  'text': textInputType,
20899
20900    /**
20901     * @ngdoc input
20902     * @name input[date]
20903     *
20904     * @description
20905     * Input with date validation and transformation. In browsers that do not yet support
20906     * the HTML5 date input, a text element will be used. In that case, text must be entered in a valid ISO-8601
20907     * date format (yyyy-MM-dd), for example: `2009-01-06`. Since many
20908     * modern browsers do not yet support this input type, it is important to provide cues to users on the
20909     * expected input format via a placeholder or label.
20910     *
20911     * The model must always be a Date object, otherwise Angular will throw an error.
20912     * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
20913     *
20914     * The timezone to be used to read/write the `Date` instance in the model can be defined using
20915     * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
20916     *
20917     * @param {string} ngModel Assignable angular expression to data-bind to.
20918     * @param {string=} name Property name of the form under which the control is published.
20919     * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be a
20920     *   valid ISO date string (yyyy-MM-dd). You can also use interpolation inside this attribute
20921     *   (e.g. `min="{{minDate | date:'yyyy-MM-dd'}}"`). Note that `min` will also add native HTML5
20922     *   constraint validation.
20923     * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must be
20924     *   a valid ISO date string (yyyy-MM-dd). You can also use interpolation inside this attribute
20925     *   (e.g. `max="{{maxDate | date:'yyyy-MM-dd'}}"`). Note that `max` will also add native HTML5
20926     *   constraint validation.
20927     * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO date string
20928     *   the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
20929     * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO date string
20930     *   the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
20931     * @param {string=} required Sets `required` validation error key if the value is not entered.
20932     * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
20933     *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
20934     *    `required` when you want to data-bind to the `required` attribute.
20935     * @param {string=} ngChange Angular expression to be executed when input changes due to user
20936     *    interaction with the input element.
20937     *
20938     * @example
20939     <example name="date-input-directive" module="dateInputExample">
20940     <file name="index.html">
20941       <script>
20942          angular.module('dateInputExample', [])
20943            .controller('DateController', ['$scope', function($scope) {
20944              $scope.example = {
20945                value: new Date(2013, 9, 22)
20946              };
20947            }]);
20948       </script>
20949       <form name="myForm" ng-controller="DateController as dateCtrl">
20950          <label for="exampleInput">Pick a date in 2013:</label>
20951          <input type="date" id="exampleInput" name="input" ng-model="example.value"
20952              placeholder="yyyy-MM-dd" min="2013-01-01" max="2013-12-31" required />
20953          <div role="alert">
20954            <span class="error" ng-show="myForm.input.$error.required">
20955                Required!</span>
20956            <span class="error" ng-show="myForm.input.$error.date">
20957                Not a valid date!</span>
20958           </div>
20959           <tt>value = {{example.value | date: "yyyy-MM-dd"}}</tt><br/>
20960           <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
20961           <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
20962           <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
20963           <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
20964       </form>
20965     </file>
20966     <file name="protractor.js" type="protractor">
20967        var value = element(by.binding('example.value | date: "yyyy-MM-dd"'));
20968        var valid = element(by.binding('myForm.input.$valid'));
20969        var input = element(by.model('example.value'));
20970
20971        // currently protractor/webdriver does not support
20972        // sending keys to all known HTML5 input controls
20973        // for various browsers (see https://github.com/angular/protractor/issues/562).
20974        function setInput(val) {
20975          // set the value of the element and force validation.
20976          var scr = "var ipt = document.getElementById('exampleInput'); " +
20977          "ipt.value = '" + val + "';" +
20978          "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
20979          browser.executeScript(scr);
20980        }
20981
20982        it('should initialize to model', function() {
20983          expect(value.getText()).toContain('2013-10-22');
20984          expect(valid.getText()).toContain('myForm.input.$valid = true');
20985        });
20986
20987        it('should be invalid if empty', function() {
20988          setInput('');
20989          expect(value.getText()).toEqual('value =');
20990          expect(valid.getText()).toContain('myForm.input.$valid = false');
20991        });
20992
20993        it('should be invalid if over max', function() {
20994          setInput('2015-01-01');
20995          expect(value.getText()).toContain('');
20996          expect(valid.getText()).toContain('myForm.input.$valid = false');
20997        });
20998     </file>
20999     </example>
21000     */
21001  'date': createDateInputType('date', DATE_REGEXP,
21002         createDateParser(DATE_REGEXP, ['yyyy', 'MM', 'dd']),
21003         'yyyy-MM-dd'),
21004
21005   /**
21006    * @ngdoc input
21007    * @name input[datetime-local]
21008    *
21009    * @description
21010    * Input with datetime validation and transformation. In browsers that do not yet support
21011    * the HTML5 date input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
21012    * local datetime format (yyyy-MM-ddTHH:mm:ss), for example: `2010-12-28T14:57:00`.
21013    *
21014    * The model must always be a Date object, otherwise Angular will throw an error.
21015    * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
21016    *
21017    * The timezone to be used to read/write the `Date` instance in the model can be defined using
21018    * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
21019    *
21020    * @param {string} ngModel Assignable angular expression to data-bind to.
21021    * @param {string=} name Property name of the form under which the control is published.
21022    * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
21023    *   This must be a valid ISO datetime format (yyyy-MM-ddTHH:mm:ss). You can also use interpolation
21024    *   inside this attribute (e.g. `min="{{minDatetimeLocal | date:'yyyy-MM-ddTHH:mm:ss'}}"`).
21025    *   Note that `min` will also add native HTML5 constraint validation.
21026    * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
21027    *   This must be a valid ISO datetime format (yyyy-MM-ddTHH:mm:ss). You can also use interpolation
21028    *   inside this attribute (e.g. `max="{{maxDatetimeLocal | date:'yyyy-MM-ddTHH:mm:ss'}}"`).
21029    *   Note that `max` will also add native HTML5 constraint validation.
21030    * @param {(date|string)=} ngMin Sets the `min` validation error key to the Date / ISO datetime string
21031    *   the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
21032    * @param {(date|string)=} ngMax Sets the `max` validation error key to the Date / ISO datetime string
21033    *   the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
21034    * @param {string=} required Sets `required` validation error key if the value is not entered.
21035    * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21036    *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21037    *    `required` when you want to data-bind to the `required` attribute.
21038    * @param {string=} ngChange Angular expression to be executed when input changes due to user
21039    *    interaction with the input element.
21040    *
21041    * @example
21042    <example name="datetimelocal-input-directive" module="dateExample">
21043    <file name="index.html">
21044      <script>
21045        angular.module('dateExample', [])
21046          .controller('DateController', ['$scope', function($scope) {
21047            $scope.example = {
21048              value: new Date(2010, 11, 28, 14, 57)
21049            };
21050          }]);
21051      </script>
21052      <form name="myForm" ng-controller="DateController as dateCtrl">
21053        <label for="exampleInput">Pick a date between in 2013:</label>
21054        <input type="datetime-local" id="exampleInput" name="input" ng-model="example.value"
21055            placeholder="yyyy-MM-ddTHH:mm:ss" min="2001-01-01T00:00:00" max="2013-12-31T00:00:00" required />
21056        <div role="alert">
21057          <span class="error" ng-show="myForm.input.$error.required">
21058              Required!</span>
21059          <span class="error" ng-show="myForm.input.$error.datetimelocal">
21060              Not a valid date!</span>
21061        </div>
21062        <tt>value = {{example.value | date: "yyyy-MM-ddTHH:mm:ss"}}</tt><br/>
21063        <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21064        <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21065        <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21066        <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21067      </form>
21068    </file>
21069    <file name="protractor.js" type="protractor">
21070      var value = element(by.binding('example.value | date: "yyyy-MM-ddTHH:mm:ss"'));
21071      var valid = element(by.binding('myForm.input.$valid'));
21072      var input = element(by.model('example.value'));
21073
21074      // currently protractor/webdriver does not support
21075      // sending keys to all known HTML5 input controls
21076      // for various browsers (https://github.com/angular/protractor/issues/562).
21077      function setInput(val) {
21078        // set the value of the element and force validation.
21079        var scr = "var ipt = document.getElementById('exampleInput'); " +
21080        "ipt.value = '" + val + "';" +
21081        "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
21082        browser.executeScript(scr);
21083      }
21084
21085      it('should initialize to model', function() {
21086        expect(value.getText()).toContain('2010-12-28T14:57:00');
21087        expect(valid.getText()).toContain('myForm.input.$valid = true');
21088      });
21089
21090      it('should be invalid if empty', function() {
21091        setInput('');
21092        expect(value.getText()).toEqual('value =');
21093        expect(valid.getText()).toContain('myForm.input.$valid = false');
21094      });
21095
21096      it('should be invalid if over max', function() {
21097        setInput('2015-01-01T23:59:00');
21098        expect(value.getText()).toContain('');
21099        expect(valid.getText()).toContain('myForm.input.$valid = false');
21100      });
21101    </file>
21102    </example>
21103    */
21104  'datetime-local': createDateInputType('datetimelocal', DATETIMELOCAL_REGEXP,
21105      createDateParser(DATETIMELOCAL_REGEXP, ['yyyy', 'MM', 'dd', 'HH', 'mm', 'ss', 'sss']),
21106      'yyyy-MM-ddTHH:mm:ss.sss'),
21107
21108  /**
21109   * @ngdoc input
21110   * @name input[time]
21111   *
21112   * @description
21113   * Input with time validation and transformation. In browsers that do not yet support
21114   * the HTML5 date input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
21115   * local time format (HH:mm:ss), for example: `14:57:00`. Model must be a Date object. This binding will always output a
21116   * Date object to the model of January 1, 1970, or local date `new Date(1970, 0, 1, HH, mm, ss)`.
21117   *
21118   * The model must always be a Date object, otherwise Angular will throw an error.
21119   * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
21120   *
21121   * The timezone to be used to read/write the `Date` instance in the model can be defined using
21122   * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
21123   *
21124   * @param {string} ngModel Assignable angular expression to data-bind to.
21125   * @param {string=} name Property name of the form under which the control is published.
21126   * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
21127   *   This must be a valid ISO time format (HH:mm:ss). You can also use interpolation inside this
21128   *   attribute (e.g. `min="{{minTime | date:'HH:mm:ss'}}"`). Note that `min` will also add
21129   *   native HTML5 constraint validation.
21130   * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
21131   *   This must be a valid ISO time format (HH:mm:ss). You can also use interpolation inside this
21132   *   attribute (e.g. `max="{{maxTime | date:'HH:mm:ss'}}"`). Note that `max` will also add
21133   *   native HTML5 constraint validation.
21134   * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO time string the
21135   *   `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
21136   * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO time string the
21137   *   `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
21138   * @param {string=} required Sets `required` validation error key if the value is not entered.
21139   * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21140   *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21141   *    `required` when you want to data-bind to the `required` attribute.
21142   * @param {string=} ngChange Angular expression to be executed when input changes due to user
21143   *    interaction with the input element.
21144   *
21145   * @example
21146   <example name="time-input-directive" module="timeExample">
21147   <file name="index.html">
21148     <script>
21149      angular.module('timeExample', [])
21150        .controller('DateController', ['$scope', function($scope) {
21151          $scope.example = {
21152            value: new Date(1970, 0, 1, 14, 57, 0)
21153          };
21154        }]);
21155     </script>
21156     <form name="myForm" ng-controller="DateController as dateCtrl">
21157        <label for="exampleInput">Pick a between 8am and 5pm:</label>
21158        <input type="time" id="exampleInput" name="input" ng-model="example.value"
21159            placeholder="HH:mm:ss" min="08:00:00" max="17:00:00" required />
21160        <div role="alert">
21161          <span class="error" ng-show="myForm.input.$error.required">
21162              Required!</span>
21163          <span class="error" ng-show="myForm.input.$error.time">
21164              Not a valid date!</span>
21165        </div>
21166        <tt>value = {{example.value | date: "HH:mm:ss"}}</tt><br/>
21167        <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21168        <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21169        <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21170        <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21171     </form>
21172   </file>
21173   <file name="protractor.js" type="protractor">
21174      var value = element(by.binding('example.value | date: "HH:mm:ss"'));
21175      var valid = element(by.binding('myForm.input.$valid'));
21176      var input = element(by.model('example.value'));
21177
21178      // currently protractor/webdriver does not support
21179      // sending keys to all known HTML5 input controls
21180      // for various browsers (https://github.com/angular/protractor/issues/562).
21181      function setInput(val) {
21182        // set the value of the element and force validation.
21183        var scr = "var ipt = document.getElementById('exampleInput'); " +
21184        "ipt.value = '" + val + "';" +
21185        "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
21186        browser.executeScript(scr);
21187      }
21188
21189      it('should initialize to model', function() {
21190        expect(value.getText()).toContain('14:57:00');
21191        expect(valid.getText()).toContain('myForm.input.$valid = true');
21192      });
21193
21194      it('should be invalid if empty', function() {
21195        setInput('');
21196        expect(value.getText()).toEqual('value =');
21197        expect(valid.getText()).toContain('myForm.input.$valid = false');
21198      });
21199
21200      it('should be invalid if over max', function() {
21201        setInput('23:59:00');
21202        expect(value.getText()).toContain('');
21203        expect(valid.getText()).toContain('myForm.input.$valid = false');
21204      });
21205   </file>
21206   </example>
21207   */
21208  'time': createDateInputType('time', TIME_REGEXP,
21209      createDateParser(TIME_REGEXP, ['HH', 'mm', 'ss', 'sss']),
21210     'HH:mm:ss.sss'),
21211
21212   /**
21213    * @ngdoc input
21214    * @name input[week]
21215    *
21216    * @description
21217    * Input with week-of-the-year validation and transformation to Date. In browsers that do not yet support
21218    * the HTML5 week input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
21219    * week format (yyyy-W##), for example: `2013-W02`.
21220    *
21221    * The model must always be a Date object, otherwise Angular will throw an error.
21222    * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
21223    *
21224    * The timezone to be used to read/write the `Date` instance in the model can be defined using
21225    * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
21226    *
21227    * @param {string} ngModel Assignable angular expression to data-bind to.
21228    * @param {string=} name Property name of the form under which the control is published.
21229    * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
21230    *   This must be a valid ISO week format (yyyy-W##). You can also use interpolation inside this
21231    *   attribute (e.g. `min="{{minWeek | date:'yyyy-Www'}}"`). Note that `min` will also add
21232    *   native HTML5 constraint validation.
21233    * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
21234    *   This must be a valid ISO week format (yyyy-W##). You can also use interpolation inside this
21235    *   attribute (e.g. `max="{{maxWeek | date:'yyyy-Www'}}"`). Note that `max` will also add
21236    *   native HTML5 constraint validation.
21237    * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO week string
21238    *   the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
21239    * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO week string
21240    *   the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
21241    * @param {string=} required Sets `required` validation error key if the value is not entered.
21242    * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21243    *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21244    *    `required` when you want to data-bind to the `required` attribute.
21245    * @param {string=} ngChange Angular expression to be executed when input changes due to user
21246    *    interaction with the input element.
21247    *
21248    * @example
21249    <example name="week-input-directive" module="weekExample">
21250    <file name="index.html">
21251      <script>
21252      angular.module('weekExample', [])
21253        .controller('DateController', ['$scope', function($scope) {
21254          $scope.example = {
21255            value: new Date(2013, 0, 3)
21256          };
21257        }]);
21258      </script>
21259      <form name="myForm" ng-controller="DateController as dateCtrl">
21260        <label>Pick a date between in 2013:
21261          <input id="exampleInput" type="week" name="input" ng-model="example.value"
21262                 placeholder="YYYY-W##" min="2012-W32"
21263                 max="2013-W52" required />
21264        </label>
21265        <div role="alert">
21266          <span class="error" ng-show="myForm.input.$error.required">
21267              Required!</span>
21268          <span class="error" ng-show="myForm.input.$error.week">
21269              Not a valid date!</span>
21270        </div>
21271        <tt>value = {{example.value | date: "yyyy-Www"}}</tt><br/>
21272        <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21273        <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21274        <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21275        <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21276      </form>
21277    </file>
21278    <file name="protractor.js" type="protractor">
21279      var value = element(by.binding('example.value | date: "yyyy-Www"'));
21280      var valid = element(by.binding('myForm.input.$valid'));
21281      var input = element(by.model('example.value'));
21282
21283      // currently protractor/webdriver does not support
21284      // sending keys to all known HTML5 input controls
21285      // for various browsers (https://github.com/angular/protractor/issues/562).
21286      function setInput(val) {
21287        // set the value of the element and force validation.
21288        var scr = "var ipt = document.getElementById('exampleInput'); " +
21289        "ipt.value = '" + val + "';" +
21290        "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
21291        browser.executeScript(scr);
21292      }
21293
21294      it('should initialize to model', function() {
21295        expect(value.getText()).toContain('2013-W01');
21296        expect(valid.getText()).toContain('myForm.input.$valid = true');
21297      });
21298
21299      it('should be invalid if empty', function() {
21300        setInput('');
21301        expect(value.getText()).toEqual('value =');
21302        expect(valid.getText()).toContain('myForm.input.$valid = false');
21303      });
21304
21305      it('should be invalid if over max', function() {
21306        setInput('2015-W01');
21307        expect(value.getText()).toContain('');
21308        expect(valid.getText()).toContain('myForm.input.$valid = false');
21309      });
21310    </file>
21311    </example>
21312    */
21313  'week': createDateInputType('week', WEEK_REGEXP, weekParser, 'yyyy-Www'),
21314
21315  /**
21316   * @ngdoc input
21317   * @name input[month]
21318   *
21319   * @description
21320   * Input with month validation and transformation. In browsers that do not yet support
21321   * the HTML5 month input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
21322   * month format (yyyy-MM), for example: `2009-01`.
21323   *
21324   * The model must always be a Date object, otherwise Angular will throw an error.
21325   * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
21326   * If the model is not set to the first of the month, the next view to model update will set it
21327   * to the first of the month.
21328   *
21329   * The timezone to be used to read/write the `Date` instance in the model can be defined using
21330   * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
21331   *
21332   * @param {string} ngModel Assignable angular expression to data-bind to.
21333   * @param {string=} name Property name of the form under which the control is published.
21334   * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
21335   *   This must be a valid ISO month format (yyyy-MM). You can also use interpolation inside this
21336   *   attribute (e.g. `min="{{minMonth | date:'yyyy-MM'}}"`). Note that `min` will also add
21337   *   native HTML5 constraint validation.
21338   * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
21339   *   This must be a valid ISO month format (yyyy-MM). You can also use interpolation inside this
21340   *   attribute (e.g. `max="{{maxMonth | date:'yyyy-MM'}}"`). Note that `max` will also add
21341   *   native HTML5 constraint validation.
21342   * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO week string
21343   *   the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
21344   * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO week string
21345   *   the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
21346
21347   * @param {string=} required Sets `required` validation error key if the value is not entered.
21348   * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21349   *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21350   *    `required` when you want to data-bind to the `required` attribute.
21351   * @param {string=} ngChange Angular expression to be executed when input changes due to user
21352   *    interaction with the input element.
21353   *
21354   * @example
21355   <example name="month-input-directive" module="monthExample">
21356   <file name="index.html">
21357     <script>
21358      angular.module('monthExample', [])
21359        .controller('DateController', ['$scope', function($scope) {
21360          $scope.example = {
21361            value: new Date(2013, 9, 1)
21362          };
21363        }]);
21364     </script>
21365     <form name="myForm" ng-controller="DateController as dateCtrl">
21366       <label for="exampleInput">Pick a month in 2013:</label>
21367       <input id="exampleInput" type="month" name="input" ng-model="example.value"
21368          placeholder="yyyy-MM" min="2013-01" max="2013-12" required />
21369       <div role="alert">
21370         <span class="error" ng-show="myForm.input.$error.required">
21371            Required!</span>
21372         <span class="error" ng-show="myForm.input.$error.month">
21373            Not a valid month!</span>
21374       </div>
21375       <tt>value = {{example.value | date: "yyyy-MM"}}</tt><br/>
21376       <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21377       <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21378       <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21379       <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21380     </form>
21381   </file>
21382   <file name="protractor.js" type="protractor">
21383      var value = element(by.binding('example.value | date: "yyyy-MM"'));
21384      var valid = element(by.binding('myForm.input.$valid'));
21385      var input = element(by.model('example.value'));
21386
21387      // currently protractor/webdriver does not support
21388      // sending keys to all known HTML5 input controls
21389      // for various browsers (https://github.com/angular/protractor/issues/562).
21390      function setInput(val) {
21391        // set the value of the element and force validation.
21392        var scr = "var ipt = document.getElementById('exampleInput'); " +
21393        "ipt.value = '" + val + "';" +
21394        "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
21395        browser.executeScript(scr);
21396      }
21397
21398      it('should initialize to model', function() {
21399        expect(value.getText()).toContain('2013-10');
21400        expect(valid.getText()).toContain('myForm.input.$valid = true');
21401      });
21402
21403      it('should be invalid if empty', function() {
21404        setInput('');
21405        expect(value.getText()).toEqual('value =');
21406        expect(valid.getText()).toContain('myForm.input.$valid = false');
21407      });
21408
21409      it('should be invalid if over max', function() {
21410        setInput('2015-01');
21411        expect(value.getText()).toContain('');
21412        expect(valid.getText()).toContain('myForm.input.$valid = false');
21413      });
21414   </file>
21415   </example>
21416   */
21417  'month': createDateInputType('month', MONTH_REGEXP,
21418     createDateParser(MONTH_REGEXP, ['yyyy', 'MM']),
21419     'yyyy-MM'),
21420
21421  /**
21422   * @ngdoc input
21423   * @name input[number]
21424   *
21425   * @description
21426   * Text input with number validation and transformation. Sets the `number` validation
21427   * error if not a valid number.
21428   *
21429   * <div class="alert alert-warning">
21430   * The model must always be of type `number` otherwise Angular will throw an error.
21431   * Be aware that a string containing a number is not enough. See the {@link ngModel:numfmt}
21432   * error docs for more information and an example of how to convert your model if necessary.
21433   * </div>
21434   *
21435   * ## Issues with HTML5 constraint validation
21436   *
21437   * In browsers that follow the
21438   * [HTML5 specification](https://html.spec.whatwg.org/multipage/forms.html#number-state-%28type=number%29),
21439   * `input[number]` does not work as expected with {@link ngModelOptions `ngModelOptions.allowInvalid`}.
21440   * If a non-number is entered in the input, the browser will report the value as an empty string,
21441   * which means the view / model values in `ngModel` and subsequently the scope value
21442   * will also be an empty string.
21443   *
21444   *
21445   * @param {string} ngModel Assignable angular expression to data-bind to.
21446   * @param {string=} name Property name of the form under which the control is published.
21447   * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
21448   * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
21449   * @param {string=} required Sets `required` validation error key if the value is not entered.
21450   * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21451   *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21452   *    `required` when you want to data-bind to the `required` attribute.
21453   * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
21454   *    minlength.
21455   * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
21456   *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
21457   *    any length.
21458   * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
21459   *    that contains the regular expression body that will be converted to a regular expression
21460   *    as in the ngPattern directive.
21461   * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
21462   *    a RegExp found by evaluating the Angular expression given in the attribute value.
21463   *    If the expression evaluates to a RegExp object, then this is used directly.
21464   *    If the expression evaluates to a string, then it will be converted to a RegExp
21465   *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
21466   *    `new RegExp('^abc$')`.<br />
21467   *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
21468   *    start at the index of the last search's match, thus not taking the whole input value into
21469   *    account.
21470   * @param {string=} ngChange Angular expression to be executed when input changes due to user
21471   *    interaction with the input element.
21472   *
21473   * @example
21474      <example name="number-input-directive" module="numberExample">
21475        <file name="index.html">
21476         <script>
21477           angular.module('numberExample', [])
21478             .controller('ExampleController', ['$scope', function($scope) {
21479               $scope.example = {
21480                 value: 12
21481               };
21482             }]);
21483         </script>
21484         <form name="myForm" ng-controller="ExampleController">
21485           <label>Number:
21486             <input type="number" name="input" ng-model="example.value"
21487                    min="0" max="99" required>
21488          </label>
21489           <div role="alert">
21490             <span class="error" ng-show="myForm.input.$error.required">
21491               Required!</span>
21492             <span class="error" ng-show="myForm.input.$error.number">
21493               Not valid number!</span>
21494           </div>
21495           <tt>value = {{example.value}}</tt><br/>
21496           <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21497           <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21498           <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21499           <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21500          </form>
21501        </file>
21502        <file name="protractor.js" type="protractor">
21503          var value = element(by.binding('example.value'));
21504          var valid = element(by.binding('myForm.input.$valid'));
21505          var input = element(by.model('example.value'));
21506
21507          it('should initialize to model', function() {
21508            expect(value.getText()).toContain('12');
21509            expect(valid.getText()).toContain('true');
21510          });
21511
21512          it('should be invalid if empty', function() {
21513            input.clear();
21514            input.sendKeys('');
21515            expect(value.getText()).toEqual('value =');
21516            expect(valid.getText()).toContain('false');
21517          });
21518
21519          it('should be invalid if over max', function() {
21520            input.clear();
21521            input.sendKeys('123');
21522            expect(value.getText()).toEqual('value =');
21523            expect(valid.getText()).toContain('false');
21524          });
21525        </file>
21526      </example>
21527   */
21528  'number': numberInputType,
21529
21530
21531  /**
21532   * @ngdoc input
21533   * @name input[url]
21534   *
21535   * @description
21536   * Text input with URL validation. Sets the `url` validation error key if the content is not a
21537   * valid URL.
21538   *
21539   * <div class="alert alert-warning">
21540   * **Note:** `input[url]` uses a regex to validate urls that is derived from the regex
21541   * used in Chromium. If you need stricter validation, you can use `ng-pattern` or modify
21542   * the built-in validators (see the {@link guide/forms Forms guide})
21543   * </div>
21544   *
21545   * @param {string} ngModel Assignable angular expression to data-bind to.
21546   * @param {string=} name Property name of the form under which the control is published.
21547   * @param {string=} required Sets `required` validation error key if the value is not entered.
21548   * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21549   *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21550   *    `required` when you want to data-bind to the `required` attribute.
21551   * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
21552   *    minlength.
21553   * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
21554   *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
21555   *    any length.
21556   * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
21557   *    that contains the regular expression body that will be converted to a regular expression
21558   *    as in the ngPattern directive.
21559   * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
21560   *    a RegExp found by evaluating the Angular expression given in the attribute value.
21561   *    If the expression evaluates to a RegExp object, then this is used directly.
21562   *    If the expression evaluates to a string, then it will be converted to a RegExp
21563   *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
21564   *    `new RegExp('^abc$')`.<br />
21565   *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
21566   *    start at the index of the last search's match, thus not taking the whole input value into
21567   *    account.
21568   * @param {string=} ngChange Angular expression to be executed when input changes due to user
21569   *    interaction with the input element.
21570   *
21571   * @example
21572      <example name="url-input-directive" module="urlExample">
21573        <file name="index.html">
21574         <script>
21575           angular.module('urlExample', [])
21576             .controller('ExampleController', ['$scope', function($scope) {
21577               $scope.url = {
21578                 text: 'http://google.com'
21579               };
21580             }]);
21581         </script>
21582         <form name="myForm" ng-controller="ExampleController">
21583           <label>URL:
21584             <input type="url" name="input" ng-model="url.text" required>
21585           <label>
21586           <div role="alert">
21587             <span class="error" ng-show="myForm.input.$error.required">
21588               Required!</span>
21589             <span class="error" ng-show="myForm.input.$error.url">
21590               Not valid url!</span>
21591           </div>
21592           <tt>text = {{url.text}}</tt><br/>
21593           <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21594           <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21595           <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21596           <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21597           <tt>myForm.$error.url = {{!!myForm.$error.url}}</tt><br/>
21598          </form>
21599        </file>
21600        <file name="protractor.js" type="protractor">
21601          var text = element(by.binding('url.text'));
21602          var valid = element(by.binding('myForm.input.$valid'));
21603          var input = element(by.model('url.text'));
21604
21605          it('should initialize to model', function() {
21606            expect(text.getText()).toContain('http://google.com');
21607            expect(valid.getText()).toContain('true');
21608          });
21609
21610          it('should be invalid if empty', function() {
21611            input.clear();
21612            input.sendKeys('');
21613
21614            expect(text.getText()).toEqual('text =');
21615            expect(valid.getText()).toContain('false');
21616          });
21617
21618          it('should be invalid if not url', function() {
21619            input.clear();
21620            input.sendKeys('box');
21621
21622            expect(valid.getText()).toContain('false');
21623          });
21624        </file>
21625      </example>
21626   */
21627  'url': urlInputType,
21628
21629
21630  /**
21631   * @ngdoc input
21632   * @name input[email]
21633   *
21634   * @description
21635   * Text input with email validation. Sets the `email` validation error key if not a valid email
21636   * address.
21637   *
21638   * <div class="alert alert-warning">
21639   * **Note:** `input[email]` uses a regex to validate email addresses that is derived from the regex
21640   * used in Chromium. If you need stricter validation (e.g. requiring a top-level domain), you can
21641   * use `ng-pattern` or modify the built-in validators (see the {@link guide/forms Forms guide})
21642   * </div>
21643   *
21644   * @param {string} ngModel Assignable angular expression to data-bind to.
21645   * @param {string=} name Property name of the form under which the control is published.
21646   * @param {string=} required Sets `required` validation error key if the value is not entered.
21647   * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21648   *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21649   *    `required` when you want to data-bind to the `required` attribute.
21650   * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
21651   *    minlength.
21652   * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
21653   *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
21654   *    any length.
21655   * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
21656   *    that contains the regular expression body that will be converted to a regular expression
21657   *    as in the ngPattern directive.
21658   * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
21659   *    a RegExp found by evaluating the Angular expression given in the attribute value.
21660   *    If the expression evaluates to a RegExp object, then this is used directly.
21661   *    If the expression evaluates to a string, then it will be converted to a RegExp
21662   *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
21663   *    `new RegExp('^abc$')`.<br />
21664   *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
21665   *    start at the index of the last search's match, thus not taking the whole input value into
21666   *    account.
21667   * @param {string=} ngChange Angular expression to be executed when input changes due to user
21668   *    interaction with the input element.
21669   *
21670   * @example
21671      <example name="email-input-directive" module="emailExample">
21672        <file name="index.html">
21673         <script>
21674           angular.module('emailExample', [])
21675             .controller('ExampleController', ['$scope', function($scope) {
21676               $scope.email = {
21677                 text: '[email protected]'
21678               };
21679             }]);
21680         </script>
21681           <form name="myForm" ng-controller="ExampleController">
21682             <label>Email:
21683               <input type="email" name="input" ng-model="email.text" required>
21684             </label>
21685             <div role="alert">
21686               <span class="error" ng-show="myForm.input.$error.required">
21687                 Required!</span>
21688               <span class="error" ng-show="myForm.input.$error.email">
21689                 Not valid email!</span>
21690             </div>
21691             <tt>text = {{email.text}}</tt><br/>
21692             <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21693             <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21694             <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21695             <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21696             <tt>myForm.$error.email = {{!!myForm.$error.email}}</tt><br/>
21697           </form>
21698         </file>
21699        <file name="protractor.js" type="protractor">
21700          var text = element(by.binding('email.text'));
21701          var valid = element(by.binding('myForm.input.$valid'));
21702          var input = element(by.model('email.text'));
21703
21704          it('should initialize to model', function() {
21705            expect(text.getText()).toContain('[email protected]');
21706            expect(valid.getText()).toContain('true');
21707          });
21708
21709          it('should be invalid if empty', function() {
21710            input.clear();
21711            input.sendKeys('');
21712            expect(text.getText()).toEqual('text =');
21713            expect(valid.getText()).toContain('false');
21714          });
21715
21716          it('should be invalid if not email', function() {
21717            input.clear();
21718            input.sendKeys('xxx');
21719
21720            expect(valid.getText()).toContain('false');
21721          });
21722        </file>
21723      </example>
21724   */
21725  'email': emailInputType,
21726
21727
21728  /**
21729   * @ngdoc input
21730   * @name input[radio]
21731   *
21732   * @description
21733   * HTML radio button.
21734   *
21735   * @param {string} ngModel Assignable angular expression to data-bind to.
21736   * @param {string} value The value to which the `ngModel` expression should be set when selected.
21737   *    Note that `value` only supports `string` values, i.e. the scope model needs to be a string,
21738   *    too. Use `ngValue` if you need complex models (`number`, `object`, ...).
21739   * @param {string=} name Property name of the form under which the control is published.
21740   * @param {string=} ngChange Angular expression to be executed when input changes due to user
21741   *    interaction with the input element.
21742   * @param {string} ngValue Angular expression to which `ngModel` will be be set when the radio
21743   *    is selected. Should be used instead of the `value` attribute if you need
21744   *    a non-string `ngModel` (`boolean`, `array`, ...).
21745   *
21746   * @example
21747      <example name="radio-input-directive" module="radioExample">
21748        <file name="index.html">
21749         <script>
21750           angular.module('radioExample', [])
21751             .controller('ExampleController', ['$scope', function($scope) {
21752               $scope.color = {
21753                 name: 'blue'
21754               };
21755               $scope.specialValue = {
21756                 "id": "12345",
21757                 "value": "green"
21758               };
21759             }]);
21760         </script>
21761         <form name="myForm" ng-controller="ExampleController">
21762           <label>
21763             <input type="radio" ng-model="color.name" value="red">
21764             Red
21765           </label><br/>
21766           <label>
21767             <input type="radio" ng-model="color.name" ng-value="specialValue">
21768             Green
21769           </label><br/>
21770           <label>
21771             <input type="radio" ng-model="color.name" value="blue">
21772             Blue
21773           </label><br/>
21774           <tt>color = {{color.name | json}}</tt><br/>
21775          </form>
21776          Note that `ng-value="specialValue"` sets radio item's value to be the value of `$scope.specialValue`.
21777        </file>
21778        <file name="protractor.js" type="protractor">
21779          it('should change state', function() {
21780            var color = element(by.binding('color.name'));
21781
21782            expect(color.getText()).toContain('blue');
21783
21784            element.all(by.model('color.name')).get(0).click();
21785
21786            expect(color.getText()).toContain('red');
21787          });
21788        </file>
21789      </example>
21790   */
21791  'radio': radioInputType,
21792
21793
21794  /**
21795   * @ngdoc input
21796   * @name input[checkbox]
21797   *
21798   * @description
21799   * HTML checkbox.
21800   *
21801   * @param {string} ngModel Assignable angular expression to data-bind to.
21802   * @param {string=} name Property name of the form under which the control is published.
21803   * @param {expression=} ngTrueValue The value to which the expression should be set when selected.
21804   * @param {expression=} ngFalseValue The value to which the expression should be set when not selected.
21805   * @param {string=} ngChange Angular expression to be executed when input changes due to user
21806   *    interaction with the input element.
21807   *
21808   * @example
21809      <example name="checkbox-input-directive" module="checkboxExample">
21810        <file name="index.html">
21811         <script>
21812           angular.module('checkboxExample', [])
21813             .controller('ExampleController', ['$scope', function($scope) {
21814               $scope.checkboxModel = {
21815                value1 : true,
21816                value2 : 'YES'
21817              };
21818             }]);
21819         </script>
21820         <form name="myForm" ng-controller="ExampleController">
21821           <label>Value1:
21822             <input type="checkbox" ng-model="checkboxModel.value1">
21823           </label><br/>
21824           <label>Value2:
21825             <input type="checkbox" ng-model="checkboxModel.value2"
21826                    ng-true-value="'YES'" ng-false-value="'NO'">
21827            </label><br/>
21828           <tt>value1 = {{checkboxModel.value1}}</tt><br/>
21829           <tt>value2 = {{checkboxModel.value2}}</tt><br/>
21830          </form>
21831        </file>
21832        <file name="protractor.js" type="protractor">
21833          it('should change state', function() {
21834            var value1 = element(by.binding('checkboxModel.value1'));
21835            var value2 = element(by.binding('checkboxModel.value2'));
21836
21837            expect(value1.getText()).toContain('true');
21838            expect(value2.getText()).toContain('YES');
21839
21840            element(by.model('checkboxModel.value1')).click();
21841            element(by.model('checkboxModel.value2')).click();
21842
21843            expect(value1.getText()).toContain('false');
21844            expect(value2.getText()).toContain('NO');
21845          });
21846        </file>
21847      </example>
21848   */
21849  'checkbox': checkboxInputType,
21850
21851  'hidden': noop,
21852  'button': noop,
21853  'submit': noop,
21854  'reset': noop,
21855  'file': noop
21856};
21857
21858function stringBasedInputType(ctrl) {
21859  ctrl.$formatters.push(function(value) {
21860    return ctrl.$isEmpty(value) ? value : value.toString();
21861  });
21862}
21863
21864function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
21865  baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
21866  stringBasedInputType(ctrl);
21867}
21868
21869function baseInputType(scope, element, attr, ctrl, $sniffer, $browser) {
21870  var type = lowercase(element[0].type);
21871
21872  // In composition mode, users are still inputing intermediate text buffer,
21873  // hold the listener until composition is done.
21874  // More about composition events: https://developer.mozilla.org/en-US/docs/Web/API/CompositionEvent
21875  if (!$sniffer.android) {
21876    var composing = false;
21877
21878    element.on('compositionstart', function(data) {
21879      composing = true;
21880    });
21881
21882    element.on('compositionend', function() {
21883      composing = false;
21884      listener();
21885    });
21886  }
21887
21888  var listener = function(ev) {
21889    if (timeout) {
21890      $browser.defer.cancel(timeout);
21891      timeout = null;
21892    }
21893    if (composing) return;
21894    var value = element.val(),
21895        event = ev && ev.type;
21896
21897    // By default we will trim the value
21898    // If the attribute ng-trim exists we will avoid trimming
21899    // If input type is 'password', the value is never trimmed
21900    if (type !== 'password' && (!attr.ngTrim || attr.ngTrim !== 'false')) {
21901      value = trim(value);
21902    }
21903
21904    // If a control is suffering from bad input (due to native validators), browsers discard its
21905    // value, so it may be necessary to revalidate (by calling $setViewValue again) even if the
21906    // control's value is the same empty value twice in a row.
21907    if (ctrl.$viewValue !== value || (value === '' && ctrl.$$hasNativeValidators)) {
21908      ctrl.$setViewValue(value, event);
21909    }
21910  };
21911
21912  // if the browser does support "input" event, we are fine - except on IE9 which doesn't fire the
21913  // input event on backspace, delete or cut
21914  if ($sniffer.hasEvent('input')) {
21915    element.on('input', listener);
21916  } else {
21917    var timeout;
21918
21919    var deferListener = function(ev, input, origValue) {
21920      if (!timeout) {
21921        timeout = $browser.defer(function() {
21922          timeout = null;
21923          if (!input || input.value !== origValue) {
21924            listener(ev);
21925          }
21926        });
21927      }
21928    };
21929
21930    element.on('keydown', function(event) {
21931      var key = event.keyCode;
21932
21933      // ignore
21934      //    command            modifiers                   arrows
21935      if (key === 91 || (15 < key && key < 19) || (37 <= key && key <= 40)) return;
21936
21937      deferListener(event, this, this.value);
21938    });
21939
21940    // if user modifies input value using context menu in IE, we need "paste" and "cut" events to catch it
21941    if ($sniffer.hasEvent('paste')) {
21942      element.on('paste cut', deferListener);
21943    }
21944  }
21945
21946  // if user paste into input using mouse on older browser
21947  // or form autocomplete on newer browser, we need "change" event to catch it
21948  element.on('change', listener);
21949
21950  ctrl.$render = function() {
21951    // Workaround for Firefox validation #12102.
21952    var value = ctrl.$isEmpty(ctrl.$viewValue) ? '' : ctrl.$viewValue;
21953    if (element.val() !== value) {
21954      element.val(value);
21955    }
21956  };
21957}
21958
21959function weekParser(isoWeek, existingDate) {
21960  if (isDate(isoWeek)) {
21961    return isoWeek;
21962  }
21963
21964  if (isString(isoWeek)) {
21965    WEEK_REGEXP.lastIndex = 0;
21966    var parts = WEEK_REGEXP.exec(isoWeek);
21967    if (parts) {
21968      var year = +parts[1],
21969          week = +parts[2],
21970          hours = 0,
21971          minutes = 0,
21972          seconds = 0,
21973          milliseconds = 0,
21974          firstThurs = getFirstThursdayOfYear(year),
21975          addDays = (week - 1) * 7;
21976
21977      if (existingDate) {
21978        hours = existingDate.getHours();
21979        minutes = existingDate.getMinutes();
21980        seconds = existingDate.getSeconds();
21981        milliseconds = existingDate.getMilliseconds();
21982      }
21983
21984      return new Date(year, 0, firstThurs.getDate() + addDays, hours, minutes, seconds, milliseconds);
21985    }
21986  }
21987
21988  return NaN;
21989}
21990
21991function createDateParser(regexp, mapping) {
21992  return function(iso, date) {
21993    var parts, map;
21994
21995    if (isDate(iso)) {
21996      return iso;
21997    }
21998
21999    if (isString(iso)) {
22000      // When a date is JSON'ified to wraps itself inside of an extra
22001      // set of double quotes. This makes the date parsing code unable
22002      // to match the date string and parse it as a date.
22003      if (iso.charAt(0) == '"' && iso.charAt(iso.length - 1) == '"') {
22004        iso = iso.substring(1, iso.length - 1);
22005      }
22006      if (ISO_DATE_REGEXP.test(iso)) {
22007        return new Date(iso);
22008      }
22009      regexp.lastIndex = 0;
22010      parts = regexp.exec(iso);
22011
22012      if (parts) {
22013        parts.shift();
22014        if (date) {
22015          map = {
22016            yyyy: date.getFullYear(),
22017            MM: date.getMonth() + 1,
22018            dd: date.getDate(),
22019            HH: date.getHours(),
22020            mm: date.getMinutes(),
22021            ss: date.getSeconds(),
22022            sss: date.getMilliseconds() / 1000
22023          };
22024        } else {
22025          map = { yyyy: 1970, MM: 1, dd: 1, HH: 0, mm: 0, ss: 0, sss: 0 };
22026        }
22027
22028        forEach(parts, function(part, index) {
22029          if (index < mapping.length) {
22030            map[mapping[index]] = +part;
22031          }
22032        });
22033        return new Date(map.yyyy, map.MM - 1, map.dd, map.HH, map.mm, map.ss || 0, map.sss * 1000 || 0);
22034      }
22035    }
22036
22037    return NaN;
22038  };
22039}
22040
22041function createDateInputType(type, regexp, parseDate, format) {
22042  return function dynamicDateInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter) {
22043    badInputChecker(scope, element, attr, ctrl);
22044    baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
22045    var timezone = ctrl && ctrl.$options && ctrl.$options.timezone;
22046    var previousDate;
22047
22048    ctrl.$$parserName = type;
22049    ctrl.$parsers.push(function(value) {
22050      if (ctrl.$isEmpty(value)) return null;
22051      if (regexp.test(value)) {
22052        // Note: We cannot read ctrl.$modelValue, as there might be a different
22053        // parser/formatter in the processing chain so that the model
22054        // contains some different data format!
22055        var parsedDate = parseDate(value, previousDate);
22056        if (timezone) {
22057          parsedDate = convertTimezoneToLocal(parsedDate, timezone);
22058        }
22059        return parsedDate;
22060      }
22061      return undefined;
22062    });
22063
22064    ctrl.$formatters.push(function(value) {
22065      if (value && !isDate(value)) {
22066        throw ngModelMinErr('datefmt', 'Expected `{0}` to be a date', value);
22067      }
22068      if (isValidDate(value)) {
22069        previousDate = value;
22070        if (previousDate && timezone) {
22071          previousDate = convertTimezoneToLocal(previousDate, timezone, true);
22072        }
22073        return $filter('date')(value, format, timezone);
22074      } else {
22075        previousDate = null;
22076        return '';
22077      }
22078    });
22079
22080    if (isDefined(attr.min) || attr.ngMin) {
22081      var minVal;
22082      ctrl.$validators.min = function(value) {
22083        return !isValidDate(value) || isUndefined(minVal) || parseDate(value) >= minVal;
22084      };
22085      attr.$observe('min', function(val) {
22086        minVal = parseObservedDateValue(val);
22087        ctrl.$validate();
22088      });
22089    }
22090
22091    if (isDefined(attr.max) || attr.ngMax) {
22092      var maxVal;
22093      ctrl.$validators.max = function(value) {
22094        return !isValidDate(value) || isUndefined(maxVal) || parseDate(value) <= maxVal;
22095      };
22096      attr.$observe('max', function(val) {
22097        maxVal = parseObservedDateValue(val);
22098        ctrl.$validate();
22099      });
22100    }
22101
22102    function isValidDate(value) {
22103      // Invalid Date: getTime() returns NaN
22104      return value && !(value.getTime && value.getTime() !== value.getTime());
22105    }
22106
22107    function parseObservedDateValue(val) {
22108      return isDefined(val) && !isDate(val) ? parseDate(val) || undefined : val;
22109    }
22110  };
22111}
22112
22113function badInputChecker(scope, element, attr, ctrl) {
22114  var node = element[0];
22115  var nativeValidation = ctrl.$$hasNativeValidators = isObject(node.validity);
22116  if (nativeValidation) {
22117    ctrl.$parsers.push(function(value) {
22118      var validity = element.prop(VALIDITY_STATE_PROPERTY) || {};
22119      // Detect bug in FF35 for input[email] (https://bugzilla.mozilla.org/show_bug.cgi?id=1064430):
22120      // - also sets validity.badInput (should only be validity.typeMismatch).
22121      // - see http://www.whatwg.org/specs/web-apps/current-work/multipage/forms.html#e-mail-state-(type=email)
22122      // - can ignore this case as we can still read out the erroneous email...
22123      return validity.badInput && !validity.typeMismatch ? undefined : value;
22124    });
22125  }
22126}
22127
22128function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) {
22129  badInputChecker(scope, element, attr, ctrl);
22130  baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
22131
22132  ctrl.$$parserName = 'number';
22133  ctrl.$parsers.push(function(value) {
22134    if (ctrl.$isEmpty(value))      return null;
22135    if (NUMBER_REGEXP.test(value)) return parseFloat(value);
22136    return undefined;
22137  });
22138
22139  ctrl.$formatters.push(function(value) {
22140    if (!ctrl.$isEmpty(value)) {
22141      if (!isNumber(value)) {
22142        throw ngModelMinErr('numfmt', 'Expected `{0}` to be a number', value);
22143      }
22144      value = value.toString();
22145    }
22146    return value;
22147  });
22148
22149  if (isDefined(attr.min) || attr.ngMin) {
22150    var minVal;
22151    ctrl.$validators.min = function(value) {
22152      return ctrl.$isEmpty(value) || isUndefined(minVal) || value >= minVal;
22153    };
22154
22155    attr.$observe('min', function(val) {
22156      if (isDefined(val) && !isNumber(val)) {
22157        val = parseFloat(val, 10);
22158      }
22159      minVal = isNumber(val) && !isNaN(val) ? val : undefined;
22160      // TODO(matsko): implement validateLater to reduce number of validations
22161      ctrl.$validate();
22162    });
22163  }
22164
22165  if (isDefined(attr.max) || attr.ngMax) {
22166    var maxVal;
22167    ctrl.$validators.max = function(value) {
22168      return ctrl.$isEmpty(value) || isUndefined(maxVal) || value <= maxVal;
22169    };
22170
22171    attr.$observe('max', function(val) {
22172      if (isDefined(val) && !isNumber(val)) {
22173        val = parseFloat(val, 10);
22174      }
22175      maxVal = isNumber(val) && !isNaN(val) ? val : undefined;
22176      // TODO(matsko): implement validateLater to reduce number of validations
22177      ctrl.$validate();
22178    });
22179  }
22180}
22181
22182function urlInputType(scope, element, attr, ctrl, $sniffer, $browser) {
22183  // Note: no badInputChecker here by purpose as `url` is only a validation
22184  // in browsers, i.e. we can always read out input.value even if it is not val
vendor: 6,862 bytes, lines 22184-22345
22184id!
22185  baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
22186  stringBasedInputType(ctrl);
22187
22188  ctrl.$$parserName = 'url';
22189  ctrl.$validators.url = function(modelValue, viewValue) {
22190    var value = modelValue || viewValue;
22191    return ctrl.$isEmpty(value) || URL_REGEXP.test(value);
22192  };
22193}
22194
22195function emailInputType(scope, element, attr, ctrl, $sniffer, $browser) {
22196  // Note: no badInputChecker here by purpose as `url` is only a validation
22197  // in browsers, i.e. we can always read out input.value even if it is not valid!
22198  baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
22199  stringBasedInputType(ctrl);
22200
22201  ctrl.$$parserName = 'email';
22202  ctrl.$validators.email = function(modelValue, viewValue) {
22203    var value = modelValue || viewValue;
22204    return ctrl.$isEmpty(value) || EMAIL_REGEXP.test(value);
22205  };
22206}
22207
22208function radioInputType(scope, element, attr, ctrl) {
22209  // make the name unique, if not defined
22210  if (isUndefined(attr.name)) {
22211    element.attr('name', nextUid());
22212  }
22213
22214  var listener = function(ev) {
22215    if (element[0].checked) {
22216      ctrl.$setViewValue(attr.value, ev && ev.type);
22217    }
22218  };
22219
22220  element.on('click', listener);
22221
22222  ctrl.$render = function() {
22223    var value = attr.value;
22224    element[0].checked = (value == ctrl.$viewValue);
22225  };
22226
22227  attr.$observe('value', ctrl.$render);
22228}
22229
22230function parseConstantExpr($parse, context, name, expression, fallback) {
22231  var parseFn;
22232  if (isDefined(expression)) {
22233    parseFn = $parse(expression);
22234    if (!parseFn.constant) {
22235      throw ngModelMinErr('constexpr', 'Expected constant expression for `{0}`, but saw ' +
22236                                   '`{1}`.', name, expression);
22237    }
22238    return parseFn(context);
22239  }
22240  return fallback;
22241}
22242
22243function checkboxInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter, $parse) {
22244  var trueValue = parseConstantExpr($parse, scope, 'ngTrueValue', attr.ngTrueValue, true);
22245  var falseValue = parseConstantExpr($parse, scope, 'ngFalseValue', attr.ngFalseValue, false);
22246
22247  var listener = function(ev) {
22248    ctrl.$setViewValue(element[0].checked, ev && ev.type);
22249  };
22250
22251  element.on('click', listener);
22252
22253  ctrl.$render = function() {
22254    element[0].checked = ctrl.$viewValue;
22255  };
22256
22257  // Override the standard `$isEmpty` because the $viewValue of an empty checkbox is always set to `false`
22258  // This is because of the parser below, which compares the `$modelValue` with `trueValue` to convert
22259  // it to a boolean.
22260  ctrl.$isEmpty = function(value) {
22261    return value === false;
22262  };
22263
22264  ctrl.$formatters.push(function(value) {
22265    return equals(value, trueValue);
22266  });
22267
22268  ctrl.$parsers.push(function(value) {
22269    return value ? trueValue : falseValue;
22270  });
22271}
22272
22273
22274/**
22275 * @ngdoc directive
22276 * @name textarea
22277 * @restrict E
22278 *
22279 * @description
22280 * HTML textarea element control with angular data-binding. The data-binding and validation
22281 * properties of this element are exactly the same as those of the
22282 * {@link ng.directive:input input element}.
22283 *
22284 * @param {string} ngModel Assignable angular expression to data-bind to.
22285 * @param {string=} name Property name of the form under which the control is published.
22286 * @param {string=} required Sets `required` validation error key if the value is not entered.
22287 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
22288 *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
22289 *    `required` when you want to data-bind to the `required` attribute.
22290 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
22291 *    minlength.
22292 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
22293 *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any
22294 *    length.
22295 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
22296 *    a RegExp found by evaluating the Angular expression given in the attribute value.
22297 *    If the expression evaluates to a RegExp object, then this is used directly.
22298 *    If the expression evaluates to a string, then it will be converted to a RegExp
22299 *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
22300 *    `new RegExp('^abc$')`.<br />
22301 *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
22302 *    start at the index of the last search's match, thus not taking the whole input value into
22303 *    account.
22304 * @param {string=} ngChange Angular expression to be executed when input changes due to user
22305 *    interaction with the input element.
22306 * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
22307 */
22308
22309
22310/**
22311 * @ngdoc directive
22312 * @name input
22313 * @restrict E
22314 *
22315 * @description
22316 * HTML input element control. When used together with {@link ngModel `ngModel`}, it provides data-binding,
22317 * input state control, and validation.
22318 * Input control follows HTML5 input types and polyfills the HTML5 validation behavior for older browsers.
22319 *
22320 * <div class="alert alert-warning">
22321 * **Note:** Not every feature offered is available for all input types.
22322 * Specifically, data binding and event handling via `ng-model` is unsupported for `input[file]`.
22323 * </div>
22324 *
22325 * @param {string} ngModel Assignable angular expression to data-bind to.
22326 * @param {string=} name Property name of the form under which the control is published.
22327 * @param {string=} required Sets `required` validation error key if the value is not entered.
22328 * @param {boolean=} ngRequired Sets `required` attribute if set to true
22329 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
22330 *    minlength.
22331 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
22332 *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any
22333 *    length.
22334 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
22335 *    a RegExp found by evaluating the Angular expression given in the attribute value.
22336 *    If the expression evaluates to a RegExp object, then this is used directly.
22337 *    If the expression evaluates to a string, then it will be converted to a RegExp
22338 *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
22339 *    `new RegExp('^abc$')`.<br />
22340 *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
22341 *    start at the index of the last search's match, thus not taking the whole input value into
22342 *    account.
22343 * @param {string=} ngChange Angular expression to be executed when input changes due to user
22344 *    interaction with the input element.
22345 * @param {boolean=} [ngTrim=true] If set to false Angular
vendor: 4,842 bytes, lines 22345-22463
22345 will not automatically trim the input.
22346 *    This parameter is ignored for input[type=password] controls, which will never trim the
22347 *    input.
22348 *
22349 * @example
22350    <example name="input-directive" module="inputExample">
22351      <file name="index.html">
22352       <script>
22353          angular.module('inputExample', [])
22354            .controller('ExampleController', ['$scope', function($scope) {
22355              $scope.user = {name: 'guest', last: 'visitor'};
22356            }]);
22357       </script>
22358       <div ng-controller="ExampleController">
22359         <form name="myForm">
22360           <label>
22361              User name:
22362              <input type="text" name="userName" ng-model="user.name" required>
22363           </label>
22364           <div role="alert">
22365             <span class="error" ng-show="myForm.userName.$error.required">
22366              Required!</span>
22367           </div>
22368           <label>
22369              Last name:
22370              <input type="text" name="lastName" ng-model="user.last"
22371              ng-minlength="3" ng-maxlength="10">
22372           </label>
22373           <div role="alert">
22374             <span class="error" ng-show="myForm.lastName.$error.minlength">
22375               Too short!</span>
22376             <span class="error" ng-show="myForm.lastName.$error.maxlength">
22377               Too long!</span>
22378           </div>
22379         </form>
22380         <hr>
22381         <tt>user = {{user}}</tt><br/>
22382         <tt>myForm.userName.$valid = {{myForm.userName.$valid}}</tt><br/>
22383         <tt>myForm.userName.$error = {{myForm.userName.$error}}</tt><br/>
22384         <tt>myForm.lastName.$valid = {{myForm.lastName.$valid}}</tt><br/>
22385         <tt>myForm.lastName.$error = {{myForm.lastName.$error}}</tt><br/>
22386         <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
22387         <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
22388         <tt>myForm.$error.minlength = {{!!myForm.$error.minlength}}</tt><br/>
22389         <tt>myForm.$error.maxlength = {{!!myForm.$error.maxlength}}</tt><br/>
22390       </div>
22391      </file>
22392      <file name="protractor.js" type="protractor">
22393        var user = element(by.exactBinding('user'));
22394        var userNameValid = element(by.binding('myForm.userName.$valid'));
22395        var lastNameValid = element(by.binding('myForm.lastName.$valid'));
22396        var lastNameError = element(by.binding('myForm.lastName.$error'));
22397        var formValid = element(by.binding('myForm.$valid'));
22398        var userNameInput = element(by.model('user.name'));
22399        var userLastInput = element(by.model('user.last'));
22400
22401        it('should initialize to model', function() {
22402          expect(user.getText()).toContain('{"name":"guest","last":"visitor"}');
22403          expect(userNameValid.getText()).toContain('true');
22404          expect(formValid.getText()).toContain('true');
22405        });
22406
22407        it('should be invalid if empty when required', function() {
22408          userNameInput.clear();
22409          userNameInput.sendKeys('');
22410
22411          expect(user.getText()).toContain('{"last":"visitor"}');
22412          expect(userNameValid.getText()).toContain('false');
22413          expect(formValid.getText()).toContain('false');
22414        });
22415
22416        it('should be valid if empty when min length is set', function() {
22417          userLastInput.clear();
22418          userLastInput.sendKeys('');
22419
22420          expect(user.getText()).toContain('{"name":"guest","last":""}');
22421          expect(lastNameValid.getText()).toContain('true');
22422          expect(formValid.getText()).toContain('true');
22423        });
22424
22425        it('should be invalid if less than required min length', function() {
22426          userLastInput.clear();
22427          userLastInput.sendKeys('xx');
22428
22429          expect(user.getText()).toContain('{"name":"guest"}');
22430          expect(lastNameValid.getText()).toContain('false');
22431          expect(lastNameError.getText()).toContain('minlength');
22432          expect(formValid.getText()).toContain('false');
22433        });
22434
22435        it('should be invalid if longer than max length', function() {
22436          userLastInput.clear();
22437          userLastInput.sendKeys('some ridiculously long name');
22438
22439          expect(user.getText()).toContain('{"name":"guest"}');
22440          expect(lastNameValid.getText()).toContain('false');
22441          expect(lastNameError.getText()).toContain('maxlength');
22442          expect(formValid.getText()).toContain('false');
22443        });
22444      </file>
22445    </example>
22446 */
22447var inputDirective = ['$browser', '$sniffer', '$filter', '$parse',
22448    function($browser, $sniffer, $filter, $parse) {
22449  return {
22450    restrict: 'E',
22451    require: ['?ngModel'],
22452    link: {
22453      pre: function(scope, element, attr, ctrls) {
22454        if (ctrls[0]) {
22455          (inputType[lowercase(attr.type)] || inputType.text)(scope, element, attr, ctrls[0], $sniffer,
22456                                                              $browser, $filter, $parse);
22457        }
22458      }
22459    }
22460  };
22461}];
22462
22463
22464
22465var CONSTANT_VALUE_REGEXP = /^(true|false|\d+)$/;
22466/**
22467 * @ngdoc directive
22468 * @name ngValue
22469 *
22470 * @description
22471 * Binds the given expression to the value of `<option>` or {@link input[radio] `input[radio]`},
22472 * so that when the element is selected, the {@link ngModel `ngModel`} of that element is set to
22473 * the bound value.
22474 *
22475 * `ngValue` is useful when dynamically generating lists of radio buttons using
22476 * {@link ngRepeat `ngRepeat`}, as shown below.
22477 *
22478 * Likewise, `ngValue` can be used to generate `<option>` elements for
22479 * the {@link select `select`} element. In that case however, only strings are supported
22480 * for the `value `attribute, so the resulting `ngModel` will always be a string.
22481 * Support for `select` models with non-string values is available via `ngOptions`.
22482 *
22483 * @element input
22484 * @param {string=} ngValue angular expression, whose value will be bound to the `value` attribute
22485 *   of the `input` element
22486 *
22487 * @example
22488    <example name="ngValue-directive" module="valueExample">
22489      <file name="index.html">
22490       <script>
22491          angular.module('valueExample', [])
22492            .controller('ExampleController', ['$scope', function($scope) {
22493              $scope.names = ['pizza', 'unicorns', 'robots'];
22494              $scope.my = { favorite: 'unicorns' };
22495            }]);
22496       </script>
22497        <form ng-controller="ExampleController">
22498          <h2>Which is your favorite?</h2>
22499            <label ng-repeat="name in names" for="{{name}}">
22500              {{name}}
22501              <input type="radio"
22502                     ng-model="my.favorite"
22503                     ng-value="name"
22504                     id="{{name}}"
22505                     name="favorite">
22506            </label>
22507          <div>You chose {{my.favorite}}</div>
22508        </form>
22509      </file>
22510      <file name="protractor.js" type="protractor">
22511        var favorite = element(by.binding('my.favorite'));
22512
22513        it('should initialize to model', function() {
22514          expect(favorite.getText()).toContain('unicorns');
22515        });
22516        it('should bind the values to the inputs', function() {
22517          element.all(by.model('my.favorite')).get(0).click();
22518          expect(favorite.getText()).toContain('pizza');
22519        });
22520      </file>
22521    </example>
22522 */
22523var ngValueDirective = function() {
22524  return {
22525    restrict: 'A',
22526    priority: 100,
22527    compile: function(tpl, tplAttr) {
22528      if (CONSTANT_VALUE_REGEXP.test(tplAttr.ngValue)) {
22529        return function ngValueConstantLink(scope, elm, attr) {
22530          attr.$set('value', scope.$eval(attr.ngValue));
22531        };
22532      } else {
22533        return function ngValueLink(scope, elm, attr) {
22534          scope.$watch(attr.ngValue, function valueWatchAction(value) {
22535            attr.$set('value', value);
22536          });
22537        };
22538      }
22539    }
22540  };
22541};
22542
22543/**
22544 * @ngdoc directive
22545 * @name ngBind
22546 * @restrict AC
22547 *
22548 * @description
22549 * The `ngBind` attribute tells Angular to replace the text content of the specified HTML element
22550 * with the value of a given expression, and to update the text content when the value of that
22551 * expression changes.
22552 *
22553 * Typically, you don't use `ngBind` directly, but instead you use the double curly markup like
22554 * `{{ expression }}` which is similar but less verbose.
22555 *
22556 * It is preferable to use `ngBind` instead of `{{ expression }}` if a template is momentarily
22557 * displayed by the browser in its raw state before Angular compiles it. Since `ngBind` is an
22558 * element attribute, it makes the bindings invisible to the user while the page is loading.
22559 *
22560 * An alternative solution to this problem would be using the
22561 * {@link ng.directive:ngCloak ngCloak} directive.
22562 *
22563 *
22564 * @element ANY
22565 * @param {expression} ngBind {@link guide/expression Expression} to evaluate.
22566 *
22567 * @example
22568 * Enter a name in the Live Preview text box; the greeting below the text box changes instantly.
22569   <example module="bindExample">
22570     <file name="index.html">
22571       <script>
22572         angular.module('bindExample', [])
22573           .controller('ExampleController', ['$scope', function($scope) {
22574             $scope.name = 'Whirled';
22575           }]);
22576       </script>
22577       <div ng-controller="ExampleController">
22578         <label>Enter name: <input type="text" ng-model="name"></label><br>
22579         Hello <span ng-bind="name"></span>!
22580       </div>
22581     </file>
22582     <file name="protractor.js" type="protractor">
22583       it('should check ng-bind', function() {
22584         var nameInput = element(by.model('name'));
22585
22586         expect(element(by.binding('name')).getText()).toBe('Whirled');
22587         nameInput.clear();
22588         nameInput.sendKeys('world');
22589         expect(element(by.binding('name')).getText()).toBe('world');
22590       });
22591     </file>
22592   </example>
22593 */
22594var ngBindDirective = ['$compile', function($compile) {
22595  return {
22596    restrict: 'AC',
22597    compile: function ngBindCompile(templateElement) {
22598      $compile.$$addBindingClass(templateElement);
22599      return function ngBindLink(scope, element, attr) {
22600        $compile.$$addBindingInfo(element, attr.ngBind);
22601        element = element[0];
22602        scope.$watch(attr.ngBind, function ngBindWatchAction(value) {
22603          element.textContent = isUndefined(value) ? '' : value;
22604        });
22605      };
22606    }
22607  };
22608}];
22609
22610
22611/**
22612 * @ngdoc directive
22613 * @name ngBindTemplate
22614 *
22615 * @description
22616 * The `ngBindTemplate` directive specifies that the element
22617 * text content should be replaced with the interpolation of the template
22618 * in the `ngBindTemplate` attribute.
22619 * Unlike `ngBind`, the `ngBindTemplate` can contain multiple `{{` `}}`
22620 * expressions. This directive is needed since some HTML elements
22621 * (such as TITLE and OPTION) cannot contain SPAN elements.
22622 *
22623 * @element ANY
22624 * @param {string} ngBindTemplate template of form
22625 *   <tt>{{</tt> <tt>expression</tt> <tt>}}</tt> to eval.
22626 *
22627 * @example
22628 * Try it here: enter text in text box and watch the greeting change.
22629   <example module="bindExample">
22630     <file name="index.html">
22631       <script>
22632         angular.module('bindExample', [])
22633           .controller('ExampleController', ['$scope', function($scope) {
22634             $scope.salutation = 'Hello';
22635             $scope.name = 'World';
22636           }]);
22637       </script>
22638       <div ng-controller="ExampleController">
22639        <label>Salutation: <input type="text" ng-model="salutation"></label><br>
22640        <label>Name: <input type="text" ng-model="name"></label><br>
22641        <pre ng-bind-template="{{salutation}} {{name}}!"></pre>
22642       </div>
22643     </file>
22644     <file name="protractor.js" type="protractor">
22645       it('should check ng-bind', function() {
22646         var salutationElem = element(by.binding('salutation'));
22647         var salutationInput = element(by.model('salutation'));
22648         var nameInput = element(by.model('name'));
22649
22650         expect(salutationElem.getText()).toBe('Hello World!');
22651
22652         salutationInput.clear();
22653         salutationInput.sendKeys('Greetings');
22654         nameInput.clear();
22655         nameInput.sendKeys('user');
22656
22657         expect(salutationElem.getText()).toBe('Greetings user!');
22658       });
22659     </file>
22660   </example>
22661 */
22662var ngBindTemplateDirective = ['$interpolate', '$compile', function($interpolate, $compile) {
22663  return {
22664    compile: function ngBindTemplateCompile(templateElement) {
22665      $compile.$$addBindingClass(templateElement);
22666      return function ngBindTemplateLink(scope, element, attr) {
22667        var interpolateFn = $interpolate(element.attr(attr.$attr.ngBindTemplate));
22668        $compile.$$addBindingInfo(element, interpolateFn.expressions);
22669        element = element[0];
22670        attr.$observe('ngBindTemplate', function(value) {
22671          element.textContent = isUndefined(value) ? '' : value;
22672        });
22673      };
22674    }
22675  };
22676}];
22677
22678
22679/**
22680 * @ngdoc directive
22681 * @name ngBindHtml
22682 *
22683 * @description
22684 * Evaluates the expression and inserts the resulting HTML into the element in a secure way. By default,
22685 * the resulting HTML content will be sanitized using the {@link ngSanitize.$sanitize $sanitize} service.
22686 * To utilize this functionality, ensure that `$sanitize` is available, for example, by including {@link
22687 * ngSanitize} in your module's dependencies (not in core Angular). In order to use {@link ngSanitize}
22688 * in your module's dependencies, you need to include "angular-sanitize.js" in your application.
22689 *
22690 * You may also bypass sanitization for values you know are safe. To do so, bind to
22691 * an explicitly trusted value via {@link ng.$sce#trustAsHtml $sce.trustAsHtml}.  See the example
22692 * under {@link ng.$sce#show-me-an-example-using-sce- Strict Contextual Escaping (SCE)}.
22693 *
22694 * Note: If a `$sanitize` service is unavailable and the bound value isn't explicitly trusted, you
22695 * will have an exception (instead of an exploit.)
22696 *
22697 * @element ANY
22698 * @param {expression} ngBindHtml {@link guide/expression Expression} to evaluate.
22699 *
22700 * @example
22701
22702   <example module="bindHtmlExample" deps="angular-sanitize.js">
22703     <file name="index.html">
22704       <div ng-controller="ExampleController">
22705        <p ng-bind-html="myHTML"></p>
22706       </div>
22707     </file>
22708
22709     <file name="script.js">
22710       angular.module('bindHtmlExample', ['ngSanitize'])
22711         .controller('ExampleController', ['$scope', function($scope) {
22712           $scope.myHTML =
22713              'I am an <code>HTML</code>string with ' +
22714              '<a href="#">links!</a> and other <em>stuff</em>';
22715         }]);
22716     </file>
22717
22718     <file name="protractor.js" type="protractor">
22719       it('should check ng-bind-html', function() {
22720         expect(element(by.binding('myHTML')).getText()).toBe(
22721             'I am an HTMLstring with links! and other stuff');
22722       });
22723     </file>
22724   </example>
22725 */
22726var ngBindHtmlDirective = ['$sce', '$parse', '$compile', function($sce, $parse, $compile) {
22727  return {
22728    restrict: 'A',
22729    compile: function ngBindHtmlCompile(tElement, tAttrs) {
22730      var ngBindHtmlGetter = $parse(tAttrs.ngBindHtml);
22731      var ngBindHtmlWatch = $parse(tAttrs.ngBindHtml, function getStringValue(value) {
22732        return (value || '').toString();
22733      });
22734      $compile.$$addBindingClass(tElement);
22735
22736      return function ngBindHtmlLink(scope, element, attr) {
22737        $compile.$$addBindingInfo(element, attr.ngBindHtml);
22738
22739        scope.$watch(ngBindHtmlWatch, function ngBindHtmlWatchAction() {
22740          // we re-evaluate the expr because we want a TrustedValueHolderType
22741          // for $sce, not a string
22742          element.html($sce.getTrustedHtml(ngBindHtmlGetter(scope)) || '');
22743        });
22744      };
22745    }
22746  };
22747}];
22748
22749/**
22750 * @ngdoc directive
22751 * @name ngChange
22752 *
22753 * @description
22754 * Evaluate the given expression when the user changes the input.
22755 * The expression is evaluated immediately, unlike the JavaScript onchange event
22756 * which only triggers at the end of a change (usually, when the user leaves the
22757 * form element or presses the return key).
22758 *
22759 * The `ngChange` expression is only evaluated when a change in the input value causes
22760 * a new value to be committed to the model.
22761 *
22762 * It will not be evaluated:
22763 * * if the value returned from the `$parsers` transformation pipeline has not changed
22764 * * if the input has continued to be invalid since the model will stay `null`
22765 * * if the model is changed programmatically and not by a change to the input value
22766 *
22767 *
22768 * Note, this directive requires `ngModel` to be present.
22769 *
22770 * @element input
22771 * @param {expression} ngChange {@link guide/expression Expression} to evaluate upon change
22772 * in input value.
22773 *
22774 * @example
22775 * <example name="ngChange-directive" module="changeExample">
22776 *   <file name="index.html">
22777 *     <script>
22778 *       angular.module('changeExample', [])
22779 *         .controller('ExampleController', ['$scope', function($scope) {
22780 *           $scope.counter = 0;
22781 *           $scope.change = function() {
22782 *             $scope.counter++;
22783 *           };
22784 *         }]);
22785 *     </script>
22786 *     <div ng-controller="ExampleController">
22787 *       <input type="checkbox" ng-model="confirmed" ng-change="change()" id="ng-change-example1" />
22788 *       <input type="checkbox" ng-model="confirmed" id="ng-change-example2" />
22789 *       <label for="ng-change-example2">Confirmed</label><br />
22790 *       <tt>debug = {{confirmed}}</tt><br/>
22791 *       <tt>counter = {{counter}}</tt><br/>
22792 *     </div>
22793 *   </file>
22794 *   <file name="protractor.js" type="protractor">
22795 *     var counter = element(by.binding('counter'));
22796 *     var debug = element(by.binding('confirmed'));
22797 *
22798 *     it('should evaluate the expression if changing from view', function() {
22799 *       expect(counter.getText()).toContain('0');
22800 *
22801 *       element(by.id('ng-change-example1')).click();
22802 *
22803 *       expect(counter.getText()).toContain('1');
22804 *       expect(debug.getText()).toContain('true');
22805 *     });
22806 *
22807 *     it('should not evaluate the expression if changing from model', function() {
22808 *       element(by.id('ng-change-example2')).click();
22809
22810 *       expect(counter.getText()).toContain('0');
22811 *       expect(debug.getText()).toContain('true');
22812 *     });
22813 *   </file>
22814 * </example>
22815 */
22816var ngChangeDirective = valueFn({
22817  restrict: 'A',
22818  require: 'ngModel',
22819  link: function(scope, element, attr, ctrl) {
22820    ctrl.$viewChangeListeners.push(function() {
22821      scope.$eval(attr.ngChange);
22822    });
22823  }
22824});
22825
22826function classDirective(name, selector) {
22827  name = 'ngClass' + name;
22828  return ['$animate', function($animate) {
22829    return {
22830      restrict: 'AC',
22831      link: function(scope, element, attr) {
22832        var oldVal;
22833
22834        scope.$watch(attr[name], ngClassWatchAction, true);
22835
22836        attr.$observe('class', function(value) {
22837          ngClassWatchAction(scope.$eval(attr[name]));
22838        });
22839
22840
22841        if (name !== 'ngClass') {
22842          scope.$watch('$index', function($index, old$index) {
22843            // jshint bitwise: false
22844            var mod = $index & 1;
22845            if (mod !== (old$index & 1)) {
22846              var classes = arrayClasses(scope.$eval(attr[name]));
22847              mod === selector ?
22848                addClasses(classes) :
22849                removeClasses(classes);
22850            }
22851          });
22852        }
22853
22854        function addClasses(classes) {
22855          var newClasses = digestClassCounts(classes, 1);
22856          attr.$addClass(newClasses);
22857        }
22858
22859        function removeClasses(classes) {
22860          var newClasses = digestClassCounts(classes, -1);
22861          attr.$removeClass(newClasses);
22862        }
22863
22864        function digestClassCounts(classes, count) {
22865          // Use createMap() to prevent class assumptions involving property
22866          // names in Object.prototype
22867          var classCounts = element.data('$classCounts') || createMap();
22868          var classesToUpdate = [];
22869          forEach(classes, function(className) {
22870            if (count > 0 || classCounts[className]) {
22871              classCounts[className] = (classCounts[className] || 0) + count;
22872              if (classCounts[className] === +(count > 0)) {
22873                classesToUpdate.push(className);
22874              }
22875            }
22876          });
22877          element.data('$classCounts', classCounts);
22878          return classesToUpdate.join(' ');
22879        }
22880
22881        function updateClasses(oldClasses, newClasses) {
22882          var toAdd = arrayDifference(newClasses, oldClasses);
22883          var toRemove = arrayDifference(oldClasses, newClasses);
22884          toAdd = digestClassCounts(toAdd, 1);
22885          toRemove = digestClassCounts(toRemove, -1);
22886          if (toAdd && toAdd.length) {
22887            $animate.addClass(element, toAdd);
22888          }
22889          if (toRemove && toRemove.length) {
22890            $animate.removeClass(element, toRemove);
22891          }
22892        }
22893
22894        function ngClassWatchAction(newVal) {
22895          if (selector === true || scope.$index % 2 === selector) {
22896            var newClasses = arrayClasses(newVal || []);
22897            if (!oldVal) {
22898              addClasses(newClasses);
22899            } else if (!equals(newVal,oldVal)) {
22900              var oldClasses = arrayClasses(oldVal);
22901              updateClasses(oldClasses, newClasses);
22902            }
22903          }
22904          oldVal = shallowCopy(newVal);
22905        }
22906      }
22907    };
22908
22909    function arrayDifference(tokens1, tokens2) {
22910      var values = [];
22911
22912      outer:
22913      for (var i = 0; i < tokens1.length; i++) {
22914        var token = tokens1[i];
22915        for (var j = 0; j < tokens2.length; j++) {
22916          if (token == tokens2[j]) continue outer;
22917        }
22918        values.push(token);
22919      }
22920      return values;
22921    }
22922
22923    function arrayClasses(classVal) {
22924      var classes = [];
22925      if (isArray(classVal)) {
22926        forEach(classVal, function(v) {
22927          classes = classes.concat(arrayClasses(v));
22928        });
22929        return classes;
22930      } else if (isString(classVal)) {
22931        return classVal.split(' ');
22932      } else if (isObject(classVal)) {
22933        forEach(classVal, function(v, k) {
22934          if (v) {
22935            classes = classes.concat(k.split(' '));
22936          }
22937        });
22938        return classes;
22939      }
22940      return classVal;
22941    }
22942  }];
22943}
22944
22945/**
22946 * @ngdoc directive
22947 * @name ngClass
22948 * @restrict AC
22949 *
22950 * @description
22951 * The `ngClass` directive allows you to dynamically set CSS classes on an HTML element by databinding
22952 * an expression that represents all classes to be added.
22953 *
22954 * The directive operates in three different ways, depending on which of three types the expression
22955 * evaluates to:
22956 *
22957 * 1. If the expression evaluates to a string, the string should be one or more space-delimited class
22958 * names.
22959 *
22960 * 2. If the expression evaluates to an object, then for each key-value pair of the
22961 * object with a truthy value the corresponding key is used as a class name.
22962 *
22963 * 3. If the expression evaluates to an array, each element of the array should either be a string as in
22964 * type 1 or an object as in type 2. This means that you can mix strings and objects together in an array
22965 * to give you more control over what CSS classes appear. See the code below for an example of this.
22966 *
22967 *
22968 * The directive won't add duplicate classes if a particular class was already set.
22969 *
22970 * When the expression changes, the previously added classes are removed and only then are the
22971 * new classes added.
22972 *
22973 * @animations
22974 * **add** - happens just before the class is applied to the elements
22975 *
22976 * **remove** - happens just before the class is removed from the element
22977 *
22978 * @element ANY
22979 * @param {expression} ngClass {@link guide/expression Expression} to eval. The result
22980 *   of the evaluation can be a string representing space delimited class
22981 *   names, an array, or a map of class names to boolean values. In the case of a map, the
22982 *   names of the properties whose values are truthy will be added as css classes to the
22983 *   element.
22984 *
22985 * @example Example that demonstrates basic bindings via ngClass directive.
22986   <example>
22987     <file name="index.html">
22988       <p ng-class="{strike: deleted, bold: important, 'has-error': error}">Map Syntax Example</p>
22989       <label>
22990          <input type="checkbox" ng-model="deleted">
22991          deleted (apply "strike" class)
22992       </label><br>
22993       <label>
22994          <input type="checkbox" ng-model="important">
22995          important (apply "bold" class)
22996       </label><br>
22997       <label>
22998          <input type="checkbox" ng-model="error">
22999          error (apply "has-error" class)
23000       </label>
23001       <hr>
23002       <p ng-class="style">Using String Syntax</p>
23003       <input type="text" ng-model="style"
23004              placeholder="Type: bold strike red" aria-label="Type: bold strike red">
23005       <hr>
23006       <p ng-class="[style1, style2, style3]">Using Array Syntax</p>
23007       <input ng-model="style1"
23008              placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red"><br>
23009       <input ng-model="style2"
23010              placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red 2"><br>
23011       <input ng-model="style3"
23012              placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red 3"><br>
23013       <hr>
23014       <p ng-class="[style4, {orange: warning}]">Using Array and Map Syntax</p>
23015       <input ng-model="style4" placeholder="Type: bold, strike" aria-label="Type: bold, strike"><br>
23016       <label><input type="checkbox" ng-model="warning"> warning (apply "orange" class)</label>
23017     </file>
23018     <file name="style.css">
23019       .strike {
23020           text-decoration: line-through;
23021       }
23022       .bold {
23023           font-weight: bold;
23024       }
23025       .red {
23026           color: red;
23027       }
23028       .has-error {
23029           color: red;
23030           background-color: yellow;
23031       }
23032       .orange {
23033           color: orange;
23034       }
23035     </file>
23036     <file name="protractor.js" type="protractor">
23037       var ps = element.all(by.css('p'));
23038
23039       it('should let you toggle the class', function() {
23040
23041         expect(ps.first().getAttribute('class')).not.toMatch(/bold/);
23042         expect(ps.first().getAttribute('class')).not.toMatch(/has-error/);
23043
23044         element(by.model('important')).click();
23045         expect(ps.first().getAttribute('class')).toMatch(/bold/);
23046
23047         element(by.model('error')).click();
23048         expect(ps.first().getAttribute('class')).toMatch(/has-error/);
23049       });
23050
23051       it('should let you toggle string example', function() {
23052         expect(ps.get(1).getAttribute('class')).toBe('');
23053         element(by.model('style')).clear();
23054         element(by.model('style')).sendKeys('red');
23055         expect(ps.get(1).getAttribute('class')).toBe('red');
23056       });
23057
23058       it('array example should have 3 classes', function() {
23059         expect(ps.get(2).getAttribute('class')).toBe('');
23060         element(by.model('style1')).sendKeys('bold');
23061         element(by.model('style2')).sendKeys('strike');
23062         element(by.model('style3')).sendKeys('red');
23063         expect(ps.get(2).getAttribute('class')).toBe('bold strike red');
23064       });
23065
23066       it('array with map example should have 2 classes', function() {
23067         expect(ps.last().getAttribute('class')).toBe('');
23068         element(by.model('style4')).sendKeys('bold');
23069         element(by.model('warning')).click();
23070         expect(ps.last().getAttribute('class')).toBe('bold orange');
23071       });
23072     </file>
23073   </example>
23074
23075   ## Animations
23076
23077   The example below demonstrates how to perform animations using ngClass.
23078
23079   <example module="ngAnimate" deps="angular-animate.js" animations="true">
23080     <file name="index.html">
23081      <input id="setbtn" type="button" value="set" ng-click="myVar='my-class'">
23082      <input id="clearbtn" type="button" value="clear" ng-click="myVar=''">
23083      <br>
23084      <span class="base-class" ng-class="myVar">Sample Text</span>
23085     </file>
23086     <file name="style.css">
23087       .base-class {
23088         transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
23089       }
23090
23091       .base-class.my-class {
23092         color: red;
23093         font-size:3em;
23094       }
23095     </file>
23096     <file name="protractor.js" type="protractor">
23097       it('should check ng-class', function() {
23098         expect(element(by.css('.base-class')).getAttribute('class')).not.
23099           toMatch(/my-class/);
23100
23101         element(by.id('setbtn')).click();
23102
23103         expect(element(by.css('.base-class')).getAttribute('class')).
23104           toMatch(/my-class/);
23105
23106         element(by.id('clearbtn')).click();
23107
23108         expect(element(by.css('.base-class')).getAttribute('class')).not.
23109           toMatch(/my-class/);
23110       });
23111     </file>
23112   </example>
23113
23114
23115   ## ngClass and pre-existing CSS3 Transitions/Animations
23116   The ngClass directive still supports CSS3 Transitions/Animations even if they do not follow the ngAnimate CSS naming structure.
23117   Upon animation ngAnimate will apply supplementary CSS classes to track the start and end of an animation, but this will not hinder
23118   any pre-existing CSS transitions already on the element. To get an idea of what happens during a class-based animation, be sure
23119   to view the step by step details of {@link $animate#addClass $animate.addClass} and
23120   {@link $animate#removeClass $animate.removeClass}.
23121 */
23122var ngClassDirective = classDirective('', true);
23123
23124/**
23125 * @ngdoc directive
23126 * @name ngClassOdd
23127 * @restrict AC
23128 *
23129 * @description
23130 * The `ngClassOdd` and `ngClassEven` directives work exactly as
23131 * {@link ng.directive:ngClass ngClass}, except they work in
23132 * conjunction with `ngRepeat` and take effect only on odd (even) rows.
23133 *
23134 * This directive can be applied only within the scope of an
23135 * {@link ng.directive:ngRepeat ngRepeat}.
23136 *
23137 * @element ANY
23138 * @param {expression} ngClassOdd {@link guide/expression Expression} to eval. The result
23139 *   of the evaluation can be a string representing space delimited class names or an array.
23140 *
23141 * @example
23142   <example>
23143     <file name="index.html">
23144        <ol ng-init="names=['John', 'Mary', 'Cate', 'Suz']">
23145          <li ng-repeat="name in names">
23146           <span ng-class-odd="'odd'" ng-class-even="'even'">
23147             {{name}}
23148           </span>
23149          </li>
23150        </ol>
23151     </file>
23152     <file name="style.css">
23153       .odd {
23154         color: red;
23155       }
23156       .even {
23157         color: blue;
23158       }
23159     </file>
23160     <file name="protractor.js" type="protractor">
23161       it('should check ng-class-odd and ng-class-even', function() {
23162         expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')).
23163           toMatch(/odd/);
23164         expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')).
23165           toMatch(/even/);
23166       });
23167     </file>
23168   </example>
23169 */
23170var ngClassOddDirective = classDirective('Odd', 0);
23171
23172/**
23173 * @ngdoc directive
23174 * @name ngClassEven
23175 * @restrict AC
23176 *
23177 * @description
23178 * The `ngClassOdd` and `ngClassEven` directives work exactly as
23179 * {@link ng.directive:ngClass ngClass}, except they work in
23180 * conjunction with `ngRepeat` and take effect only on odd (even) rows.
23181 *
23182 * This directive can be applied only within the scope of an
23183 * {@link ng.directive:ngRepeat ngRepeat}.
23184 *
23185 * @element ANY
23186 * @param {expression} ngClassEven {@link guide/expression Expression} to eval. The
23187 *   result of the evaluation can be a string representing space delimited class names or an array.
23188 *
23189 * @example
23190   <example>
23191     <file name="index.html">
23192        <ol ng-init="names=['John', 'Mary', 'Cate', 'Suz']">
23193          <li ng-repeat="name in names">
23194           <span ng-class-odd="'odd'" ng-class-even="'even'">
23195             {{name}} &nbsp; &nbsp; &nbsp;
23196           </span>
23197          </li>
23198        </ol>
23199     </file>
23200     <file name="style.css">
23201       .odd {
23202         color: red;
23203       }
23204       .even {
23205         color: blue;
23206       }
23207     </file>
23208     <file name="protractor.js" type="protractor">
23209       it('should check ng-class-odd and ng-class-even', function() {
23210         expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')).
23211           toMatch(/odd/);
23212         expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')).
23213           toMatch(/even/);
23214       });
23215     </file>
23216   </example>
23217 */
23218var ngClassEvenDirective = classDirective('Even', 1);
23219
23220/**
23221 * @ngdoc directive
23222 * @name ngCloak
23223 * @restrict AC
23224 *
23225 * @description
23226 * The `ngCloak` directive is used to prevent the Angular html template from being briefly
23227 * displayed by the browser in its raw (uncompiled) form while your application is loading. Use this
23228 * directive to avoid the undesirable flicker effect caused by the html template display.
23229 *
23230 * The directive can be applied to the `<body>` element, but the preferred usage is to apply
23231 * multiple `ngCloak` directives to small portions of the page to permit progressive rendering
23232 * of the browser view.
23233 *
23234 * `ngCloak` works in cooperation with the following css rule embedded within `angular.js` and
23235 * `angular.min.js`.
23236 * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
23237 *
23238 * ```css
23239 * [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
23240 *   display: none !important;
23241 * }
23242 * ```
23243 *
23244 * When this css rule is loaded by the browser, all html elements (including their children) that
23245 * are tagged with the `ngCloak` directive are hidden. When Angular encounters this directive
23246 * during the compilation of the template it deletes the `ngCloak` element attribute, making
23247 * the compiled element visible.
23248 *
23249 * For the best result, the `angular.js` script must be loaded in the head section of the html
23250 * document; alternatively, the css rule above must be included in the external stylesheet of the
23251 * application.
23252 *
23253 * @element ANY
23254 *
23255 * @example
23256   <example>
23257     <file name="index.html">
23258        <div id="template1" ng-cloak>{{ 'hello' }}</div>
23259        <div id="template2" class="ng-cloak">{{ 'world' }}</div>
23260     </file>
23261     <file name="protractor.js" type="protractor">
23262       it('should remove the template directive and css class', function() {
23263         expect($('#template1').getAttribute('ng-cloak')).
23264           toBeNull();
23265         expect($('#template2').getAttribute('ng-cloak')).
23266           toBeNull();
23267       });
23268     </file>
23269   </example>
23270 *
23271 */
23272var ngCloakDirective = ngDirective({
23273  compile: function(element, attr) {
23274    attr.$set('ngCloak', undefined);
23275    element.removeClass('ng-cloak');
23276  }
23277});
23278
23279/**
23280 * @ngdoc directive
23281 * @name ngController
23282 *
23283 * @description
23284 * The `ngController` directive attaches a controller class to the view. This is a key aspect of how angular
23285 * supports the principles behind the Model-View-Controller design pattern.
23286 *
23287 * MVC components in angular:
23288 *
23289 * * Model — Models are the properties of a scope; scopes are attached to the DOM where scope properties
23290 *   are accessed through bindings.
23291 * * View — The template (HTML with data bindings) that is rendered into the View.
23292 * * Controller — The `ngController` directive specifies a Controller class; the class contains business
23293 *   logic behind the application to decorate the scope with functions and values
23294 *
23295 * Note that you can also attach controllers to the DOM by declaring it in a route definition
23296 * via the {@link ngRoute.$route $route} service. A common mistake is to declare the controller
23297 * again using `ng-controller` in the template itself.  This will cause the controller to be attached
23298 * and executed twice.
23299 *
23300 * @element ANY
23301 * @scope
23302 * @priority 500
23303 * @param {expression} ngController Name of a constructor function registered with the current
23304 * {@link ng.$controllerProvider $controllerProvider} or an {@link guide/expression expression}
23305 * that on the current scope evaluates to a constructor function.
23306 *
23307 * The controller instance can be published into a scope property by specifying
23308 * `ng-controller="as propertyName"`.
23309 *
23310 * If the current `$controllerProvider` is configured to use globals (via
23311 * {@link ng.$controllerProvider#allowGlobals `$controllerProvider.allowGlobals()` }), this may
23312 * also be the name of a globally accessible constructor function (not recommended).
23313 *
23314 * @example
23315 * Here is a simple form for editing user contact information. Adding, remov
23315ing, clearing, and
23316 * greeting are methods declared on the controller (see source tab). These methods can
23317 * easily be called from the angular markup. Any changes to the data are automatically reflected
23318 * in the View without the need for a manual update.
23319 *
23320 * Two different declaration styles are included below:
23321 *
23322 * * one binds methods and properties directly onto the controller using `this`:
23323 * `ng-controller="SettingsController1 as settings"`
23324 * * one injects `$scope` into the controller:
23325 * `ng-controller="SettingsController2"`
23326 *
23327 * The second option is more common in the Angular community, and is generally used in boilerplates
23328 * and in this guide. However, there are advantages to binding properties directly to the controller
23329 * and avoiding scope.
23330 *
23331 * * Using `controller as` makes it obvious which controller you are accessing in the template when
23332 * multiple controllers apply to an element.
23333 * * If you are writing your controllers as classes you have easier access to the properties and
23334 * methods, which will appear on the scope, from inside the controller code.
23335 * * Since there is always a `.` in the bindings, you don't have to worry about prototypal
23336 * inheritance masking primitives.
23337 *
23338 * This example demonstrates the `controller as` syntax.
23339 *
23340 * <example name="ngControllerAs" module="controllerAsExample">
23341 *   <file name="index.html">
23342 *    <div id="ctrl-as-exmpl" ng-controller="SettingsController1 as settings">
23343 *      <label>Name: <input type="text" ng-model="settings.name"/></label>
23344 *      <button ng-click="settings.greet()">greet</button><br/>
23345 *      Contact:
23346 *      <ul>
23347 *        <li ng-repeat="contact in settings.contacts">
23348 *          <select ng-model="contact.type" aria-label="Contact method" id="select_{{$index}}">
23349 *             <option>phone</option>
23350 *             <option>email</option>
23351 *          </select>
23352 *          <input type="text" ng-model="contact.value" aria-labelledby="select_{{$index}}" />
23353 *          <button ng-click="settings.clearContact(contact)">clear</button>
23354 *          <button ng-click="settings.removeContact(contact)" aria-label="Remove">X</button>
23355 *        </li>
23356 *        <li><button ng-click="settings.addContact()">add</button></li>
23357 *     </ul>
23358 *    </div>
23359 *   </file>
23360 *   <file name="app.js">
23361 *    angular.module('controllerAsExample', [])
23362 *      .controller('SettingsController1', SettingsController1);
23363 *
23364 *    function SettingsController1() {
23365 *      this.name = "John Smith";
23366 *      this.contacts = [
23367 *        {type: 'phone', value: '408 555 1212'},
23368 *        {type: 'email', value: '[email protected]'} ];
23369 *    }
23370 *
23371 *    SettingsController1.prototype.greet = function() {
23372 *      alert(this.name);
23373 *    };
23374 *
23375 *    SettingsController1.prototype.addContact = function() {
23376 *      this.contacts.push({type: 'email', value: '[email protected]'});
23377 *    };
23378 *
23379 *    SettingsController1.prototype.removeContact = function(contactToRemove) {
23380 *     var index = this.contacts.indexOf(contactToRemove);
23381 *      this.contacts.splice(index, 1);
23382 *    };
23383 *
23384 *    SettingsController1.prototype.clearContact = function(contact) {
23385 *      contact.type = 'phone';
23386 *      contact.value = '';
23387 *    };
23388 *   </file>
23389 *   <file name="protractor.js" type="protractor">
23390 *     it('should check controller as', function() {
23391 *       var container = element(by.id('ctrl-as-exmpl'));
23392 *         expect(container.element(by.model('settings.name'))
23393 *           .getAttribute('value')).toBe('John Smith');
23394 *
23395 *       var firstRepeat =
23396 *           container.element(by.repeater('contact in settings.contacts').row(0));
23397 *       var secondRepeat =
23398 *           container.element(by.repeater('contact in settings.contacts').row(1));
23399 *
23400 *       expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
23401 *           .toBe('408 555 1212');
23402 *
23403 *       expect(secondRepeat.element(by.model('contact.value')).getAttribute('value'))
23404 *           .toBe('[email protected]');
23405 *
23406 *       firstRepeat.element(by.buttonText('clear')).click();
23407 *
23408 *       expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
23409 *           .toBe('');
23410 *
23411 *       container.element(by.buttonText('add')).click();
23412 *
23413 *       expect(container.element(by.repeater('contact in settings.contacts').row(2))
23414 *           .element(by.model('contact.value'))
23415 *           .getAttribute('value'))
23416 *           .toBe('[email protected]');
23417 *     });
23418 *   </file>
23419 * </example>
23420 *
23421 * This example demonstrates the "attach to `$scope`" style of controller.
23422 *
23423 * <example name="ngController" module="controllerExample">
23424 *  <file name="index.html">
23425 *   <div id="ctrl-exmpl" ng-controller="SettingsController2">
23426 *     <label>Name: <input type="text" ng-model="name"/></label>
23427 *     <button ng-click="greet()">greet</button><br/>
23428 *     Contact:
23429 *     <ul>
23430 *       <li ng-repeat="contact in contacts">
23431 *         <select ng-model="contact.type" id="select_{{$index}}">
23432 *            <option>phone</option>
23433 *            <option>email</option>
23434 *         </select>
23435 *         <input type="text" ng-model="contact.value" aria-labelledby="select_{{$index}}" />
23436 *         <button ng-click="clearContact(contact)">clear</button>
23437 *         <button ng-click="removeContact(contact)">X</button>
23438 *       </li>
23439 *       <li>[ <button ng-click="addContact()">add</button> ]</li>
23440 *    </ul>
23441 *   </div>
23442 *  </file>
23443 *  <file name="app.js">
23444 *   angular.module('controllerExample', [])
23445 *     .controller('SettingsController2', ['$scope', SettingsController2]);
23446 *
23447 *   function SettingsController2($scope) {
23448 *     $scope.name = "John Smith";
23449 *     $scope.contacts = [
23450 *       {type:'phone', value:'408 555 1212'},
23451 *       {type:'email', value:'[email protected]'} ];
23452 *
23453 *     $scope.greet = function() {
23454 *       alert($scope.name);
23455 *     };
23456 *
23457 *     $scope.addContact = function() {
23458 *       $scope.contacts.push({type:'email', value:'[email protected]'});
23459 *     };
23460 *
23461 *     $scope.removeContact = function(contactToRemove) {
23462 *       var index = $scope.contacts.indexOf(contactToRemove);
23463 *       $scope.contacts.splice(index, 1);
23464 *     };
23465 *
23466 *     $scope.clearContact = function(contact) {
23467 *       contact.type = 'phone';
23468 *       contact.value = '';
23469 *     };
23470 *   }
23471 *  </file>
23472 *  <file name="protractor.js" type="protractor">
23473 *    it('should check controller', function() {
23474 *      var container = element(by.id('ctrl-exmpl'));
23475 *
23476 *      expect(container.element(by.model('name'))
23477 *          .getAttribute('value')).toBe('John Smith');
23478 *
23479 *      var firstRepeat =
23480 *          container.element(by.repeater('contact in contacts').row(0));
23481 *      var secondRepeat =
23482 *          container.element(by.repeater('contact in contacts').row(1));
23483 *
23484 *      expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
23485 *          .toBe('408 555 1212');
23486 *      expect(secondRepeat.element(by.model('contact.value')).getAttribute('value'))
23487 *          .toBe('[email protected]');
23488 *
23489 *      firstRepeat.element(by.buttonText('clear')).click();
23490 *
23491 *      expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
23492 *          .toBe('');
23493 *
23494 *      container.element(by.buttonText('add')).click();
23495 *
23496 *      expect(container.element(by.repeater('contact in contacts').row(2))
23497 *          .element(by.model('contact.value'))
23498 *          .getAttribute('value'))
23499 *          .toBe('[email protected]');
23500 *    });
23501 *  </file>
23502 *</example>
23503
23504 */
23505var ngControllerDirective = [function() {
23506  return {
23507    restrict: 'A',
23508    scope: true,
23509    controller: '@',
23510    priority: 500
23511  };
23512}];
23513
23514/**
23515 * @ngdoc directive
23516 * @name ngCsp
23517 *
23518 * @element html
23519 * @description
23520 *
23521 * Angular has some features that can break certain
23522 * [CSP (Content Security Policy)](https://developer.mozilla.org/en/Security/CSP) rules.
23523 *
23524 * If you intend to implement these rules then you must tell Angular not to use these features.
23525 *
23526 * This is necessary when developing things like Google Chrome Extensions or Universal Windows Apps.
23527 *
23528 *
23529 * The following rules affect Angular:
23530 *
23531 * * `unsafe-eval`: this rule forbids apps to use `eval` or `Function(string)` generated functions
23532 * (among other things). Angular makes use of this in the {@link $parse} service to provide a 30%
23533 * increase in the speed of evaluating Angular expressions.
23534 *
23535 * * `unsafe-inline`: this rule forbids apps from inject custom styles into the document. Angular
23536 * makes use of this to include some CSS rules (e.g. {@link ngCloak} and {@link ngHide}).
23537 * To make these directives work when a CSP rule is blocking inline styles, you must link to the
23538 * `angular-csp.css` in your HTML manually.
23539 *
23540 * If you do not provide `ngCsp` then Angular tries to autodetect if CSP is blocking unsafe-eval
23541 * and automatically deactivates this feature in the {@link $parse} service. This autodetection,
23542 * however, triggers a CSP error to be logged in the console:
23543 *
23544 * ```
23545 * Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of
23546 * script in the following Content Security Policy directive: "default-src 'self'". Note that
23547 * 'script-src' was not explicitly set, so 'default-src' is used as a fallback.
23548 * ```
23549 *
23550 * This error is harmless but annoying. To prevent the error from showing up, put the `ngCsp`
23551 * directive on an element of the HTML document that appears before the `<script>` tag that loads
23552 * the `angular.js` file.
23553 *
23554 * *Note: This directive is only available in the `ng-csp` and `data-ng-csp` attribute form.*
23555 *
23556 * You can specify which of the CSP related Angular features should be deactivated by providing
23557 * a value for the `ng-csp` attribute. The options are as follows:
23558 *
23559 * * no-inline-style: this stops Angular from injecting CSS styles into the DOM
23560 *
23561 * * no-unsafe-eval: this stops Angular from optimising $parse with unsafe eval of strings
23562 *
23563 * You can use these values in the following combinations:
23564 *
23565 *
23566 * * No declaration means that Angular will assume that you can do inline styles, but it will do
23567 * a runtime check for unsafe-eval. E.g. `<body>`. This is backwardly compatible with previous versions
23568 * of Angular.
23569 *
23570 * * A simple `ng-csp` (or `data-ng-csp`) attribute will tell Angular to deactivate both inline
23571 * styles and unsafe eval. E.g. `<body ng-csp>`. This is backwardly compatible with previous versions
23572 * of Angular.
23573 *
23574 * * Specifying only `no-unsafe-eval` tells Angular that we must not use eval, but that we can inject
23575 * inline styles. E.g. `<body ng-csp="no-unsafe-eval">`.
23576 *
23577 * * Specifying only `no-inline-style` tells Angular that we must not inject styles, but that we can
23578 * run eval - no automcatic check for unsafe eval will occur. E.g. `<body ng-csp="no-inline-style">`
23579 *
23580 * * Specifying both `no-unsafe-eval` and `no-inline-style` tells Angular that we must not inject
23581 * styles nor use eval, which is the same as an empty: ng-csp.
23582 * E.g.`<body ng-csp="no-inline-style;no-unsafe-eval">`
23583 *
23584 * @example
23585 * This example shows how to apply the `ngCsp` directive to the `html` tag.
23586   ```html
23587     <!doctype html>
23588     <html ng-app ng-csp>
23589     ...
23590     ...
23591     </html>
23592   ```
23593  * @example
23594      // Note: the suffix `.csp` in the example name triggers
23595      // csp mode in our http server!
23596      <example name="example.csp" module="cspExample" ng-csp="true">
23597        <file name="index.html">
23598          <div ng-controller="MainController as ctrl">
23599            <div>
23600              <button ng-click="ctrl.inc()" id="inc">Increment</button>
23601              <span id="counter">
23602                {{ctrl.counter}}
23603              </span>
23604            </div>
23605
23606            <div>
23607              <button ng-click="ctrl.evil()" id="evil">Evil</button>
23608              <span id="evilError">
23609                {{ctrl.evilError}}
23610              </span>
23611            </div>
23612          </div>
23613        </file>
23614        <file name="script.js">
23615           angular.module('cspExample', [])
23616             .controller('MainController', function() {
23617                this.counter = 0;
23618                this.inc = function() {
23619                  this.counter++;
23620                };
23621                this.evil = function() {
23622                  // jshint evil:true
23623                  try {
23624                    eval('1+2');
23625                  } catch (e) {
23626                    this.evilError = e.message;
23627                  }
23628                };
23629              });
23630        </file>
23631        <file name="protractor.js" type="protractor">
23632          var util, webdriver;
23633
23634          var incBtn = element(by.id('inc'));
23635          var counter = element(by.id('counter'));
23636          var evilBtn = element(by.id('evil'));
23637          var evilError = element(by.id('evilError'));
23638
23639          function getAndClearSevereErrors() {
23640            return browser.manage().logs().get('browser').then(function(browserLog) {
23641              return browserLog.filter(function(logEntry) {
23642                return logEntry.level.value > webdriver.logging.Level.WARNING.value;
23643              });
23644            });
23645          }
23646
23647          function clearErrors() {
23648            getAndClearSevereErrors();
23649          }
23650
23651          function expectNoErrors() {
23652            getAndClearSevereErrors().then(function(filteredLog) {
23653              expect(filteredLog.length).toEqual(0);
23654              if (filteredLog.length) {
23655                console.log('browser console errors: ' + util.inspect(filteredLog));
23656              }
23657            });
23658          }
23659
23660          function expectError(regex) {
23661            getAndClearSevereErrors().then(function(filteredLog) {
23662              var found = false;
23663              filteredLog.forEach(function(log) {
23664                if (log.message.match(regex)) {
23665                  found = true;
23666                }
23667              });
23668              if (!found) {
23669                throw new Error('expected an error that matches ' + regex);
23670              }
23671            });
23672          }
23673
23674          beforeEach(function() {
23675            util = require('util');
23676            webdriver = require('protractor/node_modules/selenium-webdriver');
23677          });
23678
23679          // For now, we only test on Chrome,
23680          // as Safari does not load the page with Protractor's injected scripts,
23681          // and Firefox webdriver always disables content security policy (#6358)
23682          if (browser.params.browser !== 'chrome') {
23683            return;
23684          }
23685
23686          it('should not report errors when the page is loaded', function() {
23687            // clear errors so we are not dependent on previous tests
23688            clearErrors();
23689            // Need to reload the page as the page is already loaded when
23690            // we come here
23691            browser.driver.getCurrentUrl().then(function(url) {
23692              browser.get(url);
23693            });
23694            expectNoErrors();
23695          });
23696
23697          it('should evaluate expressions', function() {
23698            expect(counter.getText()).toEqual('0');
23699            incBtn.click();
23700            expect(counter.getText()).toEqual('1');
23701            expectNoErrors();
23702          });
23703
23704          it('should throw and report an error when using "eval"', function() {
23705            evilBtn.click();
23706            expect(evilError.getText()).toMatch(/Content Security Policy/);
23707            expectError(/Content Security Policy/);
23708          });
23709        </file>
23710      </example>
23711  */
23712
23713// ngCsp is not implemented as a proper directive any more, because we need it be processed while we
23714// bootstrap the system (before $parse is instantiated), for this reason we just have
23715// the csp() fn that looks for the `ng-csp` attribute anywhere in the current doc
23716
23717/**
23718 * @ngdoc directive
23719 * @name ngClick
23720 *
23721 * @description
23722 * The ngClick directive allows you to specify custom behavior when
23723 * an element is clicked.
23724 *
23725 * @element ANY
23726 * @priority 0
23727 * @param {expression} ngClick {@link guide/expression Expression} to evaluate upon
23728 * click. ({@link guide/expression#-event- Event object is available as `$event`})
23729 *
23730 * @example
23731   <example>
23732     <file name="index.html">
23733      <button ng-click="count = count + 1" ng-init="count=0">
23734        Increment
23735      </button>
23736      <span>
23737        count: {{count}}
23738      </span>
23739     </file>
23740     <file name="protractor.js" type="protractor">
23741       it('should check ng-click', function() {
23742         expect(element(by.binding('count')).getText()).toMatch('0');
23743         element(by.css('button')).click();
23744         expect(element(by.binding('count')).getText()).toMatch('1');
23745       });
23746     </file>
23747   </example>
23748 */
23749/*
23750 * A collection of directives that allows creation of custom event handlers that are defined as
23751 * angular expressions and are compiled and executed within the current scope.
23752 */
23753var ngEventDirectives = {};
23754
23755// For events that might fire synchronously during DOM manipulation
23756// we need to execute their event handlers asynchronously using $evalAsync,
23757// so that they are not executed in an inconsistent state.
23758var forceAsyncEvents = {
23759  'blur': true,
23760  'focus': true
23761};
23762forEach(
23763  'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste'.split(' '),
23764  function(eventName) {
23765    var directiveName = directiveNormalize('ng-' + eventName);
23766    ngEventDirectives[directiveName] = ['$parse', '$rootScope', function($parse, $rootScope) {
23767      return {
23768        restrict: 'A',
23769        compile: function($element, attr) {
23770          // We expose the powerful $event object on the scope that provides access to the Window,
23771          // etc. that isn't protected by the fast paths in $parse.  We explicitly request better
23772          // checks at the cost of speed since event handler expressions are not executed as
23773          // frequently as regular change detection.
23774          var fn = $parse(attr[directiveName], /* interceptorFn */ null, /* expensiveChecks */ true);
23775          return function ngEventHandler(scope, element) {
23776            element.on(eventName, function(event) {
23777              var callback = function() {
23778                fn(scope, {$event:event});
23779              };
23780              if (forceAsyncEvents[eventName] && $rootScope.$$phase) {
23781                scope.$evalAsync(callback);
23782              } else {
23783                scope.$apply(callback);
23784              }
23785            });
23786          };
23787        }
23788      };
23789    }];
23790  }
23791);
23792
23793/**
23794 * @ngdoc directive
23795 * @name ngDblclick
23796 *
23797 * @description
23798 * The `ngDblclick` directive allows you to specify custom behavior on a dblclick event.
23799 *
23800 * @element ANY
23801 * @priority 0
23802 * @param {expression} ngDblclick {@link guide/expression Expression} to evaluate upon
23803 * a dblclick. (The Event object is available as `$event`)
23804 *
23805 * @example
23806   <example>
23807     <file name="index.html">
23808      <button ng-dblclick="count = count + 1" ng-init="count=0">
23809        Increment (on double click)
23810      </button>
23811      count: {{count}}
23812     </file>
23813   </example>
23814 */
23815
23816
23817/**
23818 * @ngdoc directive
23819 * @name ngMousedown
23820 *
23821 * @description
23822 * The ngMousedown directive allows you to specify custom behavior on mousedown event.
23823 *
23824 * @element ANY
23825 * @priority 0
23826 * @param {expression} ngMousedown {@link guide/expression Expression} to evaluate upon
23827 * mousedown. ({@link guide/expression#-event- Event object is available as `$event`})
23828 *
23829 * @example
23830   <example>
23831     <file name="index.html">
23832      <button ng-mousedown="count = count + 1" ng-init="count=0">
23833        Increment (on mouse down)
23834      </button>
23835      count: {{count}}
23836     </file>
23837   </example>
23838 */
23839
23840
23841/**
23842 * @ngdoc directive
23843 * @name ngMouseup
23844 *
23845 * @description
23846 * Specify custom behavior on mouseup event.
23847 *
23848 * @element ANY
23849 * @priority 0
23850 * @param {expression} ngMouseup {@link guide/expression Expression} to evaluate upon
23851 * mouseup. ({@link guide/expression#-event- Event object is available as `$event`})
23852 *
23853 * @example
23854   <example>
23855     <file name="index.html">
23856      <button ng-mouseup="count = count + 1" ng-init="count=0">
23857        Increment (on mouse up)
23858      </button>
23859      count: {{count}}
23860     </file>
23861   </example>
23862 */
23863
23864/**
23865 * @ngdoc directive
23866 * @name ngMouseover
23867 *
23868 * @description
23869 * Specify custom behavior on mouseover event.
23870 *
23871 * @element ANY
23872 * @priority 0
23873 * @param {expression} ngMouseover {@link guide/expression Expression} to evaluate upon
23874 * mouseover. ({@link guide/expression#-event- Event object is available as `$event`})
23875 *
23876 * @example
23877   <example>
23878     <file name="index.html">
23879      <button ng-mouseover="count = count + 1" ng-init="count=0">
23880        Increment (when mouse is over)
23881      </button>
23882      count: {{count}}
23883     </file>
23884   </example>
23885 */
23886
23887
23888/**
23889 * @ngdoc directive
23890 * @name ngMouseenter
23891 *
23892 * @description
23893 * Specify custom behavior on mouseenter event.
23894 *
23895 * @element ANY
23896 * @priority 0
23897 * @param {expression} ngMouseenter {@link guide/expression Expression} to evaluate upon
23898 * mouseenter. ({@link guide/expression#-event- Event object is available as `$event`})
23899 *
23900 * @example
23901   <example>
23902     <file name="index.html">
23903      <button ng-mouseenter="count = count + 1" ng-init="count=0">
23904        Increment (when mouse enters)
23905      </button>
23906      count: {{count}}
23907     </file>
23908   </example>
23909 */
23910
23911
23912/**
23913 * @ngdoc directive
23914 * @name ngMouseleave
23915 *
23916 * @description
23917 * Specify custom behavior on mouseleave event.
23918 *
23919 * @element ANY
23920 * @priority 0
23921 * @param {expression} ngMouseleave {@link guide/expression Expression} to evaluate upon
23922 * mouseleave. ({@link guide/expression#-event- Event object is available as `$event`})
23923 *
23924 * @example
23925   <example>
23926     <file name="index.html">
23927      <button ng-mouseleave="count = count + 1" ng-init="count=0">
23928        Increment (when mouse leaves)
23929      </button>
23930      count: {{count}}
23931     </file>
23932   </example>
23933 */
23934
23935
23936/**
23937 * @ngdoc directive
23938 * @name ngMousemove
23939 *
23940 * @description
23941 * Specify custom behavior on mousemove event.
23942 *
23943 * @element ANY
23944 * @priority 0
23945 * @param {expression} ngMousemove {@link guide/expression Expression} to evaluate upon
23946 * mousemove. ({@link guide/expression#-event- Event object is available as `$event`})
23947 *
23948 * @example
23949   <example>
23950     <file name="index.html">
23951      <button ng-mousemove="count = count + 1" ng-init="count=0">
23952        Increment (when mouse moves)
23953      </button>
23954      count: {{count}}
23955     </file>
23956   </example>
23957 */
23958
23959
23960/**
23961 * @ngdoc directive
23962 * @name ngKeydown
23963 *
23964 * @description
23965 * Specify custom behavior on keydown event.
23966 *
23967 * @element ANY
23968 * @priority 0
23969 * @param {expression} ngKeydown {@link guide/expression Expression} to evaluate upon
23970 * keydown. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
23971 *
23972 * @example
23973   <example>
23974     <file name="index.html">
23975      <input ng-keydown="count = count + 1" ng-init="count=0">
23976      key down count: {{count}}
23977     </file>
23978   </example>
23979 */
23980
23981
23982/**
23983 * @ngdoc directive
23984 * @name ngKeyup
23985 *
23986 * @description
23987 * Specify custom behavior on keyup event.
23988 *
23989 * @element ANY
23990 * @priority 0
23991 * @param {expression} ngKeyup {@link guide/expression Expression} to evaluate upon
23992 * keyup. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
23993 *
23994 * @example
23995   <example>
23996     <file name="index.html">
23997       <p>Typing in the input box below updates the key count</p>
23998       <input ng-keyup="count = count + 1" ng-init="count=0"> key up count: {{count}}
23999
24000       <p>Typing in the input box below updates the keycode</p>
24001       <input ng-keyup="event=$event">
24002       <p>event keyCode: {{ event.keyCode }}</p>
24003       <p>event altKey: {{ event.altKey }}</p>
24004     </file>
24005   </example>
24006 */
24007
24008
24009/**
24010 * @ngdoc directive
24011 * @name ngKeypress
24012 *
24013 * @description
24014 * Specify custom behavior on keypress event.
24015 *
24016 * @element ANY
24017 * @param {expression} ngKeypress {@link guide/expression Expression} to evaluate upon
24018 * keypress. ({@link guide/expression#-event- Event object is available as `$event`}
24019 * and can be interrogated for keyCode, altKey, etc.)
24020 *
24021 * @example
24022   <example>
24023     <file name="index.html">
24024      <input ng-keypress="count = count + 1" ng-init="count=0">
24025      key press count: {{count}}
24026     </file>
24027   </example>
24028 */
24029
24030
24031/**
24032 * @ngdoc directive
24033 * @name ngSubmit
24034 *
24035 * @description
24036 * Enables binding angular expressions to onsubmit events.
24037 *
24038 * Additionally it prevents the default action (which for form means 
24038sending the request to the
24039 * server and reloading the current page), but only if the form does not contain `action`,
24040 * `data-action`, or `x-action` attributes.
24041 *
24042 * <div class="alert alert-warning">
24043 * **Warning:** Be careful not to cause "double-submission" by using both the `ngClick` and
24044 * `ngSubmit` handlers together. See the
24045 * {@link form#submitting-a-form-and-preventing-the-default-action `form` directive documentation}
24046 * for a detailed discussion of when `ngSubmit` may be triggered.
24047 * </div>
24048 *
24049 * @element form
24050 * @priority 0
24051 * @param {expression} ngSubmit {@link guide/expression Expression} to eval.
24052 * ({@link guide/expression#-event- Event object is available as `$event`})
24053 *
24054 * @example
24055   <example module="submitExample">
24056     <file name="index.html">
24057      <script>
24058        angular.module('submitExample', [])
24059          .controller('ExampleController', ['$scope', function($scope) {
24060            $scope.list = [];
24061            $scope.text = 'hello';
24062            $scope.submit = function() {
24063              if ($scope.text) {
24064                $scope.list.push(this.text);
24065                $scope.text = '';
24066              }
24067            };
24068          }]);
24069      </script>
24070      <form ng-submit="submit()" ng-controller="ExampleController">
24071        Enter text and hit enter:
24072        <input type="text" ng-model="text" name="text" />
24073        <input type="submit" id="submit" value="Submit" />
24074        <pre>list={{list}}</pre>
24075      </form>
24076     </file>
24077     <file name="protractor.js" type="protractor">
24078       it('should check ng-submit', function() {
24079         expect(element(by.binding('list')).getText()).toBe('list=[]');
24080         element(by.css('#submit')).click();
24081         expect(element(by.binding('list')).getText()).toContain('hello');
24082         expect(element(by.model('text')).getAttribute('value')).toBe('');
24083       });
24084       it('should ignore empty strings', function() {
24085         expect(element(by.binding('list')).getText()).toBe('list=[]');
24086         element(by.css('#submit')).click();
24087         element(by.css('#submit')).click();
24088         expect(element(by.binding('list')).getText()).toContain('hello');
24089        });
24090     </file>
24091   </example>
24092 */
24093
24094/**
24095 * @ngdoc directive
24096 * @name ngFocus
24097 *
24098 * @description
24099 * Specify custom behavior on focus event.
24100 *
24101 * Note: As the `focus` event is executed synchronously when calling `input.focus()`
24102 * AngularJS executes the expression using `scope.$evalAsync` if the event is fired
24103 * during an `$apply` to ensure a consistent state.
24104 *
24105 * @element window, input, select, textarea, a
24106 * @priority 0
24107 * @param {expression} ngFocus {@link guide/expression Expression} to evaluate upon
24108 * focus. ({@link guide/expression#-event- Event object is available as `$event`})
24109 *
24110 * @example
24111 * See {@link ng.directive:ngClick ngClick}
24112 */
24113
24114/**
24115 * @ngdoc directive
24116 * @name ngBlur
24117 *
24118 * @description
24119 * Specify custom behavior on blur event.
24120 *
24121 * A [blur event](https://developer.mozilla.org/en-US/docs/Web/Events/blur) fires when
24122 * an element has lost focus.
24123 *
24124 * Note: As the `blur` event is executed synchronously also during DOM manipulations
24125 * (e.g. removing a focussed input),
24126 * AngularJS executes the expression using `scope.$evalAsync` if the event is fired
24127 * during an `$apply` to ensure a consistent state.
24128 *
24129 * @element window, input, select, textarea, a
24130 * @priority 0
24131 * @param {expression} ngBlur {@link guide/expression Expression} to evaluate upon
24132 * blur. ({@link guide/expression#-event- Event object is available as `$event`})
24133 *
24134 * @example
24135 * See {@link ng.directive:ngClick ngClick}
24136 */
24137
24138/**
24139 * @ngdoc directive
24140 * @name ngCopy
24141 *
24142 * @description
24143 * Specify custom behavior on copy event.
24144 *
24145 * @element window, input, select, textarea, a
24146 * @priority 0
24147 * @param {expression} ngCopy {@link guide/expression Expression} to evaluate upon
24148 * copy. ({@link guide/expression#-event- Event object is available as `$event`})
24149 *
24150 * @example
24151   <example>
24152     <file name="index.html">
24153      <input ng-copy="copied=true" ng-init="copied=false; value='copy me'" ng-model="value">
24154      copied: {{copied}}
24155     </file>
24156   </example>
24157 */
24158
24159/**
24160 * @ngdoc directive
24161 * @name ngCut
24162 *
24163 * @description
24164 * Specify custom behavior on cut event.
24165 *
24166 * @element window, input, select, textarea, a
24167 * @priority 0
24168 * @param {expression} ngCut {@link guide/expression Expression} to evaluate upon
24169 * cut. ({@link guide/expression#-event- Event object is available as `$event`})
24170 *
24171 * @example
24172   <example>
24173     <file name="index.html">
24174      <input ng-cut="cut=true" ng-init="cut=false; value='cut me'" ng-model="value">
24175      cut: {{cut}}
24176     </file>
24177   </example>
24178 */
24179
24180/**
24181 * @ngdoc directive
24182 * @name ngPaste
24183 *
24184 * @description
24185 * Specify custom behavior on paste event.
24186 *
24187 * @element window, input, select, textarea, a
24188 * @priority 0
24189 * @param {expression} ngPaste {@link guide/expression Expression} to evaluate upon
24190 * paste. ({@link guide/expression#-event- Event object is available as `$event`})
24191 *
24192 * @example
24193   <example>
24194     <file name="index.html">
24195      <input ng-paste="paste=true" ng-init="paste=false" placeholder='paste here'>
24196      pasted: {{paste}}
24197     </file>
24198   </example>
24199 */
24200
24201/**
24202 * @ngdoc directive
24203 * @name ngIf
24204 * @restrict A
24205 * @multiElement
24206 *
24207 * @description
24208 * The `ngIf` directive removes or recreates a portion of the DOM tree based on an
24209 * {expression}. If the expression assigned to `ngIf` evaluates to a false
24210 * value then the element is removed from the DOM, otherwise a clone of the
24211 * element is reinserted into the DOM.
24212 *
24213 * `ngIf` differs from `ngShow` and `ngHide` in that `ngIf` completely removes and recreates the
24214 * element in the DOM rather than changing its visibility via the `display` css property.  A common
24215 * case when this difference is significant is when using css selectors that rely on an element's
24216 * position within the DOM, such as the `:first-child` or `:last-child` pseudo-classe
24216s.
24217 *
24218 * Note that when an element is removed using `ngIf` its scope is destroyed and a new scope
24219 * is created when the element is restored.  The scope created within `ngIf` inherits from
24220 * its parent scope using
24221 * [prototypal inheritance](https://github.com/angular/angular.js/wiki/Understanding-Scopes#javascript-prototypal-inheritance).
24222 * An important implication of this is if `ngModel` is used within `ngIf` to bind to
24223 * a javascript primitive defined in the parent scope. In this case any modifications made to the
24224 * variable within the child scope will override (hide) the value in the parent scope.
24225 *
24226 * Also, `ngIf` recreates elements using their compiled state. An example of this behavior
24227 * is if an element's class attribute is directly modified after it's compiled, using something like
24228 * jQuery's `.addClass()` method, and the element is later removed. When `ngIf` recreates the element
24229 * the added class will be lost because the original compiled state is used to regenerate the element.
24230 *
24231 * Additionally, you can provide animations via the `ngAnimate` module to animate the `enter`
24232 * and `leave` effects.
24233 *
24234 * @animations
24235 * enter - happens just after the `ngIf` contents change and a new DOM element is created and injected into the `ngIf` container
24236 * leave - happens just before the `ngIf` contents are removed from the DOM
24237 *
24238 * @element ANY
24239 * @scope
24240 * @priority 600
24241 * @param {expression} ngIf If the {@link guide/expression expression} is falsy then
24242 *     the element is removed from the DOM tree. If it is truthy a copy of the compiled
24243 *     element is added to the DOM tree.
24244 *
24245 * @example
24246  <example module="ngAnimate" deps="angular-animate.js" animations="true">
24247    <file name="index.html">
24248      <label>Click me: <input type="checkbox" ng-model="checked" ng-init="checked=true" /></label><br/>
24249      Show when checked:
24250      <span ng-if="checked" class="animate-if">
24251        This is removed when the checkbox is unchecked.
24252      </span>
24253    </file>
24254    <file name="animations.css">
24255      .animate-if {
24256        background:white;
24257        border:1px solid black;
24258        padding:10px;
24259      }
24260
24261      .animate-if.ng-enter, .animate-if.ng-leave {
24262        transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
24263      }
24264
24265      .animate-if.ng-enter,
24266      .animate-if.ng-leave.ng-leave-active {
24267        opacity:0;
24268      }
24269
24270      .animate-if.ng-leave,
24271      .animate-if.ng-enter.ng-enter-active {
24272        opacity:1;
24273      }
24274    </file>
24275  </example>
24276 */
24277var ngIfDirective = ['$animate', function($animate) {
24278  return {
24279    multiElement: true,
24280    transclude: 'element',
24281    priority: 600,
24282    terminal: true,
24283    restrict: 'A',
24284    $$tlb: true,
24285    link: function($scope, $element, $attr, ctrl, $transclude) {
24286        var block, childScope, previousElements;
24287        $scope.$watch($attr.ngIf, function ngIfWatchAction(value) {
24288
24289          if (value) {
24290            if (!childScope) {
24291              $transclude(function(clone, newScope) {
24292                childScope = newScope;
24293                clone[clone.length++] = document.createComment(' end ngIf: ' + $attr.ngIf + ' ');
24294                // Note: We only need the first/last node of the cloned nodes.
24295                // However, we need to keep the reference to the jqlite wrapper as it might be changed later
24296                // by a directive with templateUrl when its template arrives.
24297                block = {
24298                  clone: clone
24299                };
24300                $animate.enter(clone, $element.parent(), $element);
24301              });
24302            }
24303          } else {
24304            if (previousElements) {
24305              previousElements.remove();
24306              previousElements = null;
24307            }
24308            if (childScope) {
24309              childScope.$destroy();
24310              childScope = null;
24311            }
24312            if (block) {
24313              previousElements = getBlockNodes(block.clone);
24314              $animate.leave(previousElements).then(function() {
24315                previousElements = null;
24316              });
24317              block = null;
24318            }
24319          }
24320        });
24321    }
24322  };
24323}];
24324
24325/**
24326 * @ngdoc directive
24327 * @name ngInclude
24328 * @restrict ECA
24329 *
24330 * @description
24331 * Fetches, compiles and includes an external HTML fragment.
24332 *
24333 * By default, the template URL is restricted to the same domain and protocol as the
24334 * application document. This is done by calling {@link $sce#getTrustedResourceUrl
24335 * $sce.getTrustedResourceUrl} on it. To load templates from other domains or protocols
24336 * you may either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist them} or
24337 * {@link $sce#trustAsResourceUrl wrap them} as trusted values. Refer to Angular's {@link
24338 * ng.$sce Strict Contextual Escaping}.
24339 *
24340 * In addition, the browser's
24341 * [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest)
24342 * and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/)
24343 * policy may further restrict whether the template is successfully loaded.
24344 * For example, `ngInclude` won't work for cross-domain requests on all browsers and for `file://`
24345 * access on some browsers.
24346 *
24347 * @animations
24348 * enter - animation is used to bring new content into the browser.
24349 * leave - animation is used to animate existing content away.
24350 *
24351 * The enter and leave animation occur concurrently.
24352 *
24353 * @scope
24354 * @priority 400
24355 *
24356 * @param {string} ngInclude|src angular expression evaluating to URL. If the source is a string constant,
24357 *                 make sure you wrap it in **single** quotes, e.g. `src="'myPartialTemplate.html'"`.
24358 * @param {string=} onload Expression to evaluate when a new partial is loaded.
24359 *
24360 * @param {string=} autoscroll Whether `ngInclude` should call {@link ng.$anchorScroll
24361 *                  $anchorScroll} to scroll the viewport after the content is loaded.
24362 *
24363 *                  - If the attribute is not set, disable scrolling.
24364 *                  - If the attribute is set without value, enable scrolling.
24365 *                  - Otherwise enable scrolling only if the expression evaluates to truthy value.
24366 *
24367 * @example
24368  <example module="includeExample" deps="angular-animate.js" animations="true">
24369    <file name="index.html">
24370     <div ng-controller="ExampleController">
24371       <select ng-model="template" ng-options="t.name for t in templates">
24372        <option value="">(blank)</option>
24373       </select>
24374       url of the template: <code>{{template.url}}</code>
24375       <hr/>
24376       <div class="slide-animate-container">
24377         <div class="slide-animate" ng-include="template.url"></div>
24378       </div>
24379     </div>
24380    </file>
24381    <file name="script.js">
24382      angular.module('includeExample', ['ngAnimate'])
24383        .controller('ExampleController', ['$scope', function($scope) {
24384          $scope.templates =
24385            [ { name: 'template1.html', url: 'template1.html'},
24386              { name: 'template2.html', url: 'template2.html'} ];
24387          $scope.template = $scope.templates[0];
24388        }]);
24389     </file>
24390    <file name="template1.html">
24391      Content of template1.html
24392    </file>
24393    <file name="template2.html">
24394      Content of template2.html
24395    </file>
24396    <file name="animations.css">
24397      .slide-animate-container {
24398        position:relative;
24399        background:white;
24400        border:1px solid black;
24401        height:40px;
24402        overflow:hidden;
24403      }
24404
24405      .slide-animate {
24406        padding:10px;
24407      }
24408
24409      .slide-animate.ng-enter, .slide-animate.ng-leave {
24410        transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
24411
24412        position:absolute;
24413        top:0;
24414        left:0;
24415        right:0;
24416        bottom:0;
24417        display:block;
24418        padding:10px;
24419      }
24420
24421      .slide-animate.ng-enter {
24422        top:-50px;
24423      }
24424      .slide-animate.ng-enter.ng-enter-active {
24425        top:0;
24426      }
24427
24428      .slide-animate.ng-leave {
24429        top:0;
24430      }
24431      .slide-animate.ng-leave.ng-leave-active {
24432        top:50px;
24433      }
24434    </file>
24435    <file name="protractor.js" type="protractor">
24436      var templateSelect = element(by.model('template'));
24437      var includeElem = element(by.css('[ng-include]'));
24438
24439      it('should load template1.html', function() {
24440        expect(includeElem.getText()).toMatch(/Content of template1.html/);
24441      });
24442
24443      it('should load template2.html', function() {
24444        if (browser.params.browser == 'firefox') {
24445          // Firefox can't handle using selects
24446          // See https://github.com/angular/protractor/issues/480
24447          return;
24448        }
24449        templateSelect.click();
24450        templateSelect.all(by.css('option')).get(2).click();
24451        expect(includeElem.getText()).toMatch(/Content of template2.html/);
24452      });
24453
24454      it('should change to blank', function() {
24455        if (browser.params.browser == 'firefox') {
24456          // Firefox can't handle using selects
24457          return;
24458        }
24459        templateSelect.click();
24460        templateSelect.all(by.css('option')).get(0).click();
24461        expect(includeElem.isPresent()).toBe(false);
24462      });
24463    </file>
24464  </example>
24465 */
24466
24467
24468/**
24469 * @ngdoc event
24470 * @name ngInclude#$includeContentRequested
24471 * @eventType emit on the scope ngInclude was declared in
24472 * @description
24473 * Emitted every time the ngInclude content is requested.
24474 *
24475 * @param {Object} angularEvent Synthetic event object.
24476 * @param {String} src URL of content to load.
24477 */
24478
24479
24480/**
24481 * @ngdoc event
24482 * @name ngInclude#$includeContentLoaded
24483 * @eventType emit on the current ngInclude scope
24484 * @description
24485 * Emitted every time the ngInclude content is reloaded.
24486 *
24487 * @param {Object} angularEvent Synthetic event object.
24488 * @param {String} src URL of content to load.
24489 */
24490
24491
24492/**
24493 * @ngdoc event
24494 * @name ngInclude#$includeContentError
24495 * @eventType emit on the scope ngInclude was declared in
24496 * @description
24497 * Emitted when a template HTTP request yields an erroneous response (status < 200 || status > 299)
24498 *
24499 * @param {Object} angularEvent Synthetic event object.
24500 * @param {String} src URL of content to load.
24501 */
24502var ngIncludeDirective = ['$templateRequest', '$anchorScroll', '$animate',
24503                  function($templateRequest,   $anchorScroll,   $animate) {
24504  return {
24505    restrict: 'ECA',
24506    priority: 400,
24507    terminal: true,
24508    transclude: 'element',
24509    controller: angular.noop,
24510    compile: function(element, attr) {
24511      var srcExp = attr.ngInclude || attr.src,
24512          onloadExp = attr.onload || '',
24513          autoScrollExp = attr.autoscroll;
24514
24515      return function(scope, $element, $attr, ctrl, $transclude) {
24516        var changeCounter = 0,
24517            currentScope,
24518            previousElement,
24519            currentElement;
24520
24521        var cleanupLastIncludeContent = function() {
24522          if (previousElement) {
24523            previousElement.remove();
24524            previousElement = null;
24525          }
24526          if (currentScope) {
24527            currentScope.$destroy();
24528            currentScope = null;
24529          }
24530          if (currentElement) {
24531            $animate.leave(currentElement).then(function() {
24532              previousElement = null;
24533            });
24534            previousElement = currentElement;
24535            currentElement = null;
24536          }
24537        };
24538
24539        scope.$watch(srcExp, function ngIncludeWatchAction(src) {
24540          var afterAnimation = function() {
24541            if (isDefined(autoScrollExp) && (!autoScrollExp || scope.$eval(autoScrollExp))) {
24542              $anchorScroll();
24543            }
24544          };
24545          var thisChangeId = ++changeCounter;
24546
24547          if (src) {
24548            //set the 2nd param to true to ignore the template request error so that the inner
24549            //contents and scope can be cleaned up.
24550            $templateRequest(src, true).then(function(response) {
24551              if (thisChangeId !== changeCounter) return;
24552              var newScope = scope.$new();
24553              ctrl.template = response;
24554
24555              // Note: This will also link all children of ng-include that were contained in the original
24556              // html. If that content contains controllers, ... they could pollute/change the scope.
24557              // However, using ng-include on an element with additional content does not make sense...
24558              // Note: We can't remove them in the cloneAttchFn of $transclude as that
24559              // function is called before linking the content, which would apply child
24560              // directives to non existing elements.
24561              var clone = $transclude(newScope, function(clone) {
24562                cleanupLastIncludeContent();
24563                $animate.enter(clone, null, $element).then(afterAnimation);
24564              });
24565
24566              currentScope = newScope;
24567              currentElement = clone;
24568
24569              currentScope.$emit('$includeContentLoaded', src);
24570              scope.$eval(onloadExp);
24571            }, function() {
24572              if (thisChangeId === changeCounter) {
24573                cleanupLastIncludeContent();
vendor: 2,830 bytes, lines 24574-24656
24574                scope.$emit('$includeContentError', src);
24575              }
24576            });
24577            scope.$emit('$includeContentRequested', src);
24578          } else {
24579            cleanupLastIncludeContent();
24580            ctrl.template = null;
24581          }
24582        });
24583      };
24584    }
24585  };
24586}];
24587
24588// This directive is called during the $transclude call of the first `ngInclude` directive.
24589// It will replace and compile the content of the element with the loaded template.
24590// We need this directive so that the element content is already filled when
24591// the link function of another directive on the same element as ngInclude
24592// is called.
24593var ngIncludeFillContentDirective = ['$compile',
24594  function($compile) {
24595    return {
24596      restrict: 'ECA',
24597      priority: -400,
24598      require: 'ngInclude',
24599      link: function(scope, $element, $attr, ctrl) {
24600        if (/SVG/.test($element[0].toString())) {
24601          // WebKit: https://bugs.webkit.org/show_bug.cgi?id=135698 --- SVG elements do not
24602          // support innerHTML, so detect this here and try to generate the contents
24603          // specially.
24604          $element.empty();
24605          $compile(jqLiteBuildFragment(ctrl.template, document).childNodes)(scope,
24606              function namespaceAdaptedClone(clone) {
24607            $element.append(clone);
24608          }, {futureParentElement: $element});
24609          return;
24610        }
24611
24612        $element.html(ctrl.template);
24613        $compile($element.contents())(scope);
24614      }
24615    };
24616  }];
24617
24618/**
24619 * @ngdoc directive
24620 * @name ngInit
24621 * @restrict AC
24622 *
24623 * @description
24624 * The `ngInit` directive allows you to evaluate an expression in the
24625 * current scope.
24626 *
24627 * <div class="alert alert-danger">
24628 * This directive can be abused to add unnecessary amounts of logic into your templates.
24629 * There are only a few appropriate uses of `ngInit`, such as for aliasing special properties of
24630 * {@link ng.directive:ngRepeat `ngRepeat`}, as seen in the demo below; and for injecting data via
24631 * server side scripting. Besides these few cases, you should use {@link guide/controller controllers}
24632 * rather than `ngInit` to initialize values on a scope.
24633 * </div>
24634 *
24635 * <div class="alert alert-warning">
24636 * **Note**: If you have assignment in `ngInit` along with a {@link ng.$filter `filter`}, make
24637 * sure you have parentheses to ensure correct operator precedence:
24638 * <pre class="prettyprint">
24639 * `<div ng-init="test1 = ($index | toString)"></div>`
24640 * </pre>
24641 * </div>
24642 *
24643 * @priority 450
24644 *
24645 * @element ANY
24646 * @param {expression} ngInit {@link guide/expression Expression} to eval.
24647 *
24648 * @example
24649   <example module="initExample">
24650     <file name="index.html">
24651   <script>
24652     angular.module('initExample', [])
24653       .controller('ExampleController', ['$scope', function($scope) {
24654         $scope.list = [['a', 'b'], ['c', 'd']];
24655       }]);
24656   </script>
24657   <div ng-controller="ExampleController">
24658     <div ng-repeat="innerList in list" ng-init="outerIndex = $index">
24659       <div ng-repeat="value in innerList" ng-init="innerIndex = $index">
24660          <span class="example-init">list[ {{outerIndex}} ][ {{innerIndex}} ] = {{value}};</span>
24661       </div>
24662     </div>
24663   </div>
24664     </file>
24665     <file name="protractor.js" type="protractor">
24666       it('should alias index positions', function() {
24667         var elements = element.all(by.css('.example-init'));
24668         expect(elements.get(0).getText()).toBe('list[ 0 ][ 0 ] = a;');
24669         expect(elements.get(1).getText()).toBe('list[ 0 ][ 1 ] = b;');
24670         expect(elements.get(2).getText()).toBe('list[ 1 ][ 0 ] = c;');
24671         expect(elements.get(3).getText()).toBe('list[ 1 ][ 1 ] = d;');
24672       });
24673     </file>
24674   </example>
24675 */
24676var ngInitDirective = ngDirective({
24677  priority: 450,
24678  compile: function() {
24679    return {
24680      pre: function(scope, element, attrs) {
24681        scope.$eval(attrs.ngInit);
24682      }
24683    };
24684  }
24685});
24686
24687/**
24688 * @ngdoc directive
24689 * @name ngList
24690 *
24691 * @description
24692 * Text input that converts between a delimited string and an array of strings. The default
24693 * delimiter is a comma followed by a space - equivalent to `ng-list=", "`. You can specify a custom
24694 * delimiter as the value of the `ngList` attribute - for example, `ng-list=" | "`.
24695 *
24696 * The behaviour of the directive is affected by the use of the `ngTrim` attribute.
24697 * * If `ngTrim` is set to `"false"` then whitespace around both the separator and each
24698 *   list item is respected. This implies that the user of the directive is responsible for
24699 *   dealing with whitespace but also allows you to use whitespace as a delimiter, such as a
24700 *   tab or newline character.
24701 * * Otherwise whitespace around the delimiter is ignored when splitting (although it is respected
24702 *   when joining the list items back together) and whitespace around each list item is stripped
24703 *   before it is added to the model.
24704 *
24705 * ### Example with Validation
24706 *
24707 * <example name="ngList-directive" module="listExample">
24708 *   <file name="app.js">
24709 *      angular.module('listExample', [])
24710 *        .controller('ExampleController', ['$scope', function($scope) {
24711 *          $scope.names = ['morpheus', 'neo', 'trinity'];
24712 *        }]);
24713 *   </file>
24714 *   <file name="index.html">
24715 *    <form name="myForm" ng-controller="ExampleController">
24716 *      <label>List: <input name="namesInput" ng-model="names" ng-list required></label>
24717 *      <span role="alert">
24718 *        <span class="error" ng-show="myForm.namesInput.$error.required">
24719 *        Required!</span>
24720 *      </span>
24721 *      <br>
24722 *      <tt>names = {{names}}</tt><br/>
24723 *      <tt>myForm.namesInput.$valid = {{myForm.namesInput.$valid}}</tt><br/>
24724 *      <tt>myForm.namesInput.$error = {{myForm.namesInput.$error}}</tt><br/>
24725 *      <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
24726 *      <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
24727 *     </form>
24728 *   </file>
24729 *   <file name="protractor.js" type="protractor">
24730 *     var listInput = element(by.model('names'));
24731 *     var names = element(by.exactBinding('names'));
24732 *     var valid = element(by.binding('myForm.namesInput.$valid'));
24733 *     var error = element(by.css('span.error'));
24734 *
24735 *     it('should initialize to model', function() {
24736 *       expect(names.getText()).toContain('["morpheus","neo","trinity"]');
24737 *       expect(valid.getText()).toContain('true');
24738 *       expect(error.getCssValue('display')).toBe('none');
24739 *     });
24740 *
24741 *     it('should be invalid if empty', function() {
24742 *       listInput.clear();
24743 *       listInput.sendKeys('');
24744 *
24745 *       expect(names.getText()).toContain('');
24746 *       expect(valid.getText()).toContain('false');
24747 *       expect(error.getCssValue('display')).not.toBe('none');
24748 *     });
24749 *   </file>
24750 * </example>
24751 *
24752 * ### Example - splitting on newline
24753 * <example name="ngList-directive-newlines">
24754 *   <file name="index.html">
24755 *    <textarea ng-model="list" ng-list="&#10;" ng-trim="false"></textarea>
24756 *    <pre>{{ list | json }}</pre>
24757 *   </file>
24758 *   <file name="protractor.js" type="protractor">
24759 *     it("should split the text by newlines", function() {
24760 *       var listInput = element(by.model('list'));
24761 *       var output = element(by.binding('list | json'));
24762 *       listInput.sendKeys('abc\ndef\nghi');
24763 *       expect(output.getText()).toContain('[\n  "abc",\n  "def",\n  "ghi"\n]');
24764 *     });
24765 *   </file>
24766 * </example>
24767 *
24768 * @element input
24769 * @param {string=} ngList optional delimiter that should be used to split the value.
24770 */
24771var ngListDirective = function() {
24772  return {
24773    restrict: 'A',
24774    priority: 100,
24775    require: 'ngModel',
24776    link: function(scope, element, attr, ctrl) {
24777      // We want to control whitespace trimming so we use this convoluted approach
24778      // to access the ngList attribute, which doesn't pre-trim the attribute
24779      var ngList = element.attr(attr.$attr.ngList) || ', ';
24780      var trimValues = attr.ngTrim !== 'false';
24781      var separator = trimValues ? trim(ngList) : ngList;
24782
24783      var parse = function(viewValue) {
24784        // If the viewValue is invalid (say required but empty) it will be `undefined`
24785        if (isUndefined(viewValue)) return;
24786
24787        var list = [];
24788
24789        if (viewValue) {
24790          forEach(viewValue.split(separator), function(value) {
24791            if (value) list.push(trimValues ? trim(value) : value);
24792          });
24793        }
24794
24795        return list;
24796      };
24797
24798      ctrl.$parsers.push(parse);
24799      ctrl.$formatters.push(function(value) {
24800        if (isArray(value)) {
24801          return value.join(ngList);
24802        }
24803
24804        return undefined;
24805      });
24806
24807      // Override the standard $isEmpty because an empty array means the input is empty.
24808      ctrl.$isEmpty = function(value) {
24809        return !value || !value.length;
24810      };
24811    }
24812  };
24813};
24814
24815/* global VALID_CLASS: true,
24816  INVALID_CLASS: true,
24817  PRISTINE_CLASS: true,
24818  DIRTY_CLASS: true,
24819  UNTOUCHED_CLASS: true,
24820  TOUCHED_CLASS: true,
24821*/
24822
24823var VALID_CLASS = 'ng-valid',
24824    INVALID_CLASS = 'ng-invalid',
24825    PRISTINE_CLASS = 'ng-pristine',
24826    DIRTY_CLASS = 'ng-dirty',
24827    UNTOUCHED_CLASS = 'ng-untouched',
24828    TOUCHED_CLASS = 'ng-touched',
24829    PENDING_CLASS = 'ng-pending';
24830
24831var ngModelMinErr = minErr('ngModel');
24832
24833/**
24834 * @ngdoc type
24835 * @name ngModel.NgModelController
24836 *
24837 * @property {*} $viewValue The actual value from the control's view. For `input` elements, this is a
24838 * String. See {@link ngModel.NgModelController#$setViewValue} for information about when the $viewValue
24839 * is set.
24840 * @property {*} $modelValue The value in the model that the control is bound to.
24841 * @property {Array.<Function>} $parsers Array of functions to execute, as a pipeline, whenever
24842       the control reads value from the DOM. The functions are called in array order, each passing
24843       its return value through to the next. The last return value is forwarded to the
24844       {@link ngModel.NgModelController#$validators `$validators`} collection.
24845
24846Parsers are used to sanitize / convert the {@link ngModel.NgModelController#$viewValue
24847`$viewValue`}.
24848
24849Returning `undefined` from a parser means a parse error occurred. In that case,
24850no {@link ngModel.NgModelController#$validators `$validators`} will run and the `ngModel`
24851will be set to `undefined` unless {@link ngModelOptions `ngModelOptions.allowInvalid`}
24852is set to `true`. The parse error is stored in `ngModel.$error.parse`.
24853
24854 *
24855 * @property {Array.<Function>} $formatters Array of functions to execute, as a pipeline, whenever
24856       the model value changes. The functions are called in reverse array order, each passing the value through to the
24857       next. The last return value is used as the actual DOM value.
24858       Used to format / convert values for display in the control.
24859 * ```js
24860 * function formatter(value) {
24861 *   if (value) {
24862 *     return value.toUpperCase();
24863 *   }
24864 * }
24865 * ngModel.$formatters.push(formatter);
24866 * ```
24867 *
24868 * @property {Object.<string, function>} $validators A collection of validators that are applied
24869 *      whenever the model value changes. The key value within the object refers to the name of the
24870 *      validator while the function refers to the validation operation. The validation operation is
24871 *      provided with the model value as an argument and must return a true or false value depending
24872 *      on the response of that validation.
24873 *
24874 * ```js
24875 * ngModel.$validators.validCharacters = function(modelValue, viewValue) {
24876 *   var value = modelValue || viewValue;
24877 *   return /[0-9]+/.test(value) &&
24878 *          /[a-z]+/.test(value) &&
24879 *          /[A-Z]+/.test(value) &&
24880 *          /\W+/.test(value);
24881 * };
24882 * ```
24883 *
24884 * @property {Object.<string, function>} $asyncValidators A collection of validations that are expected to
24885 *      perform an asynchronous validation (e.g. a HTTP request). The validation function that is provided
24886 *      is expected to return a promise when it is run during the model validation process. Once the promise
24887 *      is delivered then the validation status will be set to true when fulfilled and false when rejected.
24888 *      When the asynchronous validators are triggered, each of the validators will run in parallel and the model
24889 *      value will only be updated once all validators have been fulfilled. As long as an asynchronous validator
24890 *      is unfulfilled, its key will be added to the controllers `$pending` property. Also, all asynchronous validators
24891 *      will only run once all synchronous validators have passed.
24892 *
24893 * Please note that if $http is used then it is important that the server returns a success HTTP response code
24894 * in order to fulfill the validation and a status level of `4xx` in order to reject the validation.
24895 *
24896 * ```js
24897 * ngModel.$asyncValidators.uniqueUsername = function(modelValue, viewValue) {
24898 *   var value = modelValue || viewValue;
24899 *
24900 *   // Lookup user by username
24901 *   return $http.get('/api/users/' + value).
24902 *      then(function resolved() {
24903 *        //username exists, this means validation fails
24904 *        return $q.reject('exists');
24905 *      }, function rejected() {
24906 *        //username does not exist, therefore this validation passes
24907 *        return true;
24908 *      });
24909 * };
24910 * ```
24911 *
24912 * @property {Array.<Function>} $viewChangeListeners Array of functions to execute whenever the
24913 *     view value has changed. It is called with no arguments, and its return value is ignored.
24914 *     This can be used in place of additional $watches against the model value.
24915 *
24916 * @property {Object} $error An object hash with all failing validator ids as keys.
24917 * @property {Object} $pending An object hash with all pending validator ids as keys.
24918 *
24919 * @property {boolean} $untouched True if control has not lost focus yet.
24920 * @property {boolean} $touched True if control has lost focus.
24921 * @property {boolean} $pristine True if user has not interacted with the control yet.
24922 * @property {boolean} $dirty True if user has already interacted with the control.
24923 * @property {boolean} $valid True if there is no error.
24924 * @property {boolean}
24924 $invalid True if at least one error on the control.
24925 * @property {string} $name The name attribute of the control.
24926 *
24927 * @description
24928 *
24929 * `NgModelController` provides API for the {@link ngModel `ngModel`} directive.
24930 * The controller contains services for data-binding, validation, CSS updates, and value formatting
24931 * and parsing. It purposefully does not contain any logic which deals with DOM rendering or
24932 * listening to DOM events.
24933 * Such DOM related logic should be provided by other directives which make use of
24934 * `NgModelController` for data-binding to control elements.
24935 * Angular provides this DOM logic for most {@link input `input`} elements.
24936 * At the end of this page you can find a {@link ngModel.NgModelController#custom-control-example
24937 * custom control example} that uses `ngModelController` to bind to `contenteditable` elements.
24938 *
24939 * @example
24940 * ### Custom Control Example
24941 * This example shows how to use `NgModelController` with a custom control to achieve
24942 * data-binding. Notice how different directives (`contenteditable`, `ng-model`, and `required`)
24943 * collaborate together to achieve the desired result.
24944 *
24945 * `contenteditable` is an HTML5 attribute, which tells the browser to let the element
24946 * contents be edited in place by the user.
24947 *
24948 * We are using the {@link ng.service:$sce $sce} service here and include the {@link ngSanitize $sanitize}
24949 * module to automatically remove "bad" content like inline event listener (e.g. `<span onclick="...">`).
24950 * However, as we are using `$sce` the model can still decide to provide unsafe content if it marks
24951 * that content using the `$sce` service.
24952 *
24953 * <example name="NgModelController" module="customControl" deps="angular-sanitize.js">
24954    <file name="style.css">
24955      [contenteditable] {
24956        border: 1px solid black;
24957        background-color: white;
24958        min-height: 20px;
24959      }
24960
24961      .ng-invalid {
24962        border: 1px solid red;
24963      }
24964
24965    </file>
24966    <file name="script.js">
24967      angular.module('customControl', ['ngSanitize']).
24968        directive('contenteditable', ['$sce', function($sce) {
24969          return {
24970            restrict: 'A', // only activate on element attribute
24971            require: '?ngModel', // get a hold of NgModelController
24972            link: function(scope, element, attrs, ngModel) {
24973              if (!ngModel) return; // do nothing if no ng-model
24974
24975              // Specify how UI should be updated
24976              ngModel.$render = function() {
24977                element.html($sce.getTrustedHtml(ngModel.$viewValue || ''));
24978              };
24979
24980              // Listen for change events to enable binding
24981              element.on('blur keyup change', function() {
24982                scope.$evalAsync(read);
24983              });
24984              read(); // initialize
24985
24986              // Write data to the model
24987              function read() {
24988                var html = element.html();
24989                // When we clear the content editable the browser leaves a <br> behind
24990                // If strip-br attribute is provided then we strip this out
24991                if ( attrs.stripBr && html == '<br>' ) {
24992                  html = '';
24993                }
24994                ngModel.$setViewValue(html);
24995              }
24996            }
24997          };
24998        }]);
24999    </file>
25000    <file name="index.html">
25001      <form name="myForm">
25002       <div contenteditable
25003            name="myWidget" ng-model="userContent"
25004            strip-br="true"
25005            required>Change me!</div>
25006        <span ng-show="myForm.myWidget.$error.required">Required!</span>
25007       <hr>
25008       <textarea ng-model="userContent" aria-label="Dynamic textarea"></textarea>
25009      </form>
25010    </file>
25011    <file name="protractor.js" type="protractor">
25012    it('should data-bind and become invalid', function() {
25013      if (browser.params.browser == 'safari' || browser.params.browser == 'firefox') {
25014        // SafariDriver can't handle contenteditable
25015        // and Firefox driver can't clear contenteditables very well
25016        return;
25017      }
25018      var contentEditable = element(by.css('[contenteditable]'));
25019      var content = 'Change me!';
25020
25021      expect(contentEditable.getText()).toEqual(content);
25022
25023      contentEditable.clear();
25024      contentEditable.sendKeys(protractor.Key.BACK_SPACE);
25025      expect(contentEditable.getText()).toEqual('');
25026      expect(contentEditable.getAttribute('class')).toMatch(/ng-invalid-required/);
25027    });
25028    </file>
25029 * </example>
25030 *
25031 *
25032 */
25033var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$parse', '$animate', '$timeout', '$rootScope', '$q', '$interpolate',
25034    function($scope, $exceptionHandler, $attr, $element, $parse, $animate, $timeout, $rootScope, $q, $interpolate) {
25035  this.$viewValue = Number.NaN;
25036  this.$modelValue = Number.NaN;
25037  this.$$rawModelValue = undefined; // stores the parsed modelValue / model set from scope regardless of validity.
25038  this.$validators = {};
25039  this.$asyncValidators = {};
25040  this.$parsers = [];
25041  this.$formatters = [];
25042  this.$viewChangeListeners = [];
25043  this.$untouched = true;
25044  this.$touched = false;
25045  this.$pristine = true;
25046  this.$dirty = false;
25047  this.$valid = true;
25048  this.$invalid = false;
25049  this.$error = {}; // keep invalid keys here
25050  this.$$success = {}; // keep valid keys here
25051  this.$pending = undefined; // keep pending keys here
25052  this.$name = $interpolate($attr.name || '', false)($scope);
25053  this.$$parentForm = nullFormCtrl;
25054
25055  var parsedNgModel = $parse($attr.ngModel),
25056      parsedNgModelAssign = parsedNgModel.assign,
25057      ngModelGet = parsedNgModel,
25058      ngModelSet = parsedNgModelAssign,
25059      pendingDebounce = null,
25060      parserValid,
25061      ctrl = this;
25062
25063  this.$$setOptions = function(options) {
25064    ctrl.$options = options;
25065    if (options && options.getterSetter) {
25066      var invokeModelGetter = $parse($attr.ngModel + '()'),
25067          invokeModelSetter = $parse($attr.ngModel + '($$$p)');
25068
25069      ngModelGet = function($scope) {
25070        var modelValue = parsedNgModel($scope);
25071        if (isFunction(modelValue)) {
25072          modelValue = invokeModelGetter($scope);
25073        }
25074        return modelValue;
25075      };
25076      ngModelSet = function($scope, newValue) {
25077        if (isFunction(parsedNgModel($scope))) {
25078          invokeModelSetter($scope, {$$$p: ctrl.$modelValue});
25079        } else {
25080          parsedNgModelAssign($scope, ctrl.$modelValue);
25081        }
25082      };
25083    } else if (!parsedNgModel.assign) {
25084      throw ngModelMinErr('nonassign', "Expression '{0}' is non-assignable. Element: {1}",
25085          $attr.ngModel, startingTag($element));
25086    }
25087  };
25088
25089  /**
25090   * @ngdoc method
25091   * @name ngModel.NgModelController#$render
25092   *
25093   * @description
25094   * Called when the view needs to be updated. It is expected that the user of the ng-model
25095   * directive will implement this method.
25096   *
25097   * The `$render()` method is invoked in the following situations:
25098   *
25099   * * `$rollbackViewValue()` is called.  If we are rolling back the view value to the last
25100   *   committed value then `$render()` is called to update the input control.
25101   * * The value referenced by `ng-model` is changed programmatically and both the `$modelValue` and
25102   *   the `$viewValue` are different from last time.
25103   *
25104   * Since `ng-model` does not do a deep watch, `$render()` is only invoked if the values of
25105   * `$modelValue` and `$viewValue` are actually different from their previous value. If `$modelValue`
25106   * or `$viewValue` are objects (rather than a string or number) then `$render()` will not be
25107   * invoked if you only change a property on the objects.
25108   */
25109  this.$render = noop;
25110
25111  /**
25112   * @ngdoc method
25113   * @name ngModel.NgModelController#$isEmpty
25114   *
25115   * @description
25116   * This is called when we need to determine if the value of an input is empty.
25117   *
25118   * For instance, the required directive does this to work out if the input has data or not.
25119   *
25120   * The default `$isEmpty` function checks whether the value is `undefined`, `''`, `null` or `NaN`.
25121   *
25122   * You can override this for input directives whose concept of being empty is different from the
25123   * default. The `checkboxInputType` directive does this because in its case a value of `false`
25124   * implies empty.
25125   *
25126   * @param {*} value The value of the input to check for emptiness.
25127   * @returns {boolean} True if `value` is "empty".
25128   */
25129  this.$isEmpty = function(value) {
25130    return isUndefined(value) || value === '' || value === null || value !== value;
25131  };
25132
25133  var currentValidationRunId = 0;
25134
25135  /**
25136   * @ngdoc method
25137   * @name ngModel.NgModelController#$setValidity
25138   *
25139   * @description
25140   * Change the validity state, and notify the form.
25141   *
25142   * This method can be called within $parsers/$formatters or a custom validation implementation.
25143   * However, in most cases it should be sufficient to use the `ngModel.$validators` and
25144   * `ngModel.$asyncValidators` collections which will call `$setValidity` automatically.
25145   *
25146   * @param {string} validationErrorKey Name of the validator. The `validationErrorKey` will be assigned
25147   *        to either `$error[validationErrorKey]` or `$pending[validationErrorKey]`
25148   *        (for unfulfilled `$asyncValidators`), so that it is available for data-binding.
25149   *        The `validationErrorKey` should be in camelCase and will get converted into dash-case
25150   *        for class name. Example: `myError` will result in `ng-valid-my-error` and `ng-invalid-my-error`
25151   *        class and can be bound to as  `{{someForm.someControl.$error.myError}}` .
25152   * @param {boolean} isValid Whether the current state is valid (true), invalid (false), pending (undefined),
25153   *                          or skipped (null). Pending is used for unfulfilled `$asyncValidators`.
25154   *                          Skipped is used by Angular when validators do not run because of parse errors and
25155   *                          when `$asyncValidators` do not run because any of the `$validators` failed.
25156   */
25157  addSetValidityMethod({
25158    ctrl: this,
25159    $element: $element,
25160    set: function(object, property) {
25161      object[property] = true;
25162    },
25163    unset: function(object, property) {
25164      delete object[property];
25165    },
25166    $animate: $animate
25167  });
25168
25169  /**
25170   * @ngdoc method
25171   * @name ngModel.NgModelController#$setPristine
25172   *
25173   * @description
25174   * Sets the control to its pristine state.
25175   *
25176   * This method can be called to remove the `ng-dirty` class and set the control to its pristine
25177   * state (`ng-pristine` class). A model is considered to be pristine when the control
25178   * has not been changed from when first compiled.
25179   */
25180  this.$setPristine = function() {
25181    ctrl.$dirty = false;
25182    ctrl.$pristine = true;
25183    $animate.removeClass($element, DIRTY_CLASS);
25184    $animate.addClass($element, PRISTINE_CLASS);
25185  };
25186
25187  /**
25188   * @ngdoc method
25189   * @name ngModel.NgModelController#$setDirty
25190   *
25191   * @description
25192   * Sets the control to its dirty state.
25193   *
25194   * This method can be called to remove the `ng-pristine` class and set the control to its dirty
25195   * state (`ng-dirty` class). A model is considered to be dirty when the control has been changed
25196   * from when first compiled.
25197   */
25198  this.$setDirty = function() {
25199    ctrl.$dirty = true;
25200    ctrl.$pristine = false;
25201    $animate.removeClass($element, PRISTINE_CLASS);
25202    $animate.addClass($element, DIRTY_CLASS);
25203    ctrl.$$parentForm.$setDirty();
25204  };
25205
25206  /**
25207   * @ngdoc method
25208   * @name ngModel.NgModelController#$setUntouched
25209   *
25210   * @description
25211   * Sets the control to its untouched state.
25212   *
25213   * This method can be called to remove the `ng-touched` class and set the control to its
25214   * untouched state (`ng-untouched` class). Upon compilation, a model is set as untouched
25215   * by default, however this function can be used to restore that state if the model has
25216   * already been touched by the user.
25217   */
25218  this.$setUntouched = function() {
25219    ctrl.$touched = false;
25220    ctrl.$untouched = true;
25221    $animate.setClass($element, UNTOUCHED_CLASS, TOUCHED_CLASS);
25222  };
25223
25224  /**
25225   * @ngdoc method
25226   * @name ngModel.NgModelController#$setTouched
25227   *
25228   * @description
25229   * Sets the control to its touched state.
25230   *
25231   * This method can be called to remove the `ng-untouched` class and set the control to its
25232   * touched state (`ng-touched` class). A model is considered to be touched when the user has
25233   * first focused the control element and then shifted focus away from the control (blur event).
25234   */
25235  this.$setTouched = function() {
25236    ctrl.$touched = true;
25237    ctrl.$untouched = false;
25238    $animate.setClass($element, TOUCHED_CLASS, UNTOUCHED_CLASS);
25239  };
25240
25241  /**
25242   * @ngdoc method
25243   * @name ngModel.NgModelController#$rollbackViewValue
25244   *
25245   * @description
25246   * Cancel an update and reset the input element's value to prevent an update to the `$modelValue`,
25247   * which may be caused by a pending debounced event or because the input is waiting for a some
25248   * future event.
25249   *
25250   * If you have an input that uses `ng-model-options` to set up debounced events or events such
25251   * as blur you can have a situation where there is a period when the `$viewValue`
25252   * is out of synch with the ngModel's `$modelValue`.
25253   *
25254   * In this case, you can run into difficulties if you try to update the ngModel's `$modelValue`
25255   * programmatically before these debounced/future events have resolved/occurred, because Angular's
25256   * dirty checking mechanism is not able to tell whether the model has actually changed or not.
25257   *
25258   * The `$rollbackViewValue()` method should be called before programmatically changing the model of an
25259   * input which may have such events pending. This is important in order to make sure that the
25260   * input field will be updated with the new model value and any pending operations are cancelled.
25261   *
25262   * <example name="ng-model-cancel-update" module="cancel-update-example">
25263   *   <file name="app.js">
25264   *     angular.module('cancel-update-example', [])
25265   *
25266   *     .controller('CancelUpdateController', ['$scope', function($scope) {
25267   *       $scope.resetWithCancel = function(e) {
25268   *         if (e.keyCode == 27) {
25269   *           $scope.myForm.myInput1.$rollbackViewValue();
25270   *           $scope.myValue = '';
25271   *         }
25272   *       };
25273   *       $scope.resetWithoutCancel = function(e) {
25274   *         if (e.keyCode == 27) {
25275   *           $scope.myValue = '';
25276   *         }
25277   *       };
25278   *     }]);
25279   *   </file>
25280   *   <file name="index.html">
25281   *     <div ng-controller="CancelUpdateController">
25282   *       <p>Try typing something in each input.  See that the model only updates when you
25283   *          blur off the input.
25284   *        </p>
25285   *        <p>Now see what happens if you start typing then press the Escape key</p>
25286   *
25287   *       <form name="myForm" ng-model-options="{ updateOn: 'blur' }">
25288   *         <p id="inputDescription1">With $rollbackViewValue()</p>
25289   *         <input name="myInput1" aria-describedby="inputDescription1" ng-model="myValue"
25290   *                ng-keydown="resetWithCancel($event)"><br/>
25291   *         myValue: "{{ myValue }}"
25292   *
25293   *         <p id="inputDescription2">Without $rollbackViewValue()</p>
25294   *         <input name="myInput2" aria-describedby="inputDescription2" ng-model="myValue"
25295   *                ng-keydown="resetWithoutCancel($event)"><br/>
25296   *         myValue: "{{ myValue }}"
25297   *       </form>
25298   *     </div>
25299   *   </file>
25300   * </example>
25301   */
25302  this.$rollbackViewValue = function() {
25303    $timeout.cancel(pendingDebounce);
25304    ctrl.$viewValue = ctrl.$$lastCommittedViewValue;
25305    ctrl.$render();
25306  };
25307
25308  /**
25309   * @ngdoc method
25310   * @name ngModel.NgModelController#$validate
25311   *
25312   * @description
25313   * Runs each of the registered validators (first synchronous validators and then
25314   * asynchronous validators).
25315   * If the validity changes to invalid, the model will be set to `undefined`,
25316   * unless {@link ngModelOptions `ngModelOptions.allowInvalid`} is `true`.
25317   * If the validity changes to valid, it will set the model to the last available valid
25318   * `$modelValue`, i.e. either the last parsed value or the last value set from the scope.
25319   */
25320  this.$validate = function() {
25321    // ignore $validate before model is initialized
25322    if (isNumber(ctrl.$modelValue) && isNaN(ctrl.$modelValue)) {
25323      return;
25324    }
25325
25326    var viewValue = ctrl.$$lastCommittedViewValue;
25327    // Note: we use the $$rawModelValue as $modelValue might have been
25328    // set to undefined during a view -> model update that found validation
25329    // errors. We can't parse the view here, since that could change
25330    // the model although neither viewValue nor the model on the scope changed
25331    var modelValue = ctrl.$$rawModelValue;
25332
25333    var prevValid = ctrl.$valid;
25334    var prevModelValue = ctrl.$modelValue;
25335
25336    var allowInvalid = ctrl.$options && ctrl.$options.allowInvalid;
25337
25338    ctrl.$$runValidators(modelValue, viewValue, function(allValid) {
25339      // If there was no change in validity, don't update the model
25340      // This prevents changing an invalid modelValue to undefined
25341      if (!allowInvalid && prevValid !== allValid) {
25342        // Note: Don't check ctrl.$valid here, as we could have
25343        // external validators (e.g. calculated on the server),
25344        // that just call $setValidity and need the model value
25345        // to calculate their validity.
25346        ctrl.$modelValue = allValid ? modelValue : undefined;
25347
25348        if (ctrl.$modelValue !== prevModelValue) {
25349          ctrl.$$writeModelToScope();
25350        }
25351      }
25352    });
25353
25354  };
25355
25356  this.$$runValidators = function(modelValue, viewValue, doneCallback) {
25357    currentValidationRunId++;
25358    var localValidationRunId = currentValidationRunId;
25359
25360    // check parser error
25361    if (!processParseErrors()) {
25362      validationDone(false);
25363      return;
25364    }
25365    if (!processSyncValidators()) {
25366      validationDone(false);
25367      return;
25368    }
25369    processAsyncValidators();
25370
25371    function processParseErrors() {
25372      var errorKey = ctrl.$$parserName || 'parse';
25373      if (isUndefined(parserValid)) {
25374        setValidity(errorKey, null);
25375      } else {
25376        if (!parserValid) {
25377          forEach(ctrl.$validators, function(v, name) {
25378            setValidity(name, null);
25379          });
25380          forEach(ctrl.$asyncValidators, function(v, name) {
25381            setValidity(name, null);
25382          });
25383        }
25384        // Set the parse error last, to prevent unsetting it, should a $validators key == parserName
25385        setValidity(errorKey, parserValid);
25386        return parserValid;
25387      }
25388      return true;
25389    }
25390
25391    function processSyncValidators() {
25392      var syncValidatorsValid = true;
25393      forEach(ctrl.$validators, function(validator, name) {
25394        var result = validator(modelValue, viewValue);
25395        syncValidatorsValid = syncValidatorsValid && result;
25396        setValidity(name, result);
25397      });
25398      if (!syncValidatorsValid) {
25399        forEach(ctrl.$asyncValidators, function(v, name) {
25400          setValidity(name, null);
25401        });
25402        return false;
25403      }
25404      return true;
25405    }
25406
25407    function processAsyncValidators() {
25408      var validatorPromises = [];
25409      var allValid = true;
25410      forEach(ctrl.$asyncValidators, function(validator, name) {
25411        var promise = validator(modelValue, viewValue);
25412        if (!isPromiseLike(promise)) {
25413          throw ngModelMinErr("$asyncValidators",
25414            "Expected asynchronous validator to return a promise but got '{0}' instead.", promise);
25415        }
25416        setValidity(name, undefined);
25417        validatorPromises.push(promise.then(function() {
25418          setValidity(name, true);
25419        }, function(error) {
25420          allValid = false;
25421          setValidity(name, false);
25422        }));
25423      });
25424      if (!validatorPromises.length) {
25425        validationDone(true);
25426      } else {
25427        $q.all(validatorPromises).then(function() {
25428          validationDone(allValid);
25429        }, noop);
25430      }
25431    }
25432
25433    function setValidity(name, isValid) {
25434      if (localValidationRunId === currentValidationRunId) {
25435        ctrl.$setValidity(name, isValid);
25436      }
25437    }
25438
25439    function validationDone(allValid) {
25440      if (localValidationRunId === currentValidationRunId) {
25441
25442        doneCallback(allValid);
25443      }
25444    }
25445  };
25446
25447  /**
25448   * @ngdoc method
25449   * @name ngModel.NgModelController#$commitViewValue
25450   *
25451   * @description
25452   * Commit a pending update to the `$modelValue`.
25453   *
25454   * Updates may be pending by a debounced event or because the input is waiting for a some future
25455   * event defined in `ng-model-options`. this method is rarely needed as `NgModelController`
25456   * usually handles calling this in response to input events.
25457   */
25458  this.$commitViewValue = function() {
25459    var viewValue = ctrl.$viewValue;
25460
25461    $timeout.cancel(pendingDebounce);
25462
25463    // If the view value has not changed then we should just exit, except in the case where there is
25464    // a native validator on the element. In this case the validation state may have changed even though
25465    // the viewValue has stayed empty.
25466    if (ctrl.$$lastCommittedViewValue === viewValue && (viewValue !== '' || !ctrl.$$hasNativeValidators)) {
25467      return;
25468    }
25469    ctrl.$$lastCommittedViewValue = viewValue;
25470
25471    // change to dirty
25472    if (ctrl.$pristine) {
25473      this.$setDirty();
25474    }
25475    this.$$parseAndValidate();
25476  };
25477
25478  this.$$parseAndValidate = function() {
25479    var viewValue = ctrl.$$lastCommittedViewValue;
25480    var modelValue = viewValue;
25481    parserValid = isUndefined(modelValue) ? undefined : true;
25482
25483    if (parserValid) {
25484      for (var i = 0; i < ctrl.$parsers.length; i++) {
25485        modelValue = ctrl.$parsers[i](modelValue);
25486        if (isUndefined(modelValue)) {
25487          parserValid = false;
25488          break;
25489        }
25490      }
25491    }
25492    if (isNumber(ctrl.$modelValue) && isNaN(ctrl.$modelValue)) {
25493      // ctrl.$modelValue has not been touched yet...
25494      ctrl.$modelValue = ngModelGet($scope);
25495    }
25496    var prevModelValue = ctrl.$modelValue;
25497    var allowInvalid = ctrl.$options && ctrl.$options.allowInvalid;
25498    ctrl.$$rawModelValue = modelValue;
25499
25500    if (allowInvalid) {
25501      ctrl.$modelValue = modelValue;
25502      writeToModelIfNeeded();
25503    }
25504
25505    // Pass the $$lastCommittedViewValue here, because the cached viewValue might be out of date.
25506    // This can happen if e.g. $setViewValue is called from inside a parser
25507    ctrl.$$runValidators(modelValue, ctrl.$$lastCommittedViewValue, function(allValid) {
25508      if (!allowInvalid) {
25509        // Note: Don't check ctrl.$valid here, as we could have
25510        // external validators (e.g. calculated on the server),
25511        // that just call $setValidity and need the model value
25512        // to calculate their validity.
25513        ctrl.$modelValue = allValid ? modelValue : undefined;
25514        writeToModelIfNeeded();
25515      }
25516    });
25517
25518    function writeToModelIfNeeded() {
25519      if (ctrl.$modelValue !== prevModelValue) {
25520        ctrl.$$writeModelToScope();
25521      }
25522    }
25523  };
25524
25525  this.$$writeModelToScope = function() {
25526    ngModelSet($scope, ctrl.$modelValue);
25527    forEach(ctrl.$viewChangeListeners, function(listener) {
25528      try {
25529        listener();
25530      } catch (e) {
25531        $exceptionHandler(e);
25532      }
25533    });
25534  };
25535
25536  /**
25537   * @ngdoc method
25538   * @name ngModel.NgModelController#$setViewValue
25539   *
25540   * @description
25541   * Update the view value.
25542   *
25543   * This method should be called when a control wants to change the view value; typically,
25544   * this is done from within a DOM event handler. For example, the {@link ng.directive:input input}
25545   * directive calls it when the value of the input changes and {@link ng.directive:select select}
25546   * calls it when an option is selected.
25547   *
25548   * When `$setViewValue` is called, the new `value` will be staged for committing through the `$parsers`
25549   * and `$validators` pipelines. If there are no special {@link ngModelOptions} specified then the staged
25550   * value sent directly for processing, finally to be applied to `$modelValue` and then the
25551   * **expression** specified in the `ng-model` attribute. Lastly, all the registered change listeners,
25552   * in the `$viewChangeListeners` list, are called.
25553   *
25554   * In case the {@link ng.directive:ngModelOptions ngModelOptions} directive is used with `updateOn`
25555   * and the `default` trigger is not listed, all those actions will remain pending until one of the
25556   * `updateOn` events is triggered on the DOM element.
25557   * All these actions will be debounced if the {@link ng.directive:ngModelOptions ngModelOptions}
25558   * directive is used with a custom debounce for this particular event.
25559   * Note that a `$digest` is only triggered once the `updateOn` events are fired, or if `debounce`
25560   * is specified, once the timer runs out.
25561   *
25562   * When used with standard inputs, the view value will always be a string (which is in some cases
25563   * parsed into another type, such as a `Date` object for `input[date]`.)
25564   * However, custom controls might also pass objects to this method. In this case, we should make
25565   * a copy of the object before passing it to `$setViewValue`. This is because `ngModel` does not
25566   * perform a deep watch of objects, it only looks for a change of identity. If you only change
25567   * the property of the object then ngModel will not realise that the object has changed and
25568   * will not invoke the `$parsers` and `$validators` pipelines. For this reason, you should
25569   * not change properties of the copy once it has been passed to `$setViewValue`.
25570   * Otherwise you may cause the model value on the scope to change incorrectly.
25571   *
25572   * <div class="alert alert-info">
25573   * In any case, the value passed to the method should always reflect the current value
25574   * of the control. For example, if you are calling `$setViewValue` for an input element,
25575   * you should pass the input DOM value. Otherwise, the control and the scope model become
25576   * out of sync. It's also important to note that `$setViewValue` does not call `$render` or change
25577   * the control's DOM value in any way. If we want to change the control's DOM value
25578   * programmatically, we should update the `ngModel` scope expression. Its new value will be
25579   * picked up by the model controller, which will run it through the `$formatters`, `$render` it
25580   * to update the DOM, and finally call `$validate` on it.
25581   * </div>
25582   *
25583   * @param {*} value value from the view.
25584   * @param {string} trigger Event that triggered the update.
25585   */
25586  this.$setViewValue = function(value, trigger) {
25587    ctrl.$viewValue = value;
25588    if (!ctrl.$options || ctrl.$options.updateOnDefault) {
25589      ctrl.$$debounceViewValueCommit(trigger);
25590    }
25591  };
25592
25593  this.$$debounceViewValueCommit = function(trigger) {
25594    var debounceDelay = 0,
25595        options = ctrl.$options,
25596        debounce;
25597
25598    if (options && isDefined(options.debounce)) {
25599      debounce = options.debounce;
25600      if (isNumber(debounce)) {
25601        debounceDelay = debounce;
25602      } else if (isNumber(debounce[trigger])) {
25603        debounceDelay = debounce[trigger];
25604      } else if (isNumber(debounce['default'])) {
25605        debounceDelay = debounce['default'];
25606      }
25607    }
25608
25609    $timeout.cancel(pendingDebounce);
25610    if (debounceDelay) {
25611      pendingDebounce = $timeout(function() {
25612        ctrl.$commitViewValue();
25613      }, debounceDelay);
25614    } else if ($rootScope.$$phase) {
25615      ctrl.$commitViewValue();
25616    } else {
25617      $scope.$apply(function() {
25618        ctrl.$commitViewValue();
25619      });
25620    }
25621  };
25622
25623  // model -> value
25624  // Note: we cannot use a normal scope.$watch as we want to detect the following:
25625  // 1. scope value is 'a'
25626  // 2. user enters 'b'
25627  // 3. ng-change kicks in and reverts scope value to 'a'
25628  //    ->
25628 scope value did not change since the last digest as
25629  //       ng-change executes in apply phase
25630  // 4. view should be changed back to 'a'
25631  $scope.$watch(function ngModelWatch() {
25632    var modelValue = ngModelGet($scope);
25633
25634    // if scope model value and ngModel value are out of sync
25635    // TODO(perf): why not move this to the action fn?
25636    if (modelValue !== ctrl.$modelValue &&
25637       // checks for NaN is needed to allow setting the model to NaN when there's an asyncValidator
25638       (ctrl.$modelValue === ctrl.$modelValue || modelValue === modelValue)
25639    ) {
25640      ctrl.$modelValue = ctrl.$$rawModelValue = modelValue;
25641      parserValid = undefined;
25642
25643      var formatters = ctrl.$formatters,
25644          idx = formatters.length;
25645
25646      var viewValue = modelValue;
25647      while (idx--) {
25648        viewValue = formatters[idx](viewValue);
25649      }
25650      if (ctrl.$viewValue !== viewValue) {
25651        ctrl.$viewValue = ctrl.$$lastCommittedViewValue = viewValue;
25652        ctrl.$render();
25653
25654        ctrl.$$runValidators(modelValue, viewValue, noop);
25655      }
25656    }
25657
25658    return modelValue;
25659  });
25660}];
25661
25662
25663/**
25664 * @ngdoc directive
25665 * @name ngModel
25666 *
25667 * @element input
25668 * @priority 1
25669 *
25670 * @description
25671 * The `ngModel` directive binds an `input`,`select`, `textarea` (or custom form control) to a
25672 * property on the scope using {@link ngModel.NgModelController NgModelController},
25673 * which is created and exposed by this directive.
25674 *
25675 * `ngModel` is responsible for:
25676 *
25677 * - Binding the view into the model, which other directives such as `input`, `textarea` or `select`
25678 *   require.
25679 * - Providing validation behavior (i.e. required, number, email, url).
25680 * - Keeping the state of the control (valid/invalid, dirty/pristine, touched/untouched, validation errors).
25681 * - Setting related css classes on the element (`ng-valid`, `ng-invalid`, `ng-dirty`, `ng-pristine`, `ng-touched`, `ng-untouched`) including animations.
25682 * - Registering the control with its parent {@link ng.directive:form form}.
25683 *
25684 * Note: `ngModel` will try to bind to the property given by evaluating the expression on the
25685 * current scope. If the property doesn't already exist on this scope, it will be created
25686 * implicitly and added to the scope.
25687 *
25688 * For best practices on using `ngModel`, see:
25689 *
25690 *  - [Understanding Scopes](https://github.com/angular/angular.js/wiki/Understanding-Scopes)
25691 *
25692 * For basic examples, how to use `ngModel`, see:
25693 *
25694 *  - {@link ng.directive:input input}
25695 *    - {@link input[text] text}
25696 *    - {@link input[checkbox] checkbox}
25697 *    - {@link input[radio] radio}
25698 *    - {@link input[number] number}
25699 *    - {@link input[email] email}
25700 *    - {@link input[url] url}
25701 *    - {@link input[date] date}
25702 *    - {@link input[datetime-local] datetime-local}
25703 *    - {@link input[time] time}
25704 *    - {@link input[month] month}
25705 *    - {@link input[week] week}
25706 *  - {@link ng.directive:select select}
25707 *  - {@link ng.directive:textarea textarea}
25708 *
25709 * # CSS classes
25710 * The following CSS classes are added and removed on the associated input/select/textarea element
25711 * depending on the validity of the model.
25712 *
25713 *  - `ng-valid`: the model is valid
25714 *  - `ng-invalid`: the model is invalid
25715 *  - `ng-valid-[key]`: for each valid key added by `$setValidity`
25716 *  - `ng-invalid-[key]`: for each invalid key added by `$setValidity`
25717 *  - `ng-pristine`: the control hasn't been interacted with yet
25718 *  - `ng-dirty`: the control has been interacted with
25719 *  - `ng-touched`: the control has been blurred
25720 *  - `ng-untouched`: the control hasn't been blurred
25721 *  - `ng-pending`: any `$asyncValidators` are unfulfilled
25722 *
25723 * Keep in mind that ngAnimate can detect each of these classes when added and removed.
25724 *
25725 * ## Animation Hooks
25726 *
25727 * Animations within models are triggered when any of the associated CSS classes are added and removed
25728 * on the input element which is attached to the model. These classes are: `.ng-pristine`, `.ng-dirty`,
25729 * `.ng-invalid` and `.ng-valid` as well as any other validations that are performed on the model itself.
25730 * The animations that are triggered within ngModel are similar to how they work in ngClass and
25731 * animations can be hooked into using CSS transitions, keyframes as well as JS animations.
25732 *
25733 * The following example shows a simple way to utilize CSS transitions to style an input element
25734 * that has been rendered as invalid after it has been validated:
25735 *
25736 * <pre>
25737 * //be sure to include ngAnimate as a module to hook into more
25738 * //advanced animations
25739 * .my-input {
25740 *   transition:0.5s linear all;
25741 *   background: white;
25742 * }
25743 * .my-input.ng-invalid {
25744 *   background: red;
25745 *   color:white;
25746 * }
25747 * </pre>
25748 *
25749 * @example
25750 * <example deps="angular-animate.js" animations="true" fixBase="true" module="inputExample">
25751     <file name="index.html">
25752       <script>
25753        angular.module('inputExample', [])
25754          .controller('ExampleController', ['$scope', function($scope) {
25755            $scope.val = '1';
25756          }]);
25757       </script>
25758       <style>
25759         .my-input {
25760           transition:all linear 0.5s;
25761           background: transparent;
25762         }
25763         .my-input.ng-invalid {
25764           color:white;
25765           background: red;
25766         }
25767       </style>
25768       <p id="inputDescription">
25769        Update input to see transitions when valid/invalid.
25770        Integer is a valid value.
25771       </p>
25772       <form name="testForm" ng-controller="ExampleController">
25773         <input ng-model="val" ng-pattern="/^\d+$/" name="anim" class="my-input"
25774                aria-describedby="inputDescription" />
25775       </form>
25776     </file>
25777 * </example>
25778 *
25779 * ## Binding to a getter/setter
25780 *
25781 * Sometimes it's helpful to bind `ngModel` to a getter/setter function.  A getter/setter is a
25782 * function that returns a representation of the model when called with zero arguments, and sets
25783 * the internal state of a model when called with an argument. It's sometimes useful to use this
25784 * for models that have an internal representation that's different from what the model exposes
25785 * to the view.
25786 *
25787 * <div class="alert alert-success">
25788 * **Best Practice:** It's best to keep getters fast because Angular is likely to call them more
25789 * frequently than other parts of your code.
25790 * </div>
25791 *
25792 * You use this behavior by adding `ng-model-options="{ getterSetter: true }"` to an element that
25793 * has `ng-model` attached to it. You can also add `ng-model-options="{ getterSetter: true }"` to
25794 * a `<form>`, which will enable this behavior for all `<input>`s within it. See
25795 * {@link ng.directive:ngModelOptions `ngModelOptions`} for more.
25796 *
25797 * The following example shows how to use `ngModel` with a getter/setter:
25798 *
25799 * @example
25800 * <example name="ngModel-getter-setter" module="getterSetterExample">
25801     <file name="index.html">
25802       <div ng-controller="ExampleController">
25803         <form name="userForm">
25804           <label>Name:
25805             <input type="text" name="userName"
25806                    ng-model="user.name"
25807                    ng-model-options="{ getterSetter: true }" />
25808           </label>
25809         </form>
25810         <pre>user.name = <span ng-bind="user.name()"></span></pre>
25811       </div>
25812     </file>
25813     <file name="app.js">
25814       angular.module('getterSetterExample', [])
25815         .controller('ExampleController', ['$scope', function($scope) {
25816           var _name = 'Brian';
25817           $scope.user = {
25818             name: function(newName) {
25819              // Note that newName can be undefined for two reasons:
25820              // 1. Because it is called as a getter and thus called with no arguments
25821              // 2. Because the property should actually be set to undefined. This happens e.g. if the
25822              //    input is invalid
25823              return arguments.length ? (_name = newName) : _name;
25824             }
25825           };
25826         }]);
25827     </file>
25828 * </example>
25829 */
25830var ngModelDirective = ['$rootScope', function($rootScope) {
25831  return {
25832    restrict: 'A',
25833    require: ['ngModel', '^?form', '^?ngModelOptions'],
25834    controller: NgModelController,
25835    // Prelink needs to run before any input directive
25836    // so that we can set the NgModelOptions in NgModelController
25837    // before anyone else uses it.
25838    priority: 1,
25839    compile: function ngModelCompile(element) {
25840      // Setup initial state of the control
25841      element.addClass(PRISTINE_CLASS).addClass(UNTOUCHED_CLASS).addClass(VALID_CLASS);
25842
25843      return {
25844        pre: function ngModelPreLink(scope, element, attr, ctrls) {
25845          var modelCtrl = ctrls[0],
25846              formCtrl = ctrls[1] || modelCtrl.$$parentForm;
25847
25848          modelCtrl.$$setOptions(ctrls[2] && ctrls[2].$options);
25849
25850          // notify others, especially parent forms
25851          formCtrl.$addControl(modelCtrl);
25852
25853          attr.$observe('name', function(newValue) {
25854            if (modelCtrl.$name !== newValue) {
25855              modelCtrl.$$parentForm.$$renameControl(modelCtrl, newValue);
25856            }
25857          });
25858
25859          scope.$on('$destroy', function() {
25860            modelCtrl.$$parentForm.$removeControl(modelCtrl);
25861          });
25862        },
25863        post: function ngModelPostLink(scope, element, attr, ctrls) {
25864          var modelCtrl = ctrls[0];
25865          if (modelCtrl.$options && modelCtrl.$options.updateOn) {
25866            element.on(modelCtrl.$options.updateOn, function(ev) {
25867              modelCtrl.$$debounceViewValueCommit(ev && ev.type);
25868            });
25869          }
25870
25871          element.on('blur', function(ev) {
25872            if (modelCtrl.$touched) return;
25873
25874            if ($rootScope.$$phase) {
25875              scope.$evalAsync(modelCtrl.$setTouched);
25876            } else {
25877              scope.$apply(modelCtrl.$setTouched);
25878            }
25879          });
25880        }
25881      };
25882    }
25883  };
25884}];
25885
25886var DEFAULT_REGEXP = /(\s+|^)default(\s+|$)/;
25887
25888/**
25889 * @ngdoc directive
25890 * @name ngModelOptions
25891 *
25892 * @description
25893 * Allows tuning how model updates are done. Using `ngModelOptions` you can specify a custom list of
25894 * events that will trigger a model update and/or a debouncing delay so that the actual update only
25895 * takes place when a timer expires; this timer will be reset after another change takes place.
25896 *
25897 * Given the nature of `ngModelOptions`, the value displayed inside input fields in the view might
25898 * be different from the value in the actual model. This means that if you update the model you
25899 * should also invoke {@link ngModel.NgModelController `$rollbackViewValue`} on the relevant input field in
25900 * order to make sure it is synchronized with the model and that any debounced action is canceled.
25901 *
25902 * The easiest way to reference the control's {@link ngModel.NgModelController `$rollbackViewValue`}
25903 * method is by making sure the input is placed inside a form that has a `name` attribute. This is
25904 * important because `form` controllers are published to the related scope under the name in their
25905 * `name` attribute.
25906 *
25907 * Any pending changes will take place immediately when an enclosing form is submitted via the
25908 * `submit` event. Note that `ngClick` events will occur before the model is updated. Use `ngSubmit`
25909 * to have access to the updated model.
25910 *
25911 * `ngModelOptions` has an effect on the element it's declared on and its desce
25911ndants.
25912 *
25913 * @param {Object} ngModelOptions options to apply to the current model. Valid keys are:
25914 *   - `updateOn`: string specifying which event should the input be bound to. You can set several
25915 *     events using an space delimited list. There is a special event called `default` that
25916 *     matches the default events belonging of the control.
25917 *   - `debounce`: integer value which contains the debounce model update value in milliseconds. A
25918 *     value of 0 triggers an immediate update. If an object is supplied instead, you can specify a
25919 *     custom value for each event. For example:
25920 *     `ng-model-options="{ updateOn: 'default blur', debounce: { 'default': 500, 'blur': 0 } }"`
25921 *   - `allowInvalid`: boolean value which indicates that the model can be set with values that did
25922 *     not validate correctly instead of the default behavior of setting the model to undefined.
25923 *   - `getterSetter`: boolean value which determines whether or not to treat functions bound to
25924       `ngModel` as getters/setters.
25925 *   - `timezone`: Defines the timezone to be used to read/write the `Date` instance in the model for
25926 *     `<input type="date">`, `<input type="time">`, ... . It understands UTC/GMT and the
25927 *     continental US time zone abbreviations, but for general use, use a time zone offset, for
25928 *     example, `'+0430'` (4 hours, 30 minutes east of the Greenwich meridian)
25929 *     If not specified, the timezone of the browser will be used.
25930 *
25931 * @example
25932
25933  The following example shows how to override immediate updates. Changes on the inputs within the
25934  form will update the model only when the control loses focus (blur event). If `escape` key is
25935  pressed while the input field is focused, the value is reset to the value in the current model.
25936
25937  <example name="ngModelOptions-directive-blur" module="optionsExample">
25938    <file name="index.html">
25939      <div ng-controller="ExampleController">
25940        <form name="userForm">
25941          <label>Name:
25942            <input type="text" name="userName"
25943                   ng-model="user.name"
25944                   ng-model-options="{ updateOn: 'blur' }"
25945                   ng-keyup="cancel($event)" />
25946          </label><br />
25947          <label>Other data:
25948            <input type="text" ng-model="user.data" />
25949          </label><br />
25950        </form>
25951        <pre>user.name = <span ng-bind="user.name"></span></pre>
25952      </div>
25953    </file>
25954    <file name="app.js">
25955      angular.module('optionsExample', [])
25956        .controller('ExampleController', ['$scope', function($scope) {
25957          $scope.user = { name: 'say', data: '' };
25958
25959          $scope.cancel = function(e) {
25960            if (e.keyCode == 27) {
25961              $scope.userForm.userName.$rollbackViewValue();
25962            }
25963          };
25964        }]);
25965    </file>
25966    <file name="protractor.js" type="protractor">
25967      var model = element(by.binding('user.name'));
25968      var input = element(by.model('user.name'));
25969      var other = element(by.model('user.data'));
25970
25971      it('should allow custom events', function() {
25972        input.sendKeys(' hello');
25973        input.click();
25974        expect(model.getText()).toEqual('say');
25975        other.click();
25976        expect(model.getText()).toEqual('say hello');
25977      });
25978
25979      it('should $rollbackViewValue when model changes', function() {
25980        input.sendKeys(' hello');
25981        expect(input.getAttribute('value')).toEqual('say hello');
25982        input.sendKeys(protractor.Key.ESCAPE);
25983        expect(input.getAttribute('value')).toEqual('say');
25984        other.click();
25985        expect(model.getText()).toEqual('say');
25986      });
25987    </file>
25988  </example>
25989
25990  This one shows how to debounce model changes. Model will be updated only 1 sec after last change.
25991  If the `Clear` button is pressed, any debounced action is canceled and the value becomes empty.
25992
25993  <example name="ngModelOptions-directive-debounce" module="optionsExample">
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="{ debounce: 1000 }" />
26001          </label>
26002          <button ng-click="userForm.userName.$rollbackViewValue(); user.name=''">Clear</button>
26003          <br />
26004        </form>
26005        <pre>user.name = <span ng-bind="user.name"></span></pre>
26006      </div>
26007    </file>
26008    <file name="app.js">
26009      angular.module('optionsExample', [])
26010        .controller('ExampleController', ['$scope', function($scope) {
26011          $scope.user = { name: 'say' };
26012        }]);
26013    </file>
26014  </example>
26015
26016  This one shows how to bind to getter/setters:
26017
26018  <example name="ngModelOptions-directive-getter-setter" module="getterSetterExample">
26019    <file name="index.html">
26020      <div ng-controller="ExampleController">
26021        <form name="userForm">
26022          <label>Name:
26023            <input type="text" name="userName"
26024                   ng-model="user.name"
26025                   ng-model-options="{ getterSetter: true }" />
26026          </label>
26027        </form>
26028        <pre>user.name = <span ng-bind="user.name()"></span></pre>
26029      </div>
26030    </file>
26031    <file name="app.js">
26032      angular.module('getterSetterExample', [])
26033        .controller('ExampleController', ['$scope', function($scope) {
26034          var _name = 'Brian';
26035          $scope.user = {
26036            name: function(newName) {
26037              // Note that newName can be undefined for two reasons:
26038              // 1. Because it is called as a getter and thus called with no arguments
26039              // 2. Because the property should actually be set to undefined. This happens e.g. if the
26040              //    input is invalid
26041              return arguments.length ? (_name = newName) : _name;
26042            }
26043          };
26044        }]);
26045    </file>
26046  </example>
26047 */
26048var ngModelOptionsDirective = function() {
26049  return {
26050    restrict: 'A',
26051    controller: ['$scope', '$attrs', function($scope, $attrs) {
26052      var that = this;
26053      this.$options = copy($scope.$eval($attrs.ngModelOptions));
26054      // Allow adding/overriding bound events
26055      if (isDefined(this.$options.updateOn)) {
26056        this.$options.updateOnDefault = false;
26057        // extract "default" pseudo-event from list of events that can trigger a model update
26058        this.$options.updateOn = trim(this.$options.updateOn.replace(DEFAULT_REGEXP, function() {
26059          that.$options.updateOnDefault = true;
26060          return ' ';
26061        }));
26062      } else {
26063        this.$options.updateOnDefault = true;
26064      }
26065    }]
26066  };
26067};
26068
26069
26070
26071// helper methods
26072function addSetValidityMethod(context) {
26073  var ctrl = context.ctrl,
26074      $element = context.$element,
26075      classCache = {},
26076      set = context.set,
26077      unset = context.unset,
26078      $animate = context.$animate;
26079
26080  classCache[INVALID_CLASS] = !(classCache[VALID_CLASS] = $element.hasClass(VALID_CLASS));
26081
26082  ctrl.$setValidity = setValidity;
26083
26084  function setValidity(validationErrorKey, state, controller) {
26085    if (isUndefined(state)) {
26086      createAndSet('$pending', validationErrorKey, controller);
26087    } else {
26088      unsetAndCleanup('$pending', validationErrorKey, controller);
26089    }
26090    if (!isBoolean(state)) {
26091      unset(ctrl.$error, validationErrorKey, controller);
26092      unset(ctrl.$$success, validationErrorKey, controller);
26093    } else {
26094      if (state) {
26095        unset(ctrl.$error, validationErrorKey, controller);
26096        set(ctrl.$$success, validationErrorKey, controller);
26097      } else {
26098        set(ctrl.$error, validationErrorKey, controller);
26099        unset(ctrl.$$success, validationErrorKey, controller);
26100      }
26101    }
26102    if (ctrl.$pending) {
26103      cachedToggleClass(PENDING_CLASS, true);
26104      ctrl.$valid = ctrl.$invalid = undefined;
26105      toggleValidationCss('', null);
26106    } else {
26107      cachedToggleClass(PENDING_CLASS, false);
26108      ctrl.$valid = isObjectEmpty(ctrl.$error);
26109      ctrl.$invalid = !ctrl.$valid;
26110      toggleValidationCss('', ctrl.$valid);
26111    }
26112
26113    // re-read the state as the set/unset methods could have
26114    // combined state in ctrl.$error[validationError] (used for forms),
26115    // where setting/unsetting only increments/decrements the value,
26116    // and does not replace it.
26117    var combinedState;
26118    if (ctrl.$pending && ctrl.$pending[validationErrorKey]) {
26119      combinedState = undefined;
26120    } else if (ctrl.$error[validationErrorKey]) {
26121      combinedState = false;
26122    } else if (ctrl.$$success[validationErrorKey]) {
26123      combinedState = true;
26124    } else {
26125      combinedState = null;
26126    }
26127
26128    toggleValidationCss(validationErrorKey, combinedState);
26129    ctrl.$$parentForm.$setValidity(validationErrorKey, combinedState, ctrl);
26130  }
26131
26132  function createAndSet(name, value, controller) {
26133    if (!ctrl[name]) {
26134      ctrl[name] = {};
26135    }
26136    set(ctrl[name], value, controller);
26137  }
26138
26139  function unsetAndCleanup(name, value, controller) {
26140    if (ctrl[name]) {
26141      unset(ctrl[name], value, controller);
26142    }
26143    if (isObjectEmpty(ctrl[name])) {
26144      ctrl[name] = undefined;
26145    }
26146  }
26147
26148  function cachedToggleClass(className, switchValue) {
26149    if (switchValue && !classCache[className]) {
26150      $animate.addClass($element, className);
26151      classCache[className] = true;
26152    } else if (!switchValue && classCache[className]) {
26153      $animate.removeClass($element, className);
26154      classCache[className] = false;
26155    }
26156  }
26157
26158  function toggleValidationCss(validationErrorKey, isValid) {
26159    validationErrorKey = validationErrorKey ? '-' + snake_case(validationErrorKey, '-') : '';
26160
26161    cachedToggleClass(VALID_CLASS + validationErrorKey, isValid === true);
26162    cachedToggleClass(INVALID_CLASS + validationErrorKey, isValid === false);
26163  }
26164}
26165
26166function isObjectEmpty(obj) {
26167  if (obj) {
26168    for (var prop in obj) {
26169      if (obj.hasOwnProperty(prop)) {
26170        return false;
26171      }
26172    }
26173  }
26174  return true;
26175}
26176
26177/**
26178 * @ngdoc directive
26179 * @name ngNonBindable
26180 * @restrict AC
26181 * @priority 1000
26182 *
26183 * @description
26184 * The `ngNonBindable` directive tells Angular not to compile or bind the contents of the current
26185 * DOM element. This is useful if the element contains what appears to be Angular directives and
26186 * bindings but which should be ignored by Angular. This could be the case if you have a site that
26187 * displays snippets of code, for instance.
26188 *
26189 * @element ANY
26190 *
26191 * @example
26192 * In this example there are two locations where a simple interpolation binding (`{{}}`) is present,
26193 * but the one wrapped in `ngNonBindable` is left alone.
26194 *
26195 * @example
26196    <example>
26197      <file name="index.html">
26198        <div>Normal: {{1 + 2}}</div>
26199        <div ng-non-bindable>Ignored: {{1 + 2}}</div>
26200      </file>
26201      <file name="protractor.js" type="protractor">
26202       it('should check ng-non-bindable', function() {
26203         expect(element(by.binding('1 + 2')).getText()).toContain('3');
26204         expect(element.all(by.css('div')).last().getText()).toMatch(/1 \+ 2/);
26205       });
26206      </file>
26207    </example>
26208 */
26209var ngNonBindableDirective = ngDirective({ terminal: true, priority: 1000 });
26210
26211/* global jqLiteRemove */
26212
26213var ngOptionsMinErr = minErr('ngOptions');
26214
26215/**
26216 * @ngdoc directive
26217 * @name ngOptions
26218 * @restrict A
26219 *
26220 * @description
26221 *
26222 * The `ngOptions` attribute can be used to dynamically generate a list of `<option>`
26223 * elements for the `<select>` element using the array or object obtained by evaluating the
26224 * `ngOptions` comprehension expression.
26225 *
26226 * In many cases, `ngRepeat` can be used on `<option>` elements instead of `ngOptions` to achieve a
26227 * similar result. However, `ngOptions` provides some benefits such as reducing memory and
26228 * increasing speed by not creating a new scope for each repeated instance, as well as providing
26229 * more flexibility in how the `<select>`'s model is assigned via the `select` **`as`** part of the
26230 * comprehension expression. `ngOptions` should be used when the `<select>` model needs to be bound
26231 *  to a non-string value. This is because an option element can only be bound to string values at
26232 * present.
26233 *
26234 * When an item in the `<select>` menu is selected, the array element or object property
26235 * represented by the selected option will be bound to the model identified by the `ngModel`
26236 * directive.
26237 *
26238 * Optionally, a single hard-coded `<option>` element, with the value set to an empty string, can
26239 * be nested into the `<select>` element. This element will then represent the `null` or "not selected"
26240 * option. See example below for demonstration.
26241 *
26242 * ## Complex Models (objects or collections)
26243 *
26244 * **Note:** By default, `ngModel` watches the model by reference, not value. This is important when
26245 * binding any input directive to a model that is an object or a collection.
26246 *
26247 * Since this is a common situation for `ngOptions` the directive additionally watches the model using
26248 * `$watchCollection` when the select has the `multiple` attribute or when there is a `track by` clause in
26249 * the options expression. This allows ngOptions to trigger a re-rendering of the options even if the actual
26250 * object/collection has not changed identity but only a property on the object or an item in the collection
26251 * changes.
26252 *
26253 * Note that `$watchCollection` does a shallow comparison of the properties of the object (or the items in the collection
26254 * if the model is an array). This means that changing a property deeper inside the object/collection that the
26255 * first level will not trigger a re-rendering.
26256 *
26257 *
26258 * ## `select` **`as`**
26259 *
26260 * Using `select` **`as`** will bind the result of the `select` expression to the model, but
26261 * the value of the `<select>` and `<option>` html elements will be either the index (for array data sources)
26262 * or property name (for object data sources) of the value within the collection. If a **`track by`** expression
26263 * is used, the result of that expression will be set as the value of the `option` and `select` elements.
26264 *
26265 *
26266 * ### `select` **`as`** and **`track by`**
26267 *
26268 * <div class="alert alert-warning">
26269 * Do not use `select` **`as`** and **`track by`** in the same expression. They are not designed to work together.
26270 * </div>
26271 *
26272 * Consider the following example:
26273 *
26274 * ```html
26275 * <select ng-options="item.subItem as item.label for item in values track by item.id" ng-model="selected"></select>
26276 * ```
26277 *
26278 * ```js
26279 * $scope.values = [{
26280 *   id: 1,
26281 *   label: 'aLabel',
26282 *   subItem: { name: 'aSubItem' }
26283 * }, {
26284 *   id: 2,
26285 *   label: 'bLabel',
26286 *   subItem: { name: 'bSubItem' }
26287 * }];
26288 *
26289 * $scope.selected = { name: 'aSubItem' };
26290 * ```
26291 *
26292 * With the purpose of preserving the selection, the **`track by`** expression is always applied to the element
26293 * of the data source (to `item` in this example). To calculate whether an element is selected, we do the
26294 * following:
26295 *
26296 * 1. Apply **`track by`** to the elements in the array. In the example: `[1, 2]`
26297 * 2. Apply **`track by`** to the already selected value in `ngModel`.
26298 *    In the example: this is not possible as **`track by`** refers to `item.id`, but the selected
26299 *    value from `ngModel` is `{name: 'aSubItem'}`, so the **`track by`** expression is applied to
26300 *    a wrong object, the selected element can't be found, `<select>` is always reset to the "not
26301 *    selected" option.
26302 *
26303 *
26304 * @param {string} ngModel Assignable angular expression to data-bind to.
26305 * @param {string=} name Property name of the form under which the control is published.
26306 * @param {string=} required The control is considered valid only if value is entered.
26307 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
26308 *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
26309 *    `required` when you want to data-bind to the `required` attribute.
26310 * @param {comprehension_expression=} ngOptions in one of the following forms:
26311 *
26312 *   * for array data sources:
26313 *     * `label` **`for`** `value` **`in`** `array`
26314 *     * `select` **`as`** `label` **`for`** `value` **`in`** `array`
26315 *     * `label` **`group by`** `group` **`for`** `value` **`in`** `array`
26316 *     * `label` **`disable when`** `disable` **`for`** `value` **`in`** `array`
26317 *     * `label` **`group by`** `group` **`for`** `value` **`in`** `array` **`track by`** `trackexpr`
26318 *     * `label` **`disable when`** `disable` **`for`** `value` **`in`** `array` **`track by`** `trackexpr`
26319 *     * `label` **`for`** `value` **`in`** `array` | orderBy:`orderexpr` **`track by`** `trackexpr`
26320 *        (for including a filter with `track by`)
26321 *   * for object data sources:
26322 *     * `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
26323 *     * `select` **`as`** `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
26324 *     * `label` **`group by`** `group` **`for (`**`key`**`,`** `value`**`) in`** `object`
26325 *     * `label` **`disable when`** `disable` **`for (`**`key`**`,`** `value`**`) in`** `object`
26326 *     * `select` **`as`** `label` **`group by`** `group`
26327 *         **`for` `(`**`key`**`,`** `value`**`) in`** `object`
26328 *     * `select` **`as`** `label` **`disable when`** `disable`
26329 *         **`for` `(`**`key`**`,`** `value`**`) in`** `object`
26330 *
26331 * Where:
26332 *
26333 *   * `array` / `object`: an expression which evaluates to an array / object to iterate over.
26334 *   * `value`: local variable which will refer to each item in the `array` or each property value
26335 *      of `object` during iteration.
26336 *   * `key`: local variable which will refer to a property name in `object` during iteration.
26337 *   * `label`: The result of this expression will be the label for `<option>` element. The
26338 *     `expression` will most likely refer to the `value` variable (e.g. `value.propertyName`).
26339 *   * `select`: The result of this expression will be bound to the model of the parent `<select>`
26340 *      element. If not specified, `select` expression will default to `value`.
26341 *   * `group`: The result of this expression will be used to group options using the `<optgroup>`
26342 *      DOM element.
26343 *   * `disable`: The result of this expression will be used to disable the rendered `<option>`
26344 *      element. Return `true` to disable.
26345 *   * `trackexpr`: Used when working with an array of objects. The result of this expression will be
26346 *      used to identify the objects in the array. The `trackexpr` will most likely refer to the
26347 *     `value` variable (e.g. `value.propertyName`). With this the selection is preserved
26348 *      even when the options are recreated (e.g. reloaded from the server).
26349 *
26350 * @example
26351    <example module="selectExample">
26352      <file name="index.html">
26353        <script>
26354        angular.module('selectExample', [])
26355          .controller('ExampleController', ['$scope', function($scope) {
26356            $scope.colors = [
26357              {name:'black', shade:'dark'},
26358              {name:'white', shade:'light', notAnOption: true},
26359              {name:'red', shade:'dark'},
26360              {name:'blue', shade:'dark', notAnOption: true},
26361              {name:'yellow', shade:'light', notAnOption: false}
26362            ];
26363            $scope.myColor = $scope.colors[2]; // red
26364          }]);
26365        </script>
26366        <div ng-controller="ExampleController">
26367          <ul>
26368            <li ng-repeat="color in colors">
26369              <label>Name: <input ng-model="color.name"></label>
26370              <label><input type="checkbox" ng-model="color.notAnOption"> Disabled?</label>
26371              <button ng-click="colors.splice($index, 1)" aria-label="Remove">X</button>
26372            </li>
26373            <li>
26374              <button ng-click="colors.push({})">add</button>
26375            </li>
26376          </ul>
26377          <hr/>
26378          <label>Color (null not allowed):
26379            <select ng-model="myColor" ng-options="color.name for color in colors"></select>
26380          </label><br/>
26381          <label>Color (null allowed):
26382          <span  class="nullable">
26383            <select ng-model="myColor" ng-options="color.name for color in colors">
26384              <option value="">-- choose color --</option>
26385            </select>
26386          </span></label><br/>
26387
26388          <label>Color grouped by shade:
26389            <select ng-model="myColor" ng-options="color.name group by color.shade for color in colors">
26390            </select>
26391          </label><br/>
26392
26393          <label>Color grouped by shade, with some disabled:
26394            <select ng-model="myColor"
26395                  ng-options="color.name group by color.shade disable when color.notAnOption for color in colors">
26396            </select>
26397          </label><br/>
26398
26399
26400
26401          Select <button ng-click="myColor = { name:'not in list', shade: 'other' }">bogus</button>.
26402          <br/>
26403          <hr/>
26404          Currently selected: {{ {selected_color:myColor} }}
26405          <div style="border:solid 1px black; height:20px"
26406               ng-style="{'background-color':myColor.name}">
26407          </div>
26408        </div>
26409      </file>
26410      <file name="protractor.js" type="protractor">
26411         it('should check ng-options', function() {
26412           expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('red');
26413           element.all(by.model('myColor')).first().click();
26414           element.all(by.css('select[ng-model="myColor"] option')).first().click();
26415           expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('black');
26416           element(by.css('.nullable select[ng-model="myColor"]')).click();
26417           element.all(by.css('.nullable select[ng-model="myColor"] option')).first().click();
26418           expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('null');
26419         });
26420      </file>
26421    </example>
26422 */
26423
26424// jshint maxlen: false
26425//                     //00001111111111000000000002222222222000000000000000000000333333333300000000000000000000000004444444444400000000000005555555555555550000000006666666666666660000000777777777777777000000000000000888888888800000000000000000009999999999
26426var 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]+?))?$/;
26427                        // 1: value expression (valueFn)
26428                        // 2: label expression (displayFn)
26429                        // 3: group by expression (groupByFn)
26430                        // 4: disable when expression (disableWhenFn)
26431                        // 5: array item variable name
26432                        // 6: object item key variable name
26433                        // 7: object item value variable name
26434                        // 8: collection expression
26435                        // 9: track by expression
26436// jshint maxlen: 100
26437
26438
26439var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
26440
26441  function parseOptionsExpression(optionsExp, selectElement, scope) {
26442
26443    var match = optionsExp.match(NG_OPTIONS_REGEXP);
26444    if (!(match)) {
26445      throw ngOptionsMinErr('iexp',
26446        "Expected expression in form of " +
26447        "'_select_ (as _label_)? for (_key_,)?_value_ in _collection_'" +
26448        " but got '{0}'. Element: {1}",
26449        optionsExp, startingTag(selectElement));
26450    }
26451
26452    // Extract the parts from the ngOptions expression
26453
26454    // The variable name for the value of the item in the collection
26455    var valueName = match[5] || match[7];
26456    // The variable name for the key of the item in the collection
26457    var keyName = match[6];
26458
26459    // An expression that generates the viewValue for an option if there is a label expression
26460    var selectAs = / as /.test(match[0]) && match[1];
26461    // An expression that is used to track the id of each object in the options collection
26462    var trackBy = match[9];
26463    // An expression that generates the viewValue for an option if there is no label expression
26464    var valueFn = $parse(match[2] ? match[1] : valueName);
26465    var selectAsFn = selectAs && $parse(selectAs);
26466    var viewValueFn = selectAsFn || valueFn;
26467    var trackByFn = trackBy && $parse(trackBy);
26468
26469    // Get the value by which we are going to track the option
26470    // if we have a trackFn then use that (passing scope and locals)
26471    // otherwise just hash the given viewValue
26472    var getTrackByValueFn = trackBy ?
26473                              function(value, locals) { return trackByFn(scope, locals); } :
26474                              function getHashOfValue(value) { return hashKey(value); };
26475    var getTrackByValue = function(value, key) {
vendor: 5,716 bytes, lines 26476-26636
26476      return getTrackByValueFn(value, getLocals(value, key));
26477    };
26478
26479    var displayFn = $parse(match[2] || match[1]);
26480    var groupByFn = $parse(match[3] || '');
26481    var disableWhenFn = $parse(match[4] || '');
26482    var valuesFn = $parse(match[8]);
26483
26484    var locals = {};
26485    var getLocals = keyName ? function(value, key) {
26486      locals[keyName] = key;
26487      locals[valueName] = value;
26488      return locals;
26489    } : function(value) {
26490      locals[valueName] = value;
26491      return locals;
26492    };
26493
26494
26495    function Option(selectValue, viewValue, label, group, disabled) {
26496      this.selectValue = selectValue;
26497      this.viewValue = viewValue;
26498      this.label = label;
26499      this.group = group;
26500      this.disabled = disabled;
26501    }
26502
26503    function getOptionValuesKeys(optionValues) {
26504      var optionValuesKeys;
26505
26506      if (!keyName && isArrayLike(optionValues)) {
26507        optionValuesKeys = optionValues;
26508      } else {
26509        // if object, extract keys, in enumeration order, unsorted
26510        optionValuesKeys = [];
26511        for (var itemKey in optionValues) {
26512          if (optionValues.hasOwnProperty(itemKey) && itemKey.charAt(0) !== '$') {
26513            optionValuesKeys.push(itemKey);
26514          }
26515        }
26516      }
26517      return optionValuesKeys;
26518    }
26519
26520    return {
26521      trackBy: trackBy,
26522      getTrackByValue: getTrackByValue,
26523      getWatchables: $parse(valuesFn, function(optionValues) {
26524        // Create a collection of things that we would like to watch (watchedArray)
26525        // so that they can all be watched using a single $watchCollection
26526        // that only runs the handler once if anything changes
26527        var watchedArray = [];
26528        optionValues = optionValues || [];
26529
26530        var optionValuesKeys = getOptionValuesKeys(optionValues);
26531        var optionValuesLength = optionValuesKeys.length;
26532        for (var index = 0; index < optionValuesLength; index++) {
26533          var key = (optionValues === optionValuesKeys) ? index : optionValuesKeys[index];
26534          var value = optionValues[key];
26535
26536          var locals = getLocals(optionValues[key], key);
26537          var selectValue = getTrackByValueFn(optionValues[key], locals);
26538          watchedArray.push(selectValue);
26539
26540          // Only need to watch the displayFn if there is a specific label expression
26541          if (match[2] || match[1]) {
26542            var label = displayFn(scope, locals);
26543            watchedArray.push(label);
26544          }
26545
26546          // Only need to watch the disableWhenFn if there is a specific disable expression
26547          if (match[4]) {
26548            var disableWhen = disableWhenFn(scope, locals);
26549            watchedArray.push(disableWhen);
26550          }
26551        }
26552        return watchedArray;
26553      }),
26554
26555      getOptions: function() {
26556
26557        var optionItems = [];
26558        var selectValueMap = {};
26559
26560        // The option values were already computed in the `getWatchables` fn,
26561        // which must have been called to trigger `getOptions`
26562        var optionValues = valuesFn(scope) || [];
26563        var optionValuesKeys = getOptionValuesKeys(optionValues);
26564        var optionValuesLength = optionValuesKeys.length;
26565
26566        for (var index = 0; index < optionValuesLength; index++) {
26567          var key = (optionValues === optionValuesKeys) ? index : optionValuesKeys[index];
26568          var value = optionValues[key];
26569          var locals = getLocals(value, key);
26570          var viewValue = viewValueFn(scope, locals);
26571          var selectValue = getTrackByValueFn(viewValue, locals);
26572          var label = displayFn(scope, locals);
26573          var group = groupByFn(scope, locals);
26574          var disabled = disableWhenFn(scope, locals);
26575          var optionItem = new Option(selectValue, viewValue, label, group, disabled);
26576
26577          optionItems.push(optionItem);
26578          selectValueMap[selectValue] = optionItem;
26579        }
26580
26581        return {
26582          items: optionItems,
26583          selectValueMap: selectValueMap,
26584          getOptionFromViewValue: function(value) {
26585            return selectValueMap[getTrackByValue(value)];
26586          },
26587          getViewValueFromOption: function(option) {
26588            // If the viewValue could be an object that may be mutated by the application,
26589            // we need to make a copy and not return the reference to the value on the option.
26590            return trackBy ? angular.copy(option.viewValue) : option.viewValue;
26591          }
26592        };
26593      }
26594    };
26595  }
26596
26597
26598  // we can't just jqLite('<option>') since jqLite is not smart enough
26599  // to create it in <select> and IE barfs otherwise.
26600  var optionTemplate = document.createElement('option'),
26601      optGroupTemplate = document.createElement('optgroup');
26602
26603  return {
26604    restrict: 'A',
26605    terminal: true,
26606    require: ['select', '?ngModel'],
26607    link: function(scope, selectElement, attr, ctrls) {
26608
26609      // if ngModel is not defined, we don't need to do anything
26610      var ngModelCtrl = ctrls[1];
26611      if (!ngModelCtrl) return;
26612
26613      var selectCtrl = ctrls[0];
26614      var multiple = attr.multiple;
26615
26616      // The emptyOption allows the application developer to provide their own custom "empty"
26617      // option when the viewValue does not match any of the option values.
26618      var emptyOption;
26619      for (var i = 0, children = selectElement.children(), ii = children.length; i < ii; i++) {
26620        if (children[i].value === '') {
26621          emptyOption = children.eq(i);
26622          break;
26623        }
26624      }
26625
26626      var providedEmptyOption = !!emptyOption;
26627
26628      var unknownOption = jqLite(optionTemplate.cloneNode(false));
26629      unknownOption.val('?');
26630
26631      var options;
26632      var ngOptions = parseOptionsExpression(attr.ngOptions, selectElement, scope);
26633
26634
26635      var renderEmptyOption = function() {
26636        if (!providedEmptyOption) {
26637          selectElement.prepend(emptyOption);
26638        }
26639        selectElement.val('');
26640        emptyOption.prop('selected', true); // needed for IE
26641        emptyOption.attr('selected', true);
26642      };
26643
26644      var removeEmptyOption = function() {
26645        if (!providedEmptyOption) {
26646          emptyOption.remove();
26647        }
26648      };
26649
26650
26651      var renderUnknownOption = function() {
26652        selectElement.prepend(unknownOption);
26653        selectElement.val('?');
26654        unknownOption.prop('selected', true); // needed for IE
26655        unknownOption.attr('selected', true);
26656      };
26657
26658      var removeUnknownOption = function() {
26659        unknownOption.remove();
26660      };
26661
26662
26663      // Update the controller methods for multiple selectable options
26664      if (!multiple) {
26665
26666        selectCtrl.writeValue = function writeNgOptionsValue(value) {
26667          var option = options.getOptionFromViewValue(value);
26668
26669          if (option && !option.disabled) {
26670            if (selectElement[0].value !== option.selectValue) {
26671              removeUnknownOption();
26672              removeEmptyOption();
26673
26674              selectElement[0].value = option.selectValue;
26675              option.element.selected = true;
26676              option.element.setAttribute('selected', 'selected');
26677            }
26678          } else {
26679            if (value === null || providedEmptyOption) {
26680              removeUnknownOption();
26681              renderEmptyOption();
26682            } else {
26683              removeEmptyOption();
26684              renderUnknownOption();
26685            }
26686          }
26687        };
26688
26689        selectCtrl.readValue = function readNgOptionsValue() {
26690
26691          var selectedOption = options.selectValueMap[selectElement.val()];
26692
26693          if (selectedOption && !selectedOption.disabled) {
26694            removeEmptyOption();
26695            removeUnknownOption();
26696            return options.getViewValueFromOption(selectedOption);
26697          }
26698          return null;
26699        };
26700
26701        // If we are using `track by` then we must watch the tracked value on the model
26702        // since ngModel only watches for object identity change
26703        if (ngOptions.trackBy) {
26704          scope.$watch(
26705            function() { return ngOptions.getTrackByValue(ngModelCtrl.$viewValue); },
26706            function() { ngModelCtrl.$render(); }
26707          );
26708        }
26709
26710      } else {
26711
26712        ngModelCtrl.$isEmpty = function(value) {
26713          return !value || value.length === 0;
26714        };
26715
26716
26717        selectCtrl.writeValue = function writeNgOptionsMultiple(value) {
26718          options.items.forEach(function(option) {
26719            option.element.selected = false;
26720          });
26721
26722          if (value) {
26723            value.forEach(function(item) {
26724              var option = options.getOptionFromViewValue(item);
26725              if (option && !option.disabled) option.element.selected = true;
26726            });
26727          }
26728        };
26729
26730
26731        selectCtrl.readValue = function readNgOptionsMultiple() {
26732          var selectedValues = selectElement.val() || [],
26733              selections = [];
26734
26735          forEach(selectedValues, function(value) {
26736            var option = options.selectValueMap[value];
26737            if (option && !option.disabled) selections.push(options.getViewValueFromOption(option));
26738          });
26739
26740          return selections;
26741        };
26742
26743        // If we are using `track by` then we must watch these tracked values on the model
26744        // since ngModel only watches for object identity change
26745        if (ngOptions.trackBy) {
26746
26747          scope.$watchCollection(function() {
26748            if (isArray(ngModelCtrl.$viewValue)) {
26749              return ngModelCtrl.$viewValue.map(function(value) {
26750                return ngOptions.getTrackByValue(value);
26751              });
26752            }
26753          }, function() {
26754            ngModelCtrl.$render();
26755          });
26756
26757        }
26758      }
26759
26760
26761      if (providedEmptyOption) {
26762
26763        // we need to remove it before calling selectElement.empty() because otherwise IE will
26764        // remove the label from the element. wtf?
26765        emptyOption.remove();
26766
26767        // compile the element since there might be bindings in it
26768        $compile(emptyOption)(scope);
26769
26770        // remove the class, which is added automatically because we recompile the element and it
26771        // becomes the compilation root
26772        emptyOption.removeClass('ng-scope');
26773      } else {
26774        emptyOption = jqLite(optionTemplate.cloneNode(false));
26775      }
26776
26777      // We need to do this here to ensure that the options object is defined
26778      // when we first hit it in writeNgOptionsValue
26779      updateOptions();
26780
26781      // We will re-render the option elements if the option values or labels change
26782      scope.$watchCollection(ngOptions.getWatchables, updateOptions);
26783
26784      // ------------------------------------------------------------------ //
26785
26786
26787      function updateOptionElement(option, element) {
26788        option.element = element;
26789        element.disabled = option.disabled;
26790        // NOTE: The label must be set before the value, otherwise IE10/11/EDGE create unresponsive
26791        // selects in certain circumstances when multiple selects are next to each other and display
26792        // the option list in listbox style, i.e. the select is [multiple], or specifies a [size].
26793        // See https://github.com/angular/angular.js/issues/11314 for more info.
26794        // This is unfortunately untestable with unit / e2e tests
26795        if (option.label !== element.label) {
26796          element.label = option.label;
26797          element.textContent = option.label;
26798        }
26799        if (option.value !== element.value) element.value = option.selectValue;
26800      }
26801
26802      function addOrReuseElement(parent, current, type, templateElement) {
26803        var element;
26804        // Check whether we can reuse the next element
26805        if (current && lowercase(current.nodeName) === type) {
26806          // The next element is the right type so reuse it
26807          element = current;
26808        } else {
26809          // The next element is not the right type so create a new one
26810          element = templateElement.cloneNode(false);
26811          if (!current) {
26812            // There are no more elements so just append it to the select
26813            parent.appendChild(element);
26814          } else {
26815            // The next element is not a group so insert the new one
26816            parent.insertBefore(element, current);
26817          }
26818        }
26819        return element;
26820      }
26821
26822
26823      function removeExcessElements(current) {
26824        var next;
26825        while (current) {
26826          next = current.nextSibling;
26827          jqLiteRemove(current);
26828          current = next;
26829        }
26830      }
26831
26832
26833      function skipEmptyAndUnknownOptions(current) {
26834        var emptyOption_ = emptyOption && emptyOption[0];
26835        var unknownOption_ = unknownOption && unknownOption[0];
26836
26837        if (emptyOption_ || unknownOption_) {
26838          while (current &&
26839                (current === emptyOption_ ||
26840                current === unknownOption_ ||
26841                emptyOption_ && emptyOption_.nodeType === NODE_TYPE_COMMENT)) {
26842            // Empty options might have directives that transclude
26843            // and insert comments (e.g. ngIf)
26844            current = current.nextSibling;
26845          }
26846        }
26847        return current;
26848      }
26849
26850
26851      function updateOptions() {
26852
26853        var previousValue = options && selectCtrl.readValue();
26854
26855        options = ngOptions.getOptions();
26856
26857        var groupMap = {};
26858        var currentElement = selectElement[0].firstChild;
26859
26860        // Ensure that the empty option is always there if it was explicitly provided
26861        if (providedEmptyOption) {
26862          selectElement.prepend(emptyOption);
26863        }
26864
26865        currentElement = skipEmptyAndUnknownOptions(currentElement);
26866
26867        options.items.forEach(function updateOption(option) {
26868          var group;
26869          var groupElement;
26870          var optionElement;
26871
26872          if (option.group) {
26873
26874            // This option is to live in a group
26875            // See if we have already created this group
26876            group = groupMap[option.group];
26877
26878            if (!group) {
26879
26880              // We have not already created this group
26881              groupElement = addOrReuseElement(selectElement[0],
26882                                               currentElement,
26883                                               'optgroup',
26884                                               optGroupTemplate);
26885              // Move to the next element
26886              currentElement = groupElement.nextSibling;
26887
26888              // Update the label on the group element
26889              groupElement.label = option.group;
26890
26891              // Store it for use later
26892              group = groupMap[option.group] = {
26893                groupElement: groupElement,
26894                currentOptionElement: groupElement.firstChild
26895              };
26896
26897            }
26898
26899            // So now we have a group for this option we add the option to the group
26900            optionElement = addOrReuseElement(group.groupElement,
26901                                              group.currentOptionElement,
26902                                              'option',
26903                                              optionTemplate);
26904            updateOptionElement(option, optionElement);
26905            // Move to the next element
26906            group.currentOptionElement = optionElement.nextSibling;
26907
26908          } else {
26909
26910            // This option is not in a group
26911            optionElement = addOrReuseElement(selectElement[0],
26912                                              currentElement,
26913                                              'option',
26914                                              optionTemplate);
26915            updateOptionElement(option, optionElement);
26916            // Move to the next element
26917            currentElement = optionElement.nextSibling;
26918          }
26919        });
26920
26921
26922        // Now remove all excess options and group
26923        Object.keys(groupMap).forEach(function(key) {
26924          removeExcessElements(groupMap[key].currentOptionElement);
26925        });
26926        removeExcessElements(currentElement);
26927
26928        ngModelCtrl.$render();
26929
26930        // Check to see if the value has changed due to the update to the options
26931        if (!ngModelCtrl.$isEmpty(previousValue)) {
26932          var nextValue = selectCtrl.readValue();
26933          if (ngOptions.trackBy ? !equals(previousValue, nextValue) : previousValue !== nextValue) {
26934            ngModelCtrl.$setViewValue(nextValue);
26935            ngModelCtrl.$render();
26936          }
26937        }
26938
26939      }
26940
26941    }
26942  };
26943}];
26944
26945/**
26946 * @ngdoc directive
26947 * @name ngPluralize
26948 * @restrict EA
26949 *
26950 * @description
26951 * `ngPluralize` is a directive that displays messages according to en-US localization rules.
26952 * These rules are bundled with angular.js, but can be overridden
26953 * (see {@link guide/i18n Angular i18n} dev guide). You configure ngPluralize directive
26954 * by specifying the mappings between
26955 * [plural categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html)
26956 * and the strings to be displayed.
26957 *
26958 * # Plural categories and explicit number rules
26959 * There are two
26960 * [plural categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html)
26961 * in Angular's default en-US locale: "one" and "other".
26962 *
26963 * While a plural category may match many numbers (for example, in en-US locale, "other" can match
26964 * any number that is not 1), an explicit number rule can only match one number. For example, the
26965 * explicit number rule for "3" matches the number 3. There are examples of plural categories
26966 * and explicit number rules throughout the rest of this documentation.
26967 *
26968 * # Configuring ngPluralize
26969 * You configure ngPluralize by providing 2 attributes: `count` and `when`.
26970 * You can also provide an optional attribute, `offset`.
26971 *
26972 * The value of the `count` attribute can be either a string or an {@link guide/expression
26973 * Angular expression}; these are evaluated on the current scope for its bound value.
26974 *
26975 * The `when` attribute specifies the mappings between plural categories and the actual
26976 * string to be displayed. The value of the attribute should be a JSON object.
26977 *
26978 * The following example shows how to configure ngPluralize:
26979 *
26980 * ```html
26981 * <ng-pluralize count="personCount"
26982                 when="{'0': 'Nobody is viewing.',
26983 *                      'one': '1 person is viewing.',
26984 *                      'other': '{} people are viewing.'}">
26985 * </ng-pluralize>
26986 *```
26987 *
26988 * In the example, `"0: Nobody is viewing."` is an explicit number rule. If you did not
26989 * specify this rule, 0 would be matched to the "other" category and "0 people are viewing"
26990 * would be shown instead of "Nobody is viewing". You can specify an explicit number rule for
26991 * other numbers, for example 12, so that instead of showing "12 people are viewing", you can
26992 * show "a dozen people are viewing".
26993 *
26994 * You can use a set of closed braces (`{}`) as a placeholder for the number that you want substituted
26995 * into pluralized strings. In the previous example, Angular will replace `{}` with
26996 * <span ng-non-bindable>`{{personCount}}`</span>. The closed braces `{}` is a placeholder
26997 * for <span ng-non-bindable>{{numberExpression}}</span>.
26998 *
26999 * If no rule is defined for a category, then an empty string is displayed and a warning is generated.
27000 * Note that some locales define more categories than `one` and `other`. For example, fr-fr defines `few` and `many`.
27001 *
27002 * # Configuring ngPluralize with offset
27003 * The `offset` attribute allows further customization of pluralized text, which can result in
27004 * a better user experience. For example, instead of the message "4 people are viewing this document",
27005 * you might display "John, Kate and 2 others are viewing this document".
27006 * The offset attribute allows you to offset a number by any desired value.
27007 * Let's take a look at an example:
27008 *
27009 * ```html
27010 * <ng-pluralize count="personCount" offset=2
27011 *               when="{'0': 'Nobody is viewing.',
27012 *                      '1': '{{person1}} is viewing.',
27013 *                      '2': '{{person1}} and {{person2}} are viewing.',
27014 *                      'one': '{{person1}}, {{person2}} and one other person are viewing.',
27015 *                      'other': '{{person1}}, {{person2}} and {} other people are viewing.'}">
27016 * </ng-pluralize>
27017 * ```
27018 *
27019 * Notice that we are still using two plural categories(one, other), but we added
27020 * three explicit number rules 0, 1 and 2.
27021 * When one person, perhaps John, views the document, "John is viewing" will be shown.
27022 * When three people view the document, no explicit number rule is found, so
27023 * an offset of 2 is taken off 3, and Angular uses 1 to decide the plural category.
27024 * In this case, plural category 'one' is matched and "John, Mary and one other person are viewing"
27025 * is shown.
27026 *
27027 * Note that when you specify offsets, you must provide explicit number rules for
27028 * numbers from 0 up to and including the offset. If you use an offset of 3, for example,
27029 * you must provide explicit number rules for 0, 1, 2 and 3. You must also provide plural strings for
27030 * plural categories "one" and "other".
27031 *
27032 * @param {string|expression} count The variable to be bound to.
27033 * @param {string} when The mapping between plural category to its corresponding strings.
27034 * @param {number=} offset Offset to deduct from the total number.
27035 *
27036 * @example
27037    <example module="pluralizeExampl
vendor: 4,913 bytes, lines 27037-27151
27037e">
27038      <file name="index.html">
27039        <script>
27040          angular.module('pluralizeExample', [])
27041            .controller('ExampleController', ['$scope', function($scope) {
27042              $scope.person1 = 'Igor';
27043              $scope.person2 = 'Misko';
27044              $scope.personCount = 1;
27045            }]);
27046        </script>
27047        <div ng-controller="ExampleController">
27048          <label>Person 1:<input type="text" ng-model="person1" value="Igor" /></label><br/>
27049          <label>Person 2:<input type="text" ng-model="person2" value="Misko" /></label><br/>
27050          <label>Number of People:<input type="text" ng-model="personCount" value="1" /></label><br/>
27051
27052          <!--- Example with simple pluralization rules for en locale --->
27053          Without Offset:
27054          <ng-pluralize count="personCount"
27055                        when="{'0': 'Nobody is viewing.',
27056                               'one': '1 person is viewing.',
27057                               'other': '{} people are viewing.'}">
27058          </ng-pluralize><br>
27059
27060          <!--- Example with offset --->
27061          With Offset(2):
27062          <ng-pluralize count="personCount" offset=2
27063                        when="{'0': 'Nobody is viewing.',
27064                               '1': '{{person1}} is viewing.',
27065                               '2': '{{person1}} and {{person2}} are viewing.',
27066                               'one': '{{person1}}, {{person2}} and one other person are viewing.',
27067                               'other': '{{person1}}, {{person2}} and {} other people are viewing.'}">
27068          </ng-pluralize>
27069        </div>
27070      </file>
27071      <file name="protractor.js" type="protractor">
27072        it('should show correct pluralized string', function() {
27073          var withoutOffset = element.all(by.css('ng-pluralize')).get(0);
27074          var withOffset = element.all(by.css('ng-pluralize')).get(1);
27075          var countInput = element(by.model('personCount'));
27076
27077          expect(withoutOffset.getText()).toEqual('1 person is viewing.');
27078          expect(withOffset.getText()).toEqual('Igor is viewing.');
27079
27080          countInput.clear();
27081          countInput.sendKeys('0');
27082
27083          expect(withoutOffset.getText()).toEqual('Nobody is viewing.');
27084          expect(withOffset.getText()).toEqual('Nobody is viewing.');
27085
27086          countInput.clear();
27087          countInput.sendKeys('2');
27088
27089          expect(withoutOffset.getText()).toEqual('2 people are viewing.');
27090          expect(withOffset.getText()).toEqual('Igor and Misko are viewing.');
27091
27092          countInput.clear();
27093          countInput.sendKeys('3');
27094
27095          expect(withoutOffset.getText()).toEqual('3 people are viewing.');
27096          expect(withOffset.getText()).toEqual('Igor, Misko and one other person are viewing.');
27097
27098          countInput.clear();
27099          countInput.sendKeys('4');
27100
27101          expect(withoutOffset.getText()).toEqual('4 people are viewing.');
27102          expect(withOffset.getText()).toEqual('Igor, Misko and 2 other people are viewing.');
27103        });
27104        it('should show data-bound names', function() {
27105          var withOffset = element.all(by.css('ng-pluralize')).get(1);
27106          var personCount = element(by.model('personCount'));
27107          var person1 = element(by.model('person1'));
27108          var person2 = element(by.model('person2'));
27109          personCount.clear();
27110          personCount.sendKeys('4');
27111          person1.clear();
27112          person1.sendKeys('Di');
27113          person2.clear();
27114          person2.sendKeys('Vojta');
27115          expect(withOffset.getText()).toEqual('Di, Vojta and 2 other people are viewing.');
27116        });
27117      </file>
27118    </example>
27119 */
27120var ngPluralizeDirective = ['$locale', '$interpolate', '$log', function($locale, $interpolate, $log) {
27121  var BRACE = /{}/g,
27122      IS_WHEN = /^when(Minus)?(.+)$/;
27123
27124  return {
27125    link: function(scope, element, attr) {
27126      var numberExp = attr.count,
27127          whenExp = attr.$attr.when && element.attr(attr.$attr.when), // we have {{}} in attrs
27128          offset = attr.offset || 0,
27129          whens = scope.$eval(whenExp) || {},
27130          whensExpFns = {},
27131          startSymbol = $interpolate.startSymbol(),
27132          endSymbol = $interpolate.endSymbol(),
27133          braceReplacement = startSymbol + numberExp + '-' + offset + endSymbol,
27134          watchRemover = angular.noop,
27135          lastCount;
27136
27137      forEach(attr, function(expression, attributeName) {
27138        var tmpMatch = IS_WHEN.exec(attributeName);
27139        if (tmpMatch) {
27140          var whenKey = (tmpMatch[1] ? '-' : '') + lowercase(tmpMatch[2]);
27141          whens[whenKey] = element.attr(attr.$attr[attributeName]);
27142        }
27143      });
27144      forEach(whens, function(expression, key) {
27145        whensExpFns[key] = $interpolate(expression.replace(BRACE, braceReplacement));
27146
27147      });
27148
27149      scope.$watch(numberExp, function ngPluralizeWatchAction(newVal) {
27150        var count = parseFloat(newVal);
27151        var countIsNaN = isN
vendor: 10,776 bytes, lines 27151-27390
27151aN(count);
27152
27153        if (!countIsNaN && !(count in whens)) {
27154          // If an explicit number rule such as 1, 2, 3... is defined, just use it.
27155          // Otherwise, check it against pluralization rules in $locale service.
27156          count = $locale.pluralCat(count - offset);
27157        }
27158
27159        // If both `count` and `lastCount` are NaN, we don't need to re-register a watch.
27160        // In JS `NaN !== NaN`, so we have to exlicitly check.
27161        if ((count !== lastCount) && !(countIsNaN && isNumber(lastCount) && isNaN(lastCount))) {
27162          watchRemover();
27163          var whenExpFn = whensExpFns[count];
27164          if (isUndefined(whenExpFn)) {
27165            if (newVal != null) {
27166              $log.debug("ngPluralize: no rule defined for '" + count + "' in " + whenExp);
27167            }
27168            watchRemover = noop;
27169            updateElementText();
27170          } else {
27171            watchRemover = scope.$watch(whenExpFn, updateElementText);
27172          }
27173          lastCount = count;
27174        }
27175      });
27176
27177      function updateElementText(newText) {
27178        element.text(newText || '');
27179      }
27180    }
27181  };
27182}];
27183
27184/**
27185 * @ngdoc directive
27186 * @name ngRepeat
27187 * @multiElement
27188 *
27189 * @description
27190 * The `ngRepeat` directive instantiates a template once per item from a collection. Each template
27191 * instance gets its own scope, where the given loop variable is set to the current collection item,
27192 * and `$index` is set to the item index or key.
27193 *
27194 * Special properties are exposed on the local scope of each template instance, including:
27195 *
27196 * | Variable  | Type            | Details                                                                     |
27197 * |-----------|-----------------|-----------------------------------------------------------------------------|
27198 * | `$index`  | {@type number}  | iterator offset of the repeated element (0..length-1)                       |
27199 * | `$first`  | {@type boolean} | true if the repeated element is first in the iterator.                      |
27200 * | `$middle` | {@type boolean} | true if the repeated element is between the first and last in the iterator. |
27201 * | `$last`   | {@type boolean} | true if the repeated element is last in the iterator.                       |
27202 * | `$even`   | {@type boolean} | true if the iterator position `$index` is even (otherwise false).           |
27203 * | `$odd`    | {@type boolean} | true if the iterator position `$index` is odd (otherwise false).            |
27204 *
27205 * <div class="alert alert-info">
27206 *   Creating aliases for these properties is possible with {@link ng.directive:ngInit `ngInit`}.
27207 *   This may be useful when, for instance, nesting ngRepeats.
27208 * </div>
27209 *
27210 *
27211 * # Iterating over object properties
27212 *
27213 * It is possible to get `ngRepeat` to iterate over the properties of an object using the following
27214 * syntax:
27215 *
27216 * ```js
27217 * <div ng-repeat="(key, value) in myObj"> ... </div>
27218 * ```
27219 *
27220 * You need to be aware that the JavaScript specification does not define the order of keys
27221 * returned for an object. (To mitigate this in Angular 1.3 the `ngRepeat` directive
27222 * used to sort the keys alphabetically.)
27223 *
27224 * Version 1.4 removed the alphabetic sorting. We now rely on the order returned by the browser
27225 * when running `for key in myObj`. It seems that browsers generally follow the strategy of providing
27226 * keys in the order in which they were defined, although there are exceptions when keys are deleted
27227 * and reinstated. See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/delete#Cross-browser_issues
27228 *
27229 * If this is not desired, the recommended workaround is to convert your object into an array
27230 * that is sorted into the order that you prefer before providing it to `ngRepeat`.  You could
27231 * do this with a filter such as [toArrayFilter](http://ngmodules.org/modules/angular-toArrayFilter)
27232 * or implement a `$watch` on the object yourself.
27233 *
27234 *
27235 * # Tracking and Duplicates
27236 *
27237 * When the contents of the collection change, `ngRepeat` makes the corresponding changes to the DOM:
27238 *
27239 * * When an item is added, a new instance of the template is added to the DOM.
27240 * * When an item is removed, its template instance is removed from the DOM.
27241 * * When items are reordered, their respective templates are reordered in the DOM.
27242 *
27243 * By default, `ngRepeat` does not allow duplicate items in arrays. This is because when
27244 * there are duplicates, it is not possible to maintain a one-to-one mapping between collection
27245 * items and DOM elements.
27246 *
27247 * If you do need to repeat duplicate items, you can substitute the default tracking behavior
27248 * with your own using the `track by` expression.
27249 *
27250 * For example, you may track items by the index of each item in the collection, using the
27251 * special scope property `$index`:
27252 * ```html
27253 *    <div ng-repeat="n in [42, 42, 43, 43] track by $index">
27254 *      {{n}}
27255 *    </div>
27256 * ```
27257 *
27258 * You may use arbitrary expressions in `track by`, including references to custom functions
27259 * on the scope:
27260 * ```html
27261 *    <div ng-repeat="n in [42, 42, 43, 43] track by myTrackingFunction(n)">
27262 *      {{n}}
27263 *    </div>
27264 * ```
27265 *
27266 * If you are working with objects that have an identifier property, you can track
27267 * by the identifier instead of the whole object. Should you reload your data later, `ngRepeat`
27268 * will not have to rebuild the DOM elements for items it has already rendered, even if the
27269 * JavaScript objects in the collection have been substituted for new ones:
27270 * ```html
27271 *    <div ng-repeat="model in collection track by model.id">
27272 *      {{model.name}}
27273 *    </div>
27274 * ```
27275 *
27276 * When no `track by` expression is provided, it is equivalent to tracking by the built-in
27277 * `$id` function, which tracks items by their identity:
27278 * ```html
27279 *    <div ng-repeat="obj in collection track by $id(obj)">
27280 *      {{obj.prop}}
27281 *    </div>
27282 * ```
27283 *
27284 * <div class="alert alert-warning">
27285 * **Note:** `track by` must always be the last expression:
27286 * </div>
27287 * ```
27288 * <div ng-repeat="model in collection | orderBy: 'id' as filtered_result track by model.id">
27289 *     {{model.name}}
27290 * </div>
27291 * ```
27292 *
27293 * # Special repeat start and end points
27294 * To repeat a series of elements instead of just one parent element, ngRepeat (as well as other ng directives) supports extending
27295 * the range of the repeater by defining explicit start and end points by using **ng-repeat-start** and **ng-repeat-end** respectively.
27296 * 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)
27297 * up to and including the ending HTML tag where **ng-repeat-end** is placed.
27298 *
27299 * The example below makes use of this feature:
27300 * ```html
27301 *   <header ng-repeat-start="item in items">
27302 *     Header {{ item }}
27303 *   </header>
27304 *   <div class="body">
27305 *     Body {{ item }}
27306 *   </div>
27307 *   <footer ng-repeat-end>
27308 *     Footer {{ item }}
27309 *   </footer>
27310 * ```
27311 *
27312 * And with an input of {@type ['A','B']} for the items variable in the example above, the output will evaluate to:
27313 * ```html
27314 *   <header>
27315 *     Header A
27316 *   </header>
27317 *   <div class="body">
27318 *     Body A
27319 *   </div>
27320 *   <footer>
27321 *     Footer A
27322 *   </footer>
27323 *   <header>
27324 *     Header B
27325 *   </header>
27326 *   <div class="body">
27327 *     Body B
27328 *   </div>
27329 *   <footer>
27330 *     Footer B
27331 *   </footer>
27332 * ```
27333 *
27334 * The custom start and end points for ngRepeat also support all other HTML directive syntax flavors provided in AngularJS (such
27335 * as **data-ng-repeat-start**, **x-ng-repeat-start** and **ng:repeat-start**).
27336 *
27337 * @animations
27338 * **.enter** - when a new item is added to the list or when an item is revealed after a filter
27339 *
27340 * **.leave** - when an item is removed from the list or when an item is filtered out
27341 *
27342 * **.move** - when an adjacent item is filtered out causing a reorder or when the item contents are reordered
27343 *
27344 * @element ANY
27345 * @scope
27346 * @priority 1000
27347 * @param {repeat_expression} ngRepeat The expression indicating how to enumerate a collection. These
27348 *   formats are currently supported:
27349 *
27350 *   * `variable in expression` – where variable is the user defined loop variable and `expression`
27351 *     is a scope expression giving the collection to enumerate.
27352 *
27353 *     For example: `album in artist.albums`.
27354 *
27355 *   * `(key, value) in expression` – where `key` and `value` can be any user defined identifiers,
27356 *     and `expression` is the scope expression giving the collection to enumerate.
27357 *
27358 *     For example: `(name, age) in {'adam':10, 'amalie':12}`.
27359 *
27360 *   * `variable in expression track by tracking_expression` – You can also provide an optional tracking expression
27361 *     which can be used to associate the objects in the collection with the DOM elements. If no tracking expression
27362 *     is specified, ng-repeat associates elements by identity. It is an error to have
27363 *     more than one tracking expression value resolve to the same key. (This would mean that two distinct objects are
27364 *     mapped to the same DOM element, which is not possible.)
27365 *
27366 *     Note that the tracking expression must come last, after any filters, and the alias expression.
27367 *
27368 *     For example: `item in items` is equivalent to `item in items track by $id(item)`. This implies that the DOM elements
27369 *     will be associated by item identity in the array.
27370 *
27371 *     For example: `item in items track by $id(item)`. A built in `$id()` function can be used to assign a unique
27372 *     `$$hashKey` property to each item in the array. This property is then used as a key to associated DOM elements
27373 *     with the corresponding item in the array by identity. Moving the same object in array would move the DOM
27374 *     element in the same way in the DOM.
27375 *
27376 *     For example: `item in items track by item.id` is a typical pattern when the items come from the database. In this
27377 *     case the object identity does not matter. Two objects are considered equivalent as long as their `id`
27378 *     property is same.
27379 *
27380 *     For example: `item in items | filter:searchText track by item.id` is a pattern that might be used to apply a filter
27381 *     to items in conjunction with a tracking expression.
27382 *
27383 *   * `variable in expression as alias_expression` – You can also provide an optional alias expression which will then store the
27384 *     intermediate results of the repeater after the filters have been applied. Typically this is used to render a special message
27385 *     when a filter is active on the repeater, but the filtered result set is empty.
27386 *
27387 *     For example: `item in items | filter:x as results` will store the fragment of the repeated items as `results`, but only after
27388 *     the items have been processed through the filter.
27389 *
27390 *     Please note that `as [variable name] is not an operator but rather a part of ngRepeat micro-syntax s
27390o it can be used only at the end
27391 *     (and not as operator, inside an expression).
27392 *
27393 *     For example: `item in items | filter : x | orderBy : order | limitTo : limit as results` .
27394 *
27395 * @example
27396 * This example initializes the scope to a list of names and
27397 * then uses `ngRepeat` to display every person:
27398  <example module="ngAnimate" deps="angular-animate.js" animations="true">
27399    <file name="index.html">
27400      <div ng-init="friends = [
27401        {name:'John', age:25, gender:'boy'},
27402        {name:'Jessie', age:30, gender:'girl'},
27403        {name:'Johanna', age:28, gender:'girl'},
27404        {name:'Joy', age:15, gender:'girl'},
27405        {name:'Mary', age:28, gender:'girl'},
27406        {name:'Peter', age:95, gender:'boy'},
27407        {name:'Sebastian', age:50, gender:'boy'},
27408        {name:'Erika', age:27, gender:'girl'},
27409        {name:'Patrick', age:40, gender:'boy'},
27410        {name:'Samantha', age:60, gender:'girl'}
27411      ]">
27412        I have {{friends.length}} friends. They are:
27413        <input type="search" ng-model="q" placeholder="filter friends..." aria-label="filter friends" />
27414        <ul class="example-animate-container">
27415          <li class="animate-repeat" ng-repeat="friend in friends | filter:q as results">
27416            [{{$index + 1}}] {{friend.name}} who is {{friend.age}} years old.
27417          </li>
27418          <li class="animate-repeat" ng-if="results.length == 0">
27419            <strong>No results found...</strong>
27420          </li>
27421        </ul>
27422      </div>
27423    </file>
27424    <file name="animations.css">
27425      .example-animate-container {
27426        background:white;
27427        border:1px solid black;
27428        list-style:none;
27429        margin:0;
27430        padding:0 10px;
27431      }
27432
27433      .animate-repeat {
27434        line-height:40px;
27435        list-style:none;
27436        box-sizing:border-box;
27437      }
27438
27439      .animate-repeat.ng-move,
27440      .animate-repeat.ng-enter,
27441      .animate-repeat.ng-leave {
27442        transition:all linear 0.5s;
27443      }
27444
27445      .animate-repeat.ng-leave.ng-leave-active,
27446      .animate-repeat.ng-move,
27447      .animate-repeat.ng-enter {
27448        opacity:0;
27449        max-height:0;
27450      }
27451
27452      .animate-repeat.ng-leave,
27453      .animate-repeat.ng-move.ng-move-active,
27454      .animate-repeat.ng-enter.ng-enter-active {
27455        opacity:1;
27456        max-height:40px;
27457      }
27458    </file>
27459    <file name="protractor.js" type="protractor">
27460      var friends = element.all(by.repeater('friend in friends'));
27461
27462      it('should render initial data set', function() {
27463        expect(friends.count()).toBe(10);
27464        expect(friends.get(0).getText()).toEqual('[1] John who is 25 years old.');
27465        expect(friends.get(1).getText()).toEqual('[2] Jessie who is 30 years old.');
27466        expect(friends.last().getText()).toEqual('[10] Samantha who is 60 years old.');
27467        expect(element(by.binding('friends.length')).getText())
27468            .toMatch("I have 10 friends. They are:");
27469      });
27470
27471       it('should update repeater when filter predicate changes', function() {
27472         expect(friends.count()).toBe(10);
27473
27474         element(by.model('q')).sendKeys('ma');
27475
27476         expect(friends.count()).toBe(2);
27477         expect(friends.get(0).getText()).toEqual('[1] Mary who is 28 years old.');
27478         expect(friends.last().getText()).toEqual('[2] Samantha who is 60 years old.');
27479       });
27480      </file>
27481    </example>
27482 */
27483var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
27484  var NG_REMOVED = '$$NG_REMOVED';
27485  var ngRepeatMinErr = minErr('ngRepeat');
27486
27487  var updateScope = function(scope, index, valueIdentifier, value, keyIdentifier, key, arrayLength) {
27488    // TODO(perf): generate setters to shave off ~40ms or 1-1.5%
27489    scope[valueIdentifier] = value;
27490    if (keyIdentifier) scope[keyIdentifier] = key;
27491    scope.$index = index;
27492    scope.$first = (index === 0);
27493    scope.$last = (index === (arrayLength - 1));
27494    scope.$middle = !(scope.$first || scope.$last);
27495    // jshint bitwise: false
27496    scope.$odd = !(scope.$even = (index&1) === 0);
27497    // jshint bitwise: true
27498  };
27499
27500  var getBlockStart = function(block) {
27501    return block.clone[0];
27502  };
27503
27504  var getBlockEnd = function(block) {
27505    return block.clone[block.clone.length - 1];
27506  };
27507
27508
27509  return {
27510    restrict: 'A',
27511    multiElement: true,
27512    transclude: 'element',
27513    priority: 1000,
27514    terminal: true,
27515    $$tlb: true,
27516    compile: function ngRepeatCompile($element, $attr) {
27517      var expression = $attr.ngRepeat;
27518      var ngRepeatEndComment = document.createComment(' end ngRepeat: ' + expression + ' ');
27519
27520      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*$/);
27521
27522      if (!match) {
27523        throw ngRepeatMinErr('iexp', "Expected expression in form of '_item_ in _collection_[ track by _id_]' but got '{0}'.",
27524            expression);
27525      }
27526
27527      var lhs = match[1];
27528      var rhs = match[2];
27529      var aliasAs = match[3];
27530      var trackByExp = match[4];
27531
27532      match = lhs.match(/^(?:(\s*[\$\w]+)|\(\s*([\$\w]+)\s*,\s*([\$\w]+)\s*\))$/);
27533
27534      if (!match) {
27535        throw ngRepeatMinErr('iidexp', "'_item_' in '_item_ in _collection_' should be an identifier or '(_key_, _value_)' expression, but got '{0}'.",
27536            lhs);
27537      }
27538      var valueIdentifier = match[3] || match[1];
27539      var keyIdentifier = match[2];
27540
27541      if (aliasAs && (!/^[$a-zA-Z_][$a-zA-Z0-9_]*$/.test(aliasAs) ||
27542          /^(null|undefined|this|\$index|\$first|\$middle|\$last|\$even|\$odd|\$parent|\$root|\$id)$/.test(aliasAs))) {
27543        throw ngRepeatMinErr('badident', "alias '{0}
vendor: 11,933 bytes, lines 27543-27815
27543' is invalid --- must be a valid JS identifier which is not a reserved name.",
27544          aliasAs);
27545      }
27546
27547      var trackByExpGetter, trackByIdExpFn, trackByIdArrayFn, trackByIdObjFn;
27548      var hashFnLocals = {$id: hashKey};
27549
27550      if (trackByExp) {
27551        trackByExpGetter = $parse(trackByExp);
27552      } else {
27553        trackByIdArrayFn = function(key, value) {
27554          return hashKey(value);
27555        };
27556        trackByIdObjFn = function(key) {
27557          return key;
27558        };
27559      }
27560
27561      return function ngRepeatLink($scope, $element, $attr, ctrl, $transclude) {
27562
27563        if (trackByExpGetter) {
27564          trackByIdExpFn = function(key, value, index) {
27565            // assign key, value, and $index to the locals so that they can be used in hash functions
27566            if (keyIdentifier) hashFnLocals[keyIdentifier] = key;
27567            hashFnLocals[valueIdentifier] = value;
27568            hashFnLocals.$index = index;
27569            return trackByExpGetter($scope, hashFnLocals);
27570          };
27571        }
27572
27573        // Store a list of elements from previous run. This is a hash where key is the item from the
27574        // iterator, and the value is objects with following properties.
27575        //   - scope: bound scope
27576        //   - element: previous element.
27577        //   - index: position
27578        //
27579        // We are using no-proto object so that we don't need to guard against inherited props via
27580        // hasOwnProperty.
27581        var lastBlockMap = createMap();
27582
27583        //watch props
27584        $scope.$watchCollection(rhs, function ngRepeatAction(collection) {
27585          var index, length,
27586              previousNode = $element[0],     // node that cloned nodes should be inserted after
27587                                              // initialized to the comment node anchor
27588              nextNode,
27589              // Same as lastBlockMap but it has the current state. It will become the
27590              // lastBlockMap on the next iteration.
27591              nextBlockMap = createMap(),
27592              collectionLength,
27593              key, value, // key/value of iteration
27594              trackById,
27595              trackByIdFn,
27596              collectionKeys,
27597              block,       // last object information {scope, element, id}
27598              nextBlockOrder,
27599              elementsToRemove;
27600
27601          if (aliasAs) {
27602            $scope[aliasAs] = collection;
27603          }
27604
27605          if (isArrayLike(collection)) {
27606            collectionKeys = collection;
27607            trackByIdFn = trackByIdExpFn || trackByIdArrayFn;
27608          } else {
27609            trackByIdFn = trackByIdExpFn || trackByIdObjFn;
27610            // if object, extract keys, in enumeration order, unsorted
27611            collectionKeys = [];
27612            for (var itemKey in collection) {
27613              if (hasOwnProperty.call(collection, itemKey) && itemKey.charAt(0) !== '$') {
27614                collectionKeys.push(itemKey);
27615              }
27616            }
27617          }
27618
27619          collectionLength = collectionKeys.length;
27620          nextBlockOrder = new Array(collectionLength);
27621
27622          // locate existing items
27623          for (index = 0; index < collectionLength; index++) {
27624            key = (collection === collectionKeys) ? index : collectionKeys[index];
27625            value = collection[key];
27626            trackById = trackByIdFn(key, value, index);
27627            if (lastBlockMap[trackById]) {
27628              // found previously seen block
27629              block = lastBlockMap[trackById];
27630              delete lastBlockMap[trackById];
27631              nextBlockMap[trackById] = block;
27632              nextBlockOrder[index] = block;
27633            } else if (nextBlockMap[trackById]) {
27634              // if collision detected. restore lastBlockMap and throw an error
27635              forEach(nextBlockOrder, function(block) {
27636                if (block && block.scope) lastBlockMap[block.id] = block;
27637              });
27638              throw ngRepeatMinErr('dupes',
27639                  "Duplicates in a repeater are not allowed. Use 'track by' expression to specify unique keys. Repeater: {0}, Duplicate key: {1}, Duplicate value: {2}",
27640                  expression, trackById, value);
27641            } else {
27642              // new never before seen block
27643              nextBlockOrder[index] = {id: trackById, scope: undefined, clone: undefined};
27644              nextBlockMap[trackById] = true;
27645            }
27646          }
27647
27648          // remove leftover items
27649          for (var blockKey in lastBlockMap) {
27650            block = lastBlockMap[blockKey];
27651            elementsToRemove = getBlockNodes(block.clone);
27652            $animate.leave(elementsToRemove);
27653            if (elementsToRemove[0].parentNode) {
27654              // if the element was not removed yet because of pending animation, mark it as deleted
27655              // so that we can ignore it later
27656              for (index = 0, length = elementsToRemove.length; index < length; index++) {
27657                elementsToRemove[index][NG_REMOVED] = true;
27658              }
27659            }
27660            block.scope.$destroy();
27661          }
27662
27663          // we are not using forEach for perf reasons (trying to avoid #call)
27664          for (index = 0; index < collectionLength; index++) {
27665            key = (collection === collectionKeys) ? index : collectionKeys[index];
27666            value = collection[key];
27667            block = nextBlockOrder[index];
27668
27669            if (block.scope) {
27670              // if we have already seen this object, then we need to reuse the
27671              // associated scope/element
27672
27673              nextNode = previousNode;
27674
27675              // skip nodes that are already pending removal via leave animation
27676              do {
27677                nextNode = nextNode.nextSibling;
27678              } while (nextNode && nextNode[NG_REMOVED]);
27679
27680              if (getBlockStart(block) != nextNode) {
27681                // existing item which got moved
27682                $animate.move(getBlockNodes(block.clone), null, jqLite(previousNode));
27683              }
27684              previousNode = getBlockEnd(block);
27685              updateScope(block.scope, index, valueIdentifier, value, keyIdentifier, key, collectionLength);
27686            } else {
27687              // new item which we don't know about
27688              $transclude(function ngRepeatTransclude(clone, scope) {
27689                block.scope = scope;
27690                // http://jsperf.com/clone-vs-createcomment
27691                var endNode = ngRepeatEndComment.cloneNode(false);
27692                clone[clone.length++] = endNode;
27693
27694                // TODO(perf): support naked previousNode in `enter` to avoid creation of jqLite wrapper?
27695                $animate.enter(clone, null, jqLite(previousNode));
27696                previousNode = endNode;
27697                // Note: We only need the first/last node of the cloned nodes.
27698                // However, we need to keep the reference to the jqlite wrapper as it might be changed later
27699                // by a directive with templateUrl when its template arrives.
27700                block.clone = clone;
27701                nextBlockMap[block.id] = block;
27702                updateScope(block.scope, index, valueIdentifier, value, keyIdentifier, key, collectionLength);
27703              });
27704            }
27705          }
27706          lastBlockMap = nextBlockMap;
27707        });
27708      };
27709    }
27710  };
27711}];
27712
27713var NG_HIDE_CLASS = 'ng-hide';
27714var NG_HIDE_IN_PROGRESS_CLASS = 'ng-hide-animate';
27715/**
27716 * @ngdoc directive
27717 * @name ngShow
27718 * @multiElement
27719 *
27720 * @description
27721 * The `ngShow` directive shows or hides the given HTML element based on the expression
27722 * provided to the `ngShow` attribute. The element is shown or hidden by removing or adding
27723 * the `.ng-hide` CSS class onto the element. The `.ng-hide` CSS class is predefined
27724 * in AngularJS and sets the display style to none (using an !important flag).
27725 * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
27726 *
27727 * ```html
27728 * <!-- when $scope.myValue is truthy (element is visible) -->
27729 * <div ng-show="myValue"></div>
27730 *
27731 * <!-- when $scope.myValue is falsy (element is hidden) -->
27732 * <div ng-show="myValue" class="ng-hide"></div>
27733 * ```
27734 *
27735 * When the `ngShow` expression evaluates to a falsy value then the `.ng-hide` CSS class is added to the class
27736 * attribute on the element causing it to become hidden. When truthy, the `.ng-hide` CSS class is removed
27737 * from the element causing the element not to appear hidden.
27738 *
27739 * ## Why is !important used?
27740 *
27741 * You may be wondering why !important is used for the `.ng-hide` CSS class. This is because the `.ng-hide` selector
27742 * can be easily overridden by heavier selectors. For example, something as simple
27743 * as changing the display style on a HTML list item would make hidden elements appear visible.
27744 * This also becomes a bigger issue when dealing with CSS frameworks.
27745 *
27746 * By using !important, the show and hide behavior will work as expected despite any clash between CSS selector
27747 * specificity (when !important isn't used with any conflicting styles). If a developer chooses to override the
27748 * styling to change how to hide an element then it is just a matter of using !important in their own CSS code.
27749 *
27750 * ### Overriding `.ng-hide`
27751 *
27752 * By default, the `.ng-hide` class will style the element with `display: none!important`. If you wish to change
27753 * the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide`
27754 * class CSS. Note that the selector that needs to be used is actually `.ng-hide:not(.ng-hide-animate)` to cope
27755 * with extra animation classes that can be added.
27756 *
27757 * ```css
27758 * .ng-hide:not(.ng-hide-animate) {
27759 *   /&#42; this is just another form of hiding an element &#42;/
27760 *   display: block!important;
27761 *   position: absolute;
27762 *   top: -9999px;
27763 *   left: -9999px;
27764 * }
27765 * ```
27766 *
27767 * By default you don't need to override in CSS anything and the animations will work around the display style.
27768 *
27769 * ## A note about animations with `ngShow`
27770 *
27771 * Animations in ngShow/ngHide work with the show and hide events that are triggered when the directive expression
27772 * is true and false. This system works like the animation system present with ngClass except that
27773 * you must also include the !important flag to override the display property
27774 * so that you can perform an animation when the element is hidden during the time of the animation.
27775 *
27776 * ```css
27777 * //
27778 * //a working example can be found at the bottom of this page
27779 * //
27780 * .my-element.ng-hide-add, .my-element.ng-hide-remove {
27781 *   /&#42; this is required as of 1.3x to properly
27782 *      apply all styling in a show/hide animation &#42;/
27783 *   transition: 0s linear all;
27784 * }
27785 *
27786 * .my-element.ng-hide-add-active,
27787 * .my-element.ng-hide-remove-active {
27788 *   /&#42; the transition is defined in the active class &#42;/
27789 *   transition: 1s linear all;
27790 * }
27791 *
27792 * .my-element.ng-hide-add { ... }
27793 * .my-element.ng-hide-add.ng-hide-add-active { ... }
27794 * .my-element.ng-hide-remove { ... }
27795 * .my-element.ng-hide-remove.ng-hide-remove-active { ... }
27796 * ```
27797 *
27798 * Keep in mind that, as of AngularJS version 1.3.0-beta.11, there is no need to change the display
27799 * property to block during animation states--ngAnimate will handle the style toggling automatically for you.
27800 *
27801 * @animations
27802 * addClass: `.ng-hide` - happens after the `ngShow` expression evaluates to a truthy value and the just before contents are set to visible
27803 * removeClass: `.ng-hide` - happens after the `ngShow` expression evaluates to a non truthy value and just before the contents are set to hidden
27804 *
27805 * @element ANY
27806 * @param {expression} ngShow If the {@link guide/expression expression} is truthy
27807 *     then the element is shown or hidden respectively.
27808 *
27809 * @example
27810  <example module="ngAnimate" deps="angular-animate.js" animations="true">
27811    <file name="index.html">
27812      Click me: <input type="checkbox" ng-model="checked" aria-label="Toggle ngHide"><br/>
27813      <div>
27814        Show:
27815        <div class="check-element animate-show" ng-show="checked">
27816          <span class="glyphicon glyphicon-thumbs-up"></span> I show up when your checkbox is checked.
27817        </div>
27818      </div>
27819      <div>
27820        Hide:
27821        <div class="check-element animate-show" ng-hide="checked">
27822          <span class="glyphicon glyphicon-thumbs-down"></span> I hide when your checkbox is checked.
27823        </div>
27824      </div>
27825    </file>
27826    <file name="glyphicons.css">
27827      @import url(../../components/bootstrap-3.1.1/css/bootstrap.css);
27828    </file>
27829    <file name="animations.css">
27830      .animate-show {
27831        line-height: 20px;
27832        opacity: 1;
27833        padding: 10px;
27834        border: 1px solid black;
27835        background: white;
27836      }
27837
27838      .animate-show.ng-hide-add, .animate-show.ng-hide-remove {
27839        transition: all linear 0.5s;
27840      }
27841
27842      .animate-show.ng-hide {
27843        line-height: 0;
27844        opacity: 0;
27845        padding: 0 10px;
27846      }
27847
27848      .check-element {
27849        padding: 10px;
27850        border: 1px solid black;
27851        background: white;
27852      }
27853    </file>
27854    <file name="protractor.js" type="protractor">
27855      var thumbsUp = element(by.css('span.glyphicon-thumbs-up'));
27856      var thumbsDown = element(by.css('span.glyphicon-thumbs-down'));
27857
27858      it('should check ng-show / ng-hide', function() {
27859        expect(thumbsUp.isDisplayed()).toBeFalsy();
27860        expect(thumbsDown.isDisplayed()).toBeTruthy();
27861
27862        element(by.model('checked')).click();
27863
27864        expect(thumbsUp.isDisplayed()).toBeTruthy();
27865        expect(thumbsDown.isDisplayed()).toBeFalsy();
27866      });
27867    </file>
27868  </example>
27869 */
27870var ngShowDirective = ['$animate', function($animate) {
27871  return {
27872    restrict: 'A',
27873    multiElement: true,
27874    link: function(scope, element, attr) {
27875      scope.$watch(attr.ngShow, function ngShowWatchAction(value) {
27876        // we're adding a temporary, animation-specific class for ng-hide since this way
27877        // we can control when the element is actually displayed on screen without having
27878        // to have a global/greedy CSS selector that breaks when other animations are run.
27879        // Read: https://github.com/angular/angular.js/issues/9103#issuecomment-58335845
27880        $animate[value ? 'removeClass' : 'addClass'](element, NG_HIDE_CLASS, {
27881          tempClasses: NG_HIDE_IN_PROGRESS_CLASS
27882        });
27883      });
27884    }
27885  };
27886}];
27887
27888
27889/**
27890 * @ngdoc directive
27891 * @name ngHide
27892 * @multiElement
27893 *
27894 * @description
27895 * The `ngHide` directive shows or hides the given HTML element based on the expression
27896 * provided to the `ngHide` attribute. The element is shown or hidden by removing or adding
27897 * the `ng-hide` CSS class onto the element. The `.ng-hide` CSS class is predefined
27898 * in AngularJS and sets the display style to none (using an !important flag).
27899 * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
27900 *
27901 * ```html
27902 * <!-- when $scope.myValue is truthy (element is hidden) -->
27903 * <div ng-hide="myValue" class="ng-hide"></div>
27904 *
27905 * <!-- when $scope.myValue is falsy (element is visible) -->
27906 * <div ng-hide="myValue"></div>
27907 * ```
27908 *
27909 * When the `ngHide` expression evaluates to a truthy value then the `.ng-hide` CSS class is added to the class
27910 * attribute on the element causing it to become hidden. When falsy, the `.ng-hide` CSS class is removed
27911 * from the element causing the element not to appear hidden.
27912 *
27913 * ## Why is !important used?
27914 *
27915 * You may be wondering why !important is used for the `.ng-hide` CSS class. This is because the `.ng-hide` selector
27916 * can be easily overridden by heavier selectors. For example, something as simple
27917 * as changing the display style on a HTML list item would make hidden elements appear visible.
27918 * This also becomes a bigger issue when dealing with CSS frameworks.
27919 *
27920 * By using !important, the show and hide behavior will work as expected despite any clash between CSS selector
27921 * specificity (when !important isn't used with any conflicting styles). If a developer chooses to override the
27922 * styling to change how to hide an element then it is just a matter of using !important in their own CSS code.
27923 *
27924 * ### Overriding `.ng-hide`
27925 *
27926 * By default, the `.ng-hide` class will style the element with `display: none!important`. If you wish to change
27927 * the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide`
27928 * class in CSS:
27929 *
27930 * ```css
27931 * .ng-hide {
27932 *   /&#42; this is just another form of hiding an element &#42;/
27933 *   display: block!important;
27934 *   position: absolute;
27935 *   top: -9999px;
27936 *   left: -9999px;
27937 * }
27938 * ```
27939 *
27940 * By default you don't need to override in CSS anything and the animations will work around the display style.
27941 *
27942 * ## A note about animations with `ngHide`
27943 *
27944 * Animations in ngShow/ngHide work with the show and hide events that are triggered when the directive expression
27945 * is true and false. This system works like the animation system present with ngClass, except that the `.ng-hide`
27946 * CSS class is added and removed for you instead of your own CSS class.
27947 *
27948 * ```css
27949 * //
27950 * //a working example can be found at the bottom of this page
27951 * //
27952 * .my-element.ng-hide-add, .my-element.ng-hide-remove {
27953 *   transition: 0.5s linear all;
27954 * }
27955 *
27956 * .my-element.ng-hide-add { ... }
27957 * .my-element.ng-hide-add.ng-hide-add-active { ... }
27958 * .my-element.ng-hide-remove { ... }
27959 * .my-element.ng-hide-remove.ng-hide-remove-active { ... }
27960 * ```
27961 *
27962 * Keep in mind that, as of AngularJS version 1.3.0-beta.11, there is no need to change the display
27963 * property to block during animation states--ngAnimate will handle the style toggling automatically for you.
27964 *
27965 * @animations
27966 * removeClass: `.ng-hide` - happens after the `ngHide` expression evaluates to a truthy value and just before the contents are set to hidden
27967 * addClass: `.ng-hide` - happens after the `ngHide` expression evaluates to a non truthy value and just before the contents are set to visible
27968 *
27969 * @element ANY
27970 * @param {expression} ngHide If the {@link guide/expression expression} is truthy then
27971 *     the element is shown or hidden respectively.
27972 *
27973 * @example
27974  <example module="ngAnimate" deps="angular-animate.js" animations="true">
27975    <file name="index.html">
27976      Click me: <input type="checkbox" ng-model="checked" aria-label="Toggle ngShow"><br/>
27977      <div>
27978        Show:
27979        <div class="check-element animate-hide" ng-show="checked">
27980          <span class="glyphicon glyphicon-thumbs-up"></span> I show up when your checkbox is checked.
27981        </div>
27982      </div>
27983      <div>
27984        Hide:
27985        <div class="check-element animate-hide" ng-hide="checked">
27986          <span class="glyphicon glyphicon-thumbs-down"></span> I hide when your checkbox is checked.
27987        </div>
27988      </div>
27989    </file>
27990    <file name="glyphicons.css">
27991      @import url(../../components/bootstrap-3.1.1/css/bootstrap.css);
27992    </file>
27993    <file name="animations.css">
27994      .animate-hide {
27995        transition: all linear 0.5s;
27996        line-height: 20px;
27997        opacity: 1;
27998        padding: 10px;
27999        border: 1px solid black;
28000        background: white;
28001      }
28002
28003      .animate-hide.ng-hide {
28004        line-height: 0;
28005        opacity: 0;
28006        padding: 0 10px;
28007      }
28008
28009      .check-element {
28010        padding: 10px;
28011        border: 1px solid black;
28012        background: white;
28013      }
28014    </file>
28015    <file name="protractor.js" type="protractor">
28016      var thumbsUp = element(by.css('span.glyphicon-thumbs-up'));
28017      var thumbsDown = element(by.css('span.glyphicon-thumbs-down'));
28018
28019      it('should check ng-show / ng-hide', function() {
28020        expect(thumbsUp.isDisplayed()).toBeFalsy();
28021        expect(thumbsDown.isDisplayed()).toBeTruthy();
28022
28023        element(by.model('checked')).click();
28024
28025        expect(thumbsUp.isDisplayed()).toBeTruthy();
28026        expect(thumbsDown.isDisplayed()).toBeFalsy();
28027      });
28028    </file>
28029  </example>
28030 */
28031var ngHideDirective = ['$animate', function($animate) {
28032  return {
28033    restrict: 'A',
28034    multiElement: true,
28035    link: function(scope, element, attr) {
28036      scope.$watch(attr.ngHide, function ngHideWatchAction(value) {
28037        // The comment inside of the ngShowDirective explains why we add and
28038        // remove a temporary class for the show/hide animation
28039        $animate[value ? 'addClass' : 'removeClass'](element,NG_HIDE_CLASS, {
28040          tempClasses: NG_HIDE_IN_PROGRESS_CLASS
28041        });
28042      });
28043    }
28044  };
28045}];
28046
28047/**
28048 * @ngdoc directive
28049 * @name ngStyle
28050 * @restrict AC
28051 *
28052 * @description
28053 * The `ngStyle` directive allows you to set CSS style on an HTML element conditionally.
28054 *
28055 * @element ANY
28056 * @param {expression} ngStyle
28057 *
28058 * {@link guide/expression Expression} which evals to an
28059 * object whose keys are CSS style names and values are corresponding values for those CSS
28060 * keys.
28061 *
28062 * Since some CSS style names are not valid keys for an object, they must be quoted.
28063 * See the 'background-color' style in the example below.
28064 *
28065 * @example
28066   <example>
28067     <file name="index.html">
28068        <input type="button" value="set color" ng-click="myStyle={color:'red'}">
28069        <input type="button" value="set background" ng-click="myStyle={'background-color':'blue'}">
28070        <input type="button" value="clear" ng-click="myStyle={}">
28071        <br/>
28072        <span ng-style="myStyle">Sample Text</span>
28073        <pre>myStyle={{myStyle}}</pre>
28074     </file>
28075     <file name="style.css">
28076       span {
28077         color: black;
28078       }
28079     </file>
28080     <file name="protractor.js" type="protractor">
28081       var colorSpan = element(by.css('span'));
28082
28083       it('should check ng-style', function() {
28084         expect(colorSpan.getCssValue('color')).toBe('rgba(0, 0, 0, 1)');
28085         element(by.css('input[value=\'set color\']')).click();
28086         expect(colorSpan.getCssValue('color')).toBe('rgba(255, 0, 0, 1)');
28087         element(by.css('input[value=clear]')).click();
28088         expect(colorSpan.getCssValue('color')).toBe('rgba(0, 0, 0, 1)');
28089       });
28090     </file>
28091   </example>
28092 */
28093var ngStyleDirective = ngDirective(function(scope, element, attr) {
28094  scope.$watch(attr.ngStyle, function ngStyleWatchAction(newStyles, oldStyles) {
28095    if (oldStyles && (newStyles !== oldStyles)) {
28096      forEach(oldStyles, function(val, style) { element.css(style, '');});
28097    }
28098    if (newStyles) element.css(newStyles);
28099  }, true);
28100});
28101
28102/**
28103 * @ngdoc directive
28104 * @name ngSwitch
28105 * @restrict EA
28106 *
28107 * @description
28108 * The `ngSwitch` directive is used to conditionally swap DOM structure on your template based on a scope expression.
28109 * Elements within `ngSwitch` but without `ngSwitchWhen` or `ngSwitchDefault` directives will be preserved at the location
28110 * as specified in the template.
28111 *
28112 * The directive itself works similar to ngInclude, however, instead of downloading template code (or loading it
28113 * from the template cache), `ngSwitch` simply chooses one of the nested elements and makes it visible based on which element
28114 * matches the value obtained from the evaluated expression. In other words, you define a container element
28115 * (where you place the directive), place an expression on the **`on="..."` attribute**
28116 * (or the **`ng-switch="..."` attribute**), define any inner elements inside of the directive and place
28117 * a when attribute per element. The when attribute is used to inform ngSwitch which element to display when the on
28118 * expression is evaluated. If a matching expression is not found via a when attribute then an element with the default
28119 * attribute is displayed.
28120 *
28121 * <div class="alert alert-info">
28122 * Be aware that the attribute values to match against cannot be expressions. They are interpreted
28123 * as literal string values to match against.
28124 * For example, **`ng-switch-when="someVal"`** will match against the string `"s
28124omeVal"` not against the
28125 * value of the expression `$scope.someVal`.
28126 * </div>
28127
28128 * @animations
28129 * enter - happens after the ngSwitch contents change and the matched child element is placed inside the container
28130 * leave - happens just after the ngSwitch contents change and just before the former contents are removed from the DOM
28131 *
28132 * @usage
28133 *
28134 * ```
28135 * <ANY ng-switch="expression">
28136 *   <ANY ng-switch-when="matchValue1">...</ANY>
28137 *   <ANY ng-switch-when="matchValue2">...</ANY>
28138 *   <ANY ng-switch-default>...</ANY>
28139 * </ANY>
28140 * ```
28141 *
28142 *
28143 * @scope
28144 * @priority 1200
28145 * @param {*} ngSwitch|on expression to match against <code>ng-switch-when</code>.
28146 * On child elements add:
28147 *
28148 * * `ngSwitchWhen`: the case statement to match against. If match then this
28149 *   case will be displayed. If the same match appears multiple times, all the
28150 *   elements will be displayed.
28151 * * `ngSwitchDefault`: the default case when no other case match. If there
28152 *   are multiple default cases, all of them will be displayed when no other
28153 *   case match.
28154 *
28155 *
28156 * @example
28157  <example module="switchExample" deps="angular-animate.js" animations="true">
28158    <file name="index.html">
28159      <div ng-controller="ExampleController">
28160        <select ng-model="selection" ng-options="item for item in items">
28161        </select>
28162        <code>selection={{selection}}</code>
28163        <hr/>
28164        <div class="animate-switch-container"
28165          ng-switch on="selection">
28166            <div class="animate-switch" ng-switch-when="settings">Settings Div</div>
28167            <div class="animate-switch" ng-switch-when="home">Home Span</div>
28168            <div class="animate-switch" ng-switch-default>default</div>
28169        </div>
28170      </div>
28171    </file>
28172    <file name="script.js">
28173      angular.module('switchExample', ['ngAnimate'])
28174        .controller('ExampleController', ['$scope', function($scope) {
28175          $scope.items = ['settings', 'home', 'other'];
28176          $scope.selection = $scope.items[0];
28177        }]);
28178    </file>
28179    <file name="animations.css">
28180      .animate-switch-container {
28181        position:relative;
28182        background:white;
28183        border:1px solid black;
28184        height:40px;
28185        overflow:hidden;
28186      }
28187
28188      .animate-switch {
28189        padding:10px;
28190      }
28191
28192      .animate-switch.ng-animate {
28193        transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
28194
28195        position:absolute;
28196        top:0;
28197        left:0;
28198        right:0;
28199        bottom:0;
28200      }
28201
28202      .animate-switch.ng-leave.ng-leave-active,
28203      .animate-switch.ng-enter {
28204        top:-50px;
28205      }
28206      .animate-switch.ng-leave,
28207      .animate-switch.ng-enter.ng-enter-active {
28208        top:0;
28209      }
28210    </file>
28211    <file name="protractor.js" type="protractor">
28212      var switchElem = element(by.css('[ng-switch]'));
28213      var select = element(by.model('selection'));
28214
28215      it('should start in settings', function() {
28216        expect(switchElem.getText()).toMatch(/Settings Div/);
28217      });
28218      it('should change to home', function() {
28219        select.all(by.css('option')).get(1).click();
28220        expect(switchElem.getText()).toMatch(/Home Span/);
28221      });
28222      it('should select default', function() {
28223        select.all(by.css('option')).get(2).click();
28224        expect(switchElem.getText()).toMatch(/default/);
28225      });
28226    </file>
28227  </example>
28228 */
28229var ngSwitchDirective = ['$animate', function($animate) {
28230  return {
28231    require: 'ngSwitch',
28232
28233    // asks for $scope to fool the BC controller module
28234    controller: ['$scope', function ngSwitchController() {
28235     this.cases = {};
28236    }],
28237    link: function(scope, element, attr, ngSwitchController) {
28238      var watchExpr = attr.ngSwitch || attr.on,
28239          selectedTranscludes = [],
28240          selectedElements = [],
28241          previousLeaveAnimations = [],
28242          selectedScopes = [];
28243
28244      var spliceFactory = function(array, index) {
28245          return function() { array.splice(index, 1); };
28246      };
28247
28248      scope.$watch(watchExpr, function ngSwitchWatchAction(value) {
28249        var i, ii;
28250        for (i = 0, ii = previousLeaveAnimations.length; i < ii; ++i) {
28251          $animate.cancel(previousLeaveAnimations[i]);
28252        }
28253        previousLeaveAnimations.length = 0;
28254
28255        for (i = 0, ii = selectedScopes.length; i < ii; ++i) {
28256          var selected = getBlockNodes(selectedElements[i].clone);
28257          selectedScopes[i].$destroy();
28258          var promise = previousLeaveAnimations[i] = $animate.leave(selected);
28259          promise.then(spliceFactory(previousLeaveAnimations, i));
28260        }
28261
28262        selectedElements.length = 0;
28263        selectedScopes.length = 0;
28264
28265        if ((selectedTranscludes = ngSwitchController.cases['!' + value] || ngSwitchController.cases['?'])) {
28266          forEach(selectedTranscludes, function(selectedTransclude) {
28267            selectedTransclude.transclude(function(caseElement, selectedScope) {
28268              selectedScopes.push(selectedScope);
28269              var anchor = selectedTransclude.element;
28270              caseElement[caseElement.length++] = document.createComment(' end ngSwitchWhen: ');
28271              var block = { clone: caseElement };
28272
28273              selectedElements.push(block);
28274              $animate.enter(caseElement, anchor.parent(), anchor);
28275            });
28276          });
28277        }
28278      });
28279    }
28280  };
28281}];
28282
28283var ngSwitchWhenDirective = ngDirective({
28284  transclude: 'element',
28285  priority: 1200,
28286  require: '^ngSwitch',
28287  multiElement: true,
28288  link: function(scope, element, attrs, ctrl, $transclude) {
28289    ctrl.cases['!' + attrs.ngSwitchWhen] = (ctrl.cases['!' + attrs.ngSwitchWhen] || []);
28290    ctrl.cases['!' + attrs.ngSwitchWhen].push({ transclude: $transclude, element: element });
28291  }
28292});
28293
28294var ngSwitchDefaultDirective = ngDirective({
28295  transclude: 'element',
28296  priority: 1200,
28297  require: '^ngSwitch',
28298  multiElement: true,
28299  link: function(scope, element, attr, ctrl, $transclude) {
28300    ctrl.cases['?'] = (ctrl.cases['?'] || []);
28301    ctrl.cases['?'].push({ transclude: $transclude, element: element });
28302   }
28303});
28304
28305/**
28306 * @ngdoc directive
28307 * @name ngTransclude
28308 * @restrict EAC
28309 *
28310 * @description
28311 * Directive that marks the insertion point for the transcluded DOM of the nearest parent directive that uses transclusion.
28312 *
28313 * Any existing content of the element that this directive is placed on will be removed before the transcluded content is inserted.
28314 *
28315 * @element ANY
28316 *
28317 * @example
28318   <example module="transcludeExample">
28319     <file name="index.html">
28320       <script>
28321         angular.module('transcludeExample', [])
28322          .directive('pane', function(){
28323             return {
28324               restrict: 'E',
28325               transclude: true,
28326               scope: { title:'@' },
28327               template: '<div style="border: 1px solid black;">
28327' +
28328                           '<div style="background-color: gray">{{title}}</div>' +
28329                           '<ng-transclude></ng-transclude>' +
28330                         '</div>'
28331             };
28332         })
28333         .controller('ExampleController', ['$scope', function($scope) {
28334           $scope.title = 'Lorem Ipsum';
28335           $scope.text = 'Neque porro quisquam est qui dolorem ipsum quia dolor...';
28336         }]);
28337       </script>
28338       <div ng-controller="ExampleController">
28339         <input ng-model="title" aria-label="title"> <br/>
28340         <textarea ng-model="text" aria-label="text"></textarea> <br/>
28341         <pane title="{{title}}">{{text}}</pane>
28342       </div>
28343     </file>
28344     <file name="protractor.js" type="protractor">
28345        it('should have transcluded', function() {
28346          var titleElement = element(by.model('title'));
28347          titleElement.clear();
28348          titleElement.sendKeys('TITLE');
28349          var textElement = element(by.model('text'));
28350          textElement.clear();
28351          textElement.sendKeys('TEXT');
28352          expect(element(by.binding('title')).getText()).toEqual('TITLE');
28353          expect(element(by.binding('text')).getText()).toEqual('TEXT');
28354        });
28355     </file>
28356   </example>
28357 *
28358 */
28359var ngTranscludeDirective = ngDirective({
28360  restrict: 'EAC',
28361  link: function($scope, $element, $attrs, controller, $transclude) {
28362    if (!$transclude) {
28363      throw minErr('ngTransclude')('orphan',
28364       'Illegal use of ngTransclude directive in the template! ' +
28365       'No parent directive that requires a transclusion found. ' +
28366       'Element: {0}',
28367       startingTag($element));
28368    }
28369
28370    $transclude(function(clone) {
28371      $element.empty();
28372      $element.append(clone);
28373    });
28374  }
28375});
28376
28377/**
28378 * @ngdoc directive
28379 * @name script
28380 * @restrict E
28381 *
28382 * @description
28383 * Load the content of a `<script>` element into {@link ng.$templateCache `$templateCache`}, so that the
28384 * template can be used by {@link ng.directive:ngInclude `ngInclude`},
28385 * {@link ngRoute.directive:ngView `ngView`}, or {@link guide/directive directives}. The type of the
28386 * `<script>` element must be specified as `text/ng-template`, and a cache name for the template must be
28387 * assigned through the element's `id`, which can then be used as a directive's `templateUrl`.
28388 *
28389 * @param {string} type Must be set to `'text/ng-template'`.
28390 * @param {string} id Cache name of the template.
28391 *
28392 * @example
28393  <example>
28394    <file name="index.html">
28395      <script type="text/ng-template" id="/tpl.html">
28396        Content of the template.
28397      </script>
28398
28399      <a ng-click="currentTpl='/tpl.html'" id="tpl-link">Load inlined template</a>
28400      <div id="tpl-content" ng-include src="currentTpl"></div>
28401    </file>
28402    <file name="protractor.js" type="protractor">
28403      it('should load template defined inside script tag', function() {
28404        element(by.css('#tpl-link')).click();
28405        expect(element(by.css('#tpl-content')).getText()).toMatch(/Content of the template/);
28406      });
28407    </file>
28408  </example>
28409 */
28410var scriptDirective = ['$templateCache', function($templateCache) {
28411  return {
28412    restrict: 'E',
28413    terminal: true,
28414    compile: function(element, attr) {
28415      if (attr.type == 'text/ng-template') {
28416        var templateUrl = attr.id,
28417            text = element[0].text;
28418
28419        $templateCache.put(templateUrl, text);
28420      }
28421    }
28422  };
28423}];
28424
28425var noopNgModelController = { $setViewValue: noop, $render: noop };
28426
28427/**
28428 * @ngdoc type
28429 * @name  select.SelectController
28430 * @description
28431 * The controller for the `<select>` directive. This provides support for reading
28432 * and writing the selected value(s) of the control and also coordinates dynamically
28433 * added `<option>` elements, perhaps by an `ngRepeat` directive.
28434 */
28435var SelectController =
28436        ['$element', '$scope', '$attrs', function($element, $scope, $attrs) {
28437
28438  var self = this,
28439      optionsMap = new HashMap();
28440
28441  // If the ngModel doesn't get provided then provide a dummy noop version to prevent errors
28442  self.ngModelCtrl = noopNgModelController;
28443
28444  // The "unknown" option is one that is prepended to the list if the viewValue
28445  // does not match any of the options. When it is rendered the value of the unknown
28446  // option is '? XXX ?' where XXX is the hashKey of the value that is not known.
28447  //
28448  // We can't just jqLite('<option>') since jqLite is not smart enough
28449  // to create it in <select> and IE barfs otherwise.
28450  self.unknownOption = jqLite(document.createElement('option'));
28451  self.renderUnknownOption = function(val) {
28452    var unknownVal = '? ' + hashKey(val) + ' ?';
28453    self.unknownOption.val(unknownVal);
28454    $element.prepend(self.unknownOption);
28455    $element.val(unknownVal);
28456  };
28457
28458  $scope.$on('$destroy', function() {
28459    // disable unknown option so that we don't do work when the whole select is being destroyed
28460    self.renderUnknownOption = noop;
28461  });
28462
28463  self.removeUnknownOption = function() {
28464    if (self.unknownOption.parent()) self.unknownOption.remove();
28465  };
28466
28467
28468  // Read the value of the select control, the implementation of this changes depending
28469  // upon whether the select can have multiple values and whether ngOptions is at work.
28470  self.readValue = function readSingleValue() {
28471    self.removeUnknownOption();
28472    return $element.val();
28473  };
28474
28475
28476  // Write the value to the select control, the implementation of this changes depending
28477  // upon whether the select can have multiple values and whether ngOptions is at work.
28478  self.writeValue = function writeSingleValue(value) {
28479    if (self.hasOption(value)) {
28480      self.removeUnknownOption();
28481      $element.val(value);
28482      if (value === '') self.emptyOption.prop('selected', true); // to make IE9 happy
28483    } else {
28484      if (value == null && self.emptyOption) {
28485        self.removeUnknownOption();
28486        $element.val('');
28487      } else {
28488        self.renderUnknownOption(value);
28489      }
28490    }
28491  };
28492
28493
28494  // Tell the select control that an option, with the given value, has been added
28495  self.addOption = function(value, element) {
28496    assertNotHasOwnProperty(value, '"option value"');
28497    if (value === '') {
28498      self.emptyOption = element;
28499    }
28500    var count = optionsMap.get(value) || 0;
28501    optionsMap.put(value, count + 1);
28502  };
28503
28504  // Tell the select control that an option, with the given value, has been removed
28505  self.removeOption = function(value) {
28506    var count = optionsMap.get(value);
28507    if (count) {
28508      if (count === 1) {
28509        optionsMap.remove(value);
28510        if (value === '') {
28511          self.emptyOption = undefined;
28512        }
28513      } else {
28514        optionsMap.put(value, count - 1);
28515      }
28516    }
28517  };
28518
28519  // Check whether the select control has an option matching the given value
28520  self.hasOption = function(value) {
28521    return !!optionsMap.get(value);
28522  };
28523}];
28524
28525/**
28526 * @ngdoc directive
28527 * @name select
28528 * @restrict E
28529 *
28530 * @description
28531 * HTML `SELECT` element with angular data-binding.
28532 *
28533 * The `select` directive is used together with {@link ngModel `ngModel`} to provide data-binding
28534 * between the scope and the `<select>` control (including setting default values).
28535 * Ìt also handles dynamic `<option>` elements, which can be added using the {@link ngRepeat `ngRepeat}` or
28536 * {@link ngOptions `ngOptions`} directives.
28537 *
28538 * When an item in the `<select>` menu is selected, the value of the selected option will be bound
28539 * to the model identified by the `ngModel` directive. With static or repeated options, this is
28540 * the content of the `value` attribute or the textContent of the `<option>`, if the value attribute is missing.
28541 * If you want dynamic value attributes, you can use interpolation inside the value attribute.
28542 *
28543 * <div class="alert alert-warning">
28544 * Note that the value of a `select` directive used without `ngOptions` is always a string.
28545 * When the model needs to be bound to a non-string value, you must either explictly convert it
28546 * using a directive (see example below) or use `ngOptions` to specify the set of options.
28547 * This is because an option element can only be bound to string values at present.
28548 * </div>
28549 *
28550 * If the viewValue of `ngModel` does not match any of the options, then the control
28551 * will automatically add an "unknown" option, which it then removes when the mismatch is resolved.
28552 *
28553 * Optionally, a single hard-coded `<option>` element, with the value set to an empty string, can
28554 * be nested into the `<select>` element. This element will then represent the `null` or "not selected"
28555 * option. See example below for demonstration.
28556 *
28557 * <div class="alert alert-info">
28558 * In many cases, `ngRepeat` can be used on `<option>` elements instead of {@link ng.directive:ngOptions
28559 * ngOptions} to achieve a similar result. However, `ngOptions` provides some benef
28559its, such as
28560 * more flexibility in how the `<select>`'s model is assigned via the `select` **`as`** part of the
28561 * comprehension expression, and additionally in reducing memory and increasing speed by not creating
28562 * a new scope for each repeated instance.
28563 * </div>
28564 *
28565 *
28566 * @param {string} ngModel Assignable angular expression to data-bind to.
28567 * @param {string=} name Property name of the form under which the control is published.
28568 * @param {string=} required Sets `required` validation error key if the value is not entered.
28569 * @param {string=} ngRequired Adds required attribute and required validation constraint to
28570 * the element when the ngRequired expression evaluates to true. Use ngRequired instead of required
28571 * when you want to data-bind to the required attribute.
28572 * @param {string=} ngChange Angular expression to be executed when selected option(s) changes due to user
28573 *    interaction with the select element.
28574 * @param {string=} ngOptions sets the options that the select is populated with and defines what is
28575 * set on the model on selection. See {@link ngOptions `ngOptions`}.
28576 *
28577 * @example
28578 * ### Simple `select` elements with static options
28579 *
28580 * <example name="static-select" module="staticSelect">
28581 * <file name="index.html">
28582 * <div ng-controller="ExampleController">
28583 *   <form name="myForm">
28584 *     <label for="singleSelect"> Single select: </label><br>
28585 *     <select name="singleSelect" ng-model="data.singleSelect">
28586 *       <option value="option-1">Option 1</option>
28587 *       <option value="option-2">Option 2</option>
28588 *     </select><br>
28589 *
28590 *     <label for="singleSelect"> Single select with "not selected" option and dynamic option values: </label><br>
28591 *     <select name="singleSelect" id="singleSelect" ng-model="data.singleSelect">
28592 *       <option value="">---Please select---</option> <!-- not selected / blank option -->
28593 *       <option value="{{data.option1}}">Option 1</option> <!-- interpolation -->
28594 *       <option value="option-2">Option 2</option>
28595 *     </select><br>
28596 *     <button ng-click="forceUnknownOption()">Force unknown option</button><br>
28597 *     <tt>singleSelect = {{data.singleSelect}}</tt>
28598 *
28599 *     <hr>
28600 *     <label for="multipleSelect"> Multiple select: </label><br>
28601 *     <select name="multipleSelect" id="multipleSelect" ng-model="data.multipleSelect" multiple>
28602 *       <option value="option-1">Option 1</option>
28603 *       <option value="option-2">Option 2</option>
28604 *       <option value="option-3">Option 3</option>
28605 *     </select><br>
28606 *     <tt>multipleSelect = {{data.multipleSelect}}</tt><br/>
28607 *   </form>
28608 * </div>
28609 * </file>
28610 * <file name="app.js">
28611 *  angular.module('staticSelect', [])
28612 *    .controller('ExampleController', ['$scope', function($scope) {
28613 *      $scope.data = {
28614 *       singleSelect: null,
28615 *       multipleSelect: [],
28616 *       option1: 'option-1',
28617 *      };
28618 *
28619 *      $scope.forceUnknownOption = function() {
28620 *        $scope.data.singleSelect = 'nonsense';
28621 *      };
28622 *   }]);
28623 * </file>
28624 *</example>
28625 *
28626 * ### Using `ngRepeat` to generate `select` options
28627 * <example name="ngrepeat-select" module="ngrepeatSelect">
28628 * <file name="index.html">
28629 * <div ng-controller="ExampleController">
28630 *   <form name="myForm">
28631 *     <label for="repeatSelect"> Repeat select: </label>
28632 *     <select name="repeatSelect" id="repeatSelect" ng-model="data.repeatSelect">
28633 *       <option ng-repeat="option in data.availableOptions" value="{{option.id}}">{{option.name}}</option>
28634 *     </select>
28635 *   </form>
28636 *   <hr>
28637 *   <tt>repeatSelect = {{data.repeatSelect}}</tt><br/>
28638 * </div>
28639 * </file>
28640 * <file name="app.js">
28641 *  angular.module('ngrepeatSelect', [])
28642 *    .controller('ExampleController', ['$scope', function($scope) {
28643 *      $scope.data = {
28644 *       repeatSelect: null,
28645 *       availableOptions: [
28646 *         {id: '1', name: 'Option A'},
28647 *         {id: '2', name: 'Option B'},
28648 *         {id: '3', name: 'Option C'}
28649 *       ],
28650 *      };
28651 *   }]);
28652 * </file>
28653 *</example>
28654 *
28655 *
28656 * ### Using `select` with `ngOptions` and setting a default value
28657 * See the {@link ngOptions ngOptions documentation} for more `ngOptions` usage examples.
28658 *
28659 * <example name="select-with-default-values" module="defaultValueSelect">
28660 * <file name="index.html">
28661 * <div ng-controller="ExampleController">
28662 *   <form name="myForm">
28663 *     <label for="mySelect">Make a choice:</label>
28664 *     <select name="mySelect" id="mySelect"
28665 *       ng-options="option.name for option in data.availableOptions track by option.id"
28666 *       ng-model="data.selectedOption"></select>
28667 *   </form>
28668 *   <hr>
28669 *   <tt>option = {{data.selectedOption}}</tt><br/>
28670 * </div>
28671 * </file>
28672 * <file name="app.js">
28673 *  angular.module('defaultValueSelect', [])
28674 *    .controller('ExampleController', ['$scope', function($scope) {
28675 *      $scope.data = {
28676 *       availableOptions: [
28677 *         {id: '1', name: 'Option A'},
28678 *         {id: '2', name: 'Option B'},
28679 *         {id: '3', name: 'Option C'}
28680 *       ],
28681 *       selectedOption: {id: '3', name: 'Option C'} //This sets the default value of the select in the ui
28682 *       };
28683 *   }]);
28684 * </file>
28685 *</example>
28686 *
28687 *
28688 * ### Binding `select` to a non-string value via `ngModel` parsing / formatting
28689 *
28690 * <example name="select-with-non-string-options" module="nonStringSelect">
28691 *   <file name="index.html">
28692 *     <select ng-model="model.id" convert-to-number>
28693 *       <option value="0">Zero</option>
28694 *       <option value="1">One</option>
28695 *       <option value="2">Two</option>
28696 *     </select>
28697 *     {{ model }}
28698 *   </file>
28699 *   <file name="app.js">
28700 *     angular.module('nonStringSelect', [])
28701 *       .run(function($rootScope) {
28702 *         $rootScope.model = { id: 2 };
28703 *       })
28704 *       .directive('convertToNumber', function() {
28705 *         return {
28706 *           require: 'ngModel',
28707 *           link: function(scope, element, attrs, ngModel) {
28708 *             ngModel.$parsers.push(function(val) {
28709 *               return parseInt(val, 10);
28710 *             });
28711 *             ngModel.$formatters.push(function(val) {
28712 *               return '' + val;
28713 *             });
28714 *           }
28715 *         };
28716 *       });
28717 *   </file>
28718 *   <file name="protractor.js" type="protractor">
28719 *     it('should initialize to model', function() {
28720 *       var select = element(by.css('select'));
28721 *       expect(element(by.model('model.id')).$('option:checked').getText()).toEqual('Two');
28722 *     });
28723 *   </file>
28724 * </example>
28725 *
28726 */
28727var selectDirective = function() {
28728
28729  return {
28730    restrict: 'E',
28731    require: ['select', '?ngModel'],
28732    controller: SelectController,
28733    link: function(scope, element, attr, ctrls) {
28734
28735      // if ngModel is not defined, we don't need to do anything
28736      var ngModelCtrl = ctrls[1];
28737      if (!ngModelCtrl) return;
28738
28739      var selectCtrl = ctrls[0];
28740
28741      selectCtrl.ngModelCtrl = ngModelCtrl;
28742
28743      // We delegate rendering to the `writeValue` method, which can be changed
28744      // if the select can have multiple selected values or if the options are being
28745      // generated by `ngOptions`
28746      ngModelCtrl.$render = function() {
28747        selectCtrl.writeValue(ngModelCtrl.$viewValue);
28748      };
28749
28750      // When the selected item(s) changes we delegate getting the value of the select control
28751      // to the `readValue` method, which can be changed if the select can have multiple
28752      // selected values or if the options are being generated by `ngOptions`
28753      element.on('change', function() {
28754        scope.$apply(function() {
28755          ngModelCtrl.$setViewValue(selectCtrl.readValue());
28756        });
28757      });
28758
28759      // If the select allows multiple values then we need to modify how we read and write
28760      // values from and to the control; also what it means for the value to be empty and
28761      // we have to add an extra watch since ngModel doesn't work well with arrays - it
28762      // doesn't trigger rendering if only an item in the array changes.
28763      if (attr.multiple) {
28764
28765        // Read value now needs to check each option to see if it is selected
28766        selectCtrl.readValue = function readMultipleValue() {
28767          var array = [];
28768          forEach(element.find('option'), function(option) {
28769            if (option.selected) {
28770              array.push(option.value);
28771            }
28772          });
28773          return array;
28774        };
28775
28776        // Write value now needs to set the selected property of each matching option
28777        selectCtrl.writeValue = function writeMultipleValue(value) {
28778          var items = new HashMap(value);
28779          forEach(element.find('option'), function(option) {
28780            option.selected = isDefined(items.get(option.value));
28781          });
28782        };
28783
28784        // we have to do it on each watch since ngModel watches reference, but
28785        // we need to work of an array, so we need to see if anything was inserted/removed
28786        var lastView, lastViewRef = NaN;
28787        scope.$watch(function selectMultipleWatch() {
28788          if (lastViewRef === ngModelCtrl.$viewValue && !equals(lastView, ngModelCtrl.$viewValue)) {
28789            lastView = shallowCopy(ngModelCtrl.$viewValue);
28790            ngModelCtrl.$render();
28791          }
28792          lastViewRef = ngModelCtrl.$viewValue;
28793        });
28794
28795        // If we are a multiple select then value is now a collection
28796        // so the meaning of $isEmpty changes
28797        ngModelCtrl.$isEmpty = function(value) {
28798          return !value || value.length === 0;
28799        };
28800
28801      }
28802    }
28803  };
28804};
28805
28806
28807// The option directive is purely designed to communicate the existence (or lack of)
28808// of dynamically created (and destroyed) option elements to their containing select
28809// directive via its controller.
28810var optionDirective = ['$interpolate', function($interpolate) {
28811
28812  function chromeHack(optionElement) {
28813    // Workaround for https://code.google.com/p/chromium/issues/detail?id=381459
28814    // Adding an <option selected="selected"> element to a <select required="required"> should
28815    // automatically select the new element
28816    if (optionElement[0].hasAttribute('selected')) {
28817      optionElement[0].selected = true;
28818    }
28819  }
28820
28821  return {
28822    restrict: 'E',
28823    priority: 100,
28824    compile: function(element, attr) {
28825
28826      if (isDefined(attr.value)) {
28827        // If the value attribute is defined, check if it contains an interpolation
28828        var valueInterpolated = $interpolate(attr.value, true);
28829      } else {
28830        // If the value attribute is not defined then we fall back to the
28831        // text content of the option element, which may be interpolated
28832        var interpolateFn = $interpolate(element.text(), true);
28833        if (!interpolateFn) {
28834          attr.$set('value', element.text());
28835        }
28836      }
28837
28838      return function(scope, element, attr) {
28839
28840        // This is an optimization over using ^^ since we don't want to have to search
28841        // all the way to the root of the DOM for every single option element
28842        var selectCtrlName = '$selectController',
28843            parent = element.parent(),
28844            selectCtrl = parent.data(selectCtrlName) ||
28845              parent.parent().data(selectCtrlName); // in case we are in optgroup
28846
28847        function addOption(optionValue) {
28848          selectCtrl.addOption(optionValue, element);
28849          selectCtrl.ngModelCtrl.$render();
28850          chromeHack(element);
28851        }
28852
28853        // Only update trigger option updates if this is an option within a `select`
28854        // that also has `ngModel` attached
28855        if (selectCtrl && selectCtrl.ngModelCtrl) {
28856
28857          if (valueInterpolated) {
28858            // The value attribute is interpolated
28859            var oldVal;
28860            attr.$observe('value', function valueAttributeObserveAction(newVal) {
28861              if (isDefined(oldVal)) {
28862                selectCtrl.removeOption(oldVal);
28863              }
28864              oldVal = newVal;
28865              addOption(newVal);
28866            });
28867          } else if (interpolateFn) {
28868            // The text content is interpolated
28869            scope.$watch(interpolateFn, function interpolateWatchAction(newVal, oldVal) {
28870              attr.$set('value', newVal);
28871              if (oldVal !== newVal) {
28872                selectCtrl.removeOption(oldVal);
28873              }
28874              addOption(newVal);
28875            });
28876          } else {
28877            // The value attribute is static
28878            addOption(attr.value);
28879          }
28880
28881          element.on('$destroy', function() {
28882            selectCtrl.removeOption(attr.value);
28883            selectCtrl.ngModelCtrl.$render();
28884          });
28885        }
28886      };
28887    }
28888  };
28889}];
28890
28891var styleDirective = valueFn({
28892  restrict: 'E',
28893  terminal: false
28894});
28895
28896var requiredDirective = function() {
28897  return {
28898    restrict: 'A',
28899    require: '?ngModel',
28900    link: function(scope, elm, attr, ctrl) {
28901      if (!ctrl) return;
28902      attr.required = true; // force truthy in case we are on non input element
28903
28904      ctrl.$validators.required = function(modelValue, viewValue) {
28905        return !attr.required || !ctrl.$isEmpty(viewValue);
28906      };
28907
28908      attr.$observe('required', function() {
28909        ctrl.$validate();
28910      });
28911    }
28912  };
28913};
28914
28915
28916var patternDirective = function() {
28917  return {
28918    restrict: 'A',
28919    require: '?ngModel',
28920    link: function(scope, elm, attr, ctrl) {
28921      if (!ctrl) return;
28922
28923      var regexp, patternExp = attr.ngPattern || attr.pattern;
28924      attr.$observe('pattern', function(regex) {
28925        if (isString(regex) && regex.length > 0) {
28926          regex = new RegExp('^' + regex + '$');
28927        }
28928
28929        if (regex && !regex.test) {
28930          throw minErr('ngPattern')('noregexp',
28931            'Expected {0} to be a RegExp but was {1}. Element: {2}', patternExp,
28932            regex, startingTag(elm));
28933        }
28934
28935        regexp = regex || undefined;
28936        ctrl.$validate();
28937      });
28938
28939      ctrl.$validators.pattern = function(modelValue, viewValue) {
28940        // HTML5 pattern constraint validates the input value, so we validate the viewValue
28941        return ctrl.$isEmpty(viewValue) || isUndefined(regexp) || regexp.test(viewValue);
28942      };
28943    }
28944  };
28945};
28946
28947
28948var maxlengthDirective = function() {
28949  return {
28950    restrict: 'A',
28951    require: '?ngModel',
28952    link: function(scope, elm, attr, ctrl) {
28953      if (!ctrl) return;
28954
28955      var maxlength = -1;
28956      attr.$observe('maxlength', function(value) {
28957        var intVal = toInt(value);
28958        maxlength = isNaN(intVal) ? -1 : intVal;
28959        ctrl.$validate();
28960      });
28961      ctrl.$validators.maxlength = function(modelValue, viewValue) {
28962        return (maxlength < 0) || ctrl.$isEmpty(viewValue) || (viewValue.length <= maxlength);
28963      };
28964    }
28965  };
28966};
28967
28968var minlengthDirective = function() {
28969  return {
28970    restrict: 'A',
28971    require: '?ngModel',
28972    link: function(scope, elm, attr, ctrl) {
28973      if (!ctrl) return;
28974
28975      var minlength = 0;
28976      attr.$observe('minlength', function(value) {
28977        minlength = toInt(value) || 0;
28978        ctrl.$validate();
28979      });
28980      ctrl.$validators.minlength = function(modelValue, viewValue) {
28981        return ctrl.$isEmpty(viewValue) || viewValue.length >= minlength;
28982      };
28983    }
28984  };
28985};
28986
28987if (window.angular.bootstrap) {
28988  //AngularJS is already loaded, so we can return here...
28989  console.log('WARNING: Tried to load angular more than once.');
28990  return;
28991}
28992
28993//try to bind to jquery now so that one can write jqLite(document).ready()
28994//but we will rebind on bootstrap again.
28995bindJQuery();
28996
28997publishExternalAPI(angular);
28998
28999angular.module("ngLocale", [], ["$provide", function($provide) {
29000var PLURAL_CATEGORY = {ZERO: "zero", ONE: "one", TWO: "two", FEW: "few", MANY: "many", OTHER: "other"};
29001function getDecimals(n) {
29002  n = n + '';
29003  var i = n.indexOf('.');
29004  return (i == -1) ? 0 : n.length - i - 1;
29005}
29006
29007function getVF(n, opt_precision) {
29008  var v = opt_precision;
29009
29010  if (undefined === v) {
29011    v = Math.min(getDecimals(n), 3);
29012  }
29013
29014  var base = Math.pow(10, v);
29015  var f = ((n * base) | 0) % base;
29016  return {v: v, f: f};
29017}
29018
29019$provide.value("$locale", {
29020  "DATETIME_FORMATS": {
29021    "AMPMS": [
29022      "AM",
29023      "PM"
29024    ],
29025    "DAY": [
29026      "Sunday",
29027      "Monday",
29028      "Tuesday",
29029      "Wednesday",
29030      "Thursday",
29031      "Friday",
29032      "Saturday"
29033    ],
29034    "ERANAMES": [
29035      "Before Christ",
29036      "Anno Domini"
29037    ],
29038    "ERAS": [
29039      "BC",
29040      "AD"
29041    ],
29042    "FIRSTDAYOFWEEK": 6,
29043    "MONTH": [
29044      "January",
29045      "February",
29046      "March",
29047      "April",
29048      "May",
29049      "June",
29050      "July",
29051      "August",
29052      "September",
29053      "October",
29054      "November",
29055      "December"
29056    ],
29057    "SHORTDAY": [
29058      "Sun",
29059      "Mon",
29060      "Tue",
29061      "Wed",
29062      "Thu",
29063      "Fri",
29064      "Sat"
29065    ],
29066    "SHORTMONTH": [
29067      "Jan",
29068      "Feb",
29069      "Mar",
29070      "Apr",
29071      "May",
29072      "Jun",
29073      "Jul",
29074      "Aug",
29075      "Sep",
29076      "Oct",
29077      "Nov",
29078      "Dec"
29079    ],
29080    "WEEKENDRANGE": [
29081      5,
29082      6
29083    ],
29084    "fullDate": "EEEE, MMMM d, y",
29085    "longDate": "MMMM d, y",
29086    "medium": "MMM d, y h:mm:ss a",
29087    "mediumDate": "MMM d, y",
29088    "mediumTime": "h:mm:ss a",
29089    "short": "M/d/yy h:mm a",
29090    "shortDate": "M/d/yy",
29091    "shortTime": "h:mm a"
29092  },
29093  "NUMBER_FORMATS": {
29094    "CURRENCY_SYM": "$",
29095    "DECIMAL_SEP": ".",
29096    "GROUP_SEP": ",",
29097    "PATTERNS": [
29098      {
29099        "gSize": 3,
29100        "lgSize": 3,
29101        "maxFrac": 3,
29102        "minFrac": 0,
29103        "minInt": 1,
29104        "negPre": "-",
29105        "negSuf": "",
29106        "posPre": "",
29107        "posSuf": ""
29108      },
29109      {
29110        "gSize": 3,
29111        "lgSize": 3,
29112        "maxFrac": 2,
29113        "minFrac": 2,
29114        "minInt": 1,
29115        "negPre": "-\u00a4",
29116        "negSuf": "",
29117        "posPre": "\u00a4",
29118        "posSuf": ""
29119      }
29120    ]
29121  },
29122  "id": "en-us",
29123  "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;}
29124});
29125}]);
29126
29127  jqLite(document).ready(function() {
29128    angularInit(document, bootstrap);
29129  });
29130
29131})(window, document);
29132
29133!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>');
29134;angular.element(document).find('head').prepend('<!--[if IE 8]><style>.ng-hide {display: none !important;}</style><![endif]-->');

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.