2 * @license AngularJS v1.6.3
3 * (c) 2010-2017 Google, Inc. http://angularjs.org
6 (function(window) {'use strict';
11 * This object provides a utility for producing rich Error messages within
12 * Angular. It can be called as follows:
14 * var exampleMinErr = minErr('example');
15 * throw exampleMinErr('one', 'This {0} is {1}', foo, bar);
17 * The above creates an instance of minErr in the example namespace. The
18 * resulting error will have a namespaced error code of example.one. The
19 * resulting error will replace {0} with the value of foo, and {1} with the
20 * value of bar. The object is not restricted in the number of arguments it can
23 * If fewer arguments are specified than necessary for interpolation, the extra
24 * interpolation markers will be preserved in the final string.
26 * Since data will be parsed statically during a build step, some restrictions
27 * are applied with respect to how minErr instances are created and called.
28 * Instances should have names of the form namespaceMinErr for a minErr created
29 * using minErr('namespace') . Error codes, namespaces and template strings
30 * should all be static strings, not variables or general expressions.
32 * @param {string} module The namespace to use for the new minErr instance.
33 * @param {function} ErrorConstructor Custom error constructor to be instantiated when returning
34 * error from returned function, for cases when a particular type of error is useful.
35 * @returns {function(code:string, template:string, ...templateArgs): Error} minErr instance
38 function minErr(module, ErrorConstructor) {
39 ErrorConstructor = ErrorConstructor || Error;
41 var code = arguments[0],
42 template = arguments[1],
43 message = '[' + (module ? module + ':' : '') + code + '] ',
44 templateArgs = sliceArgs(arguments, 2).map(function(arg) {
45 return toDebugString(arg, minErrConfig.objectMaxDepth);
49 message += template.replace(/\{\d+\}/g, function(match) {
50 var index = +match.slice(1, -1);
52 if (index < templateArgs.length) {
53 return templateArgs[index];
59 message += '\nhttp://errors.angularjs.org/1.6.3/' +
60 (module ? module + '/' : '') + code;
62 for (i = 0, paramPrefix = '?'; i < templateArgs.length; i++, paramPrefix = '&') {
63 message += paramPrefix + 'p' + i + '=' + encodeURIComponent(templateArgs[i]);
66 return new ErrorConstructor(message);
70 /* We need to tell ESLint what variables are being exported */
82 isValidObjectMaxDepth,
87 VALIDITY_STATE_PROPERTY,
141 convertTimezoneToLocal,
144 tryDecodeURIComponent,
156 assertNotHasOwnProperty,
168 NODE_TYPE_DOCUMENT_FRAGMENT
171 ////////////////////////////////////
181 * The ng module is loaded by default when an AngularJS application is started. The module itself
182 * contains the essential components for an AngularJS application to function. The table below
183 * lists a high level breakdown of each of the services/factories, filters, directives and testing
184 * components available within this core module.
186 * <div doc-module-components="ng"></div>
189 var REGEX_STRING_REGEXP = /^\/(.+)\/([a-z]*)$/;
191 // The name of a form control's ValidityState property.
192 // This is used so that it's possible for internal tests to create mock ValidityStates.
193 var VALIDITY_STATE_PROPERTY = 'validity';
196 var hasOwnProperty = Object.prototype.hasOwnProperty;
204 * @name angular.errorHandlingConfig
209 * Configure several aspects of error handling in AngularJS if used as a setter or return the
210 * current configuration if used as a getter. The following options are supported:
212 * - **objectMaxDepth**: The maximum depth to which objects are traversed when stringified for error messages.
214 * Omitted or undefined options will leave the corresponding configuration values unchanged.
216 * @param {Object=} config - The configuration object. May only contain the options that need to be
217 * updated. Supported keys:
219 * * `objectMaxDepth` **{Number}** - The max depth for stringifying objects. Setting to a
220 * non-positive or non-numeric value, removes the max depth limit.
223 function errorHandlingConfig(config) {
224 if (isObject(config)) {
225 if (isDefined(config.objectMaxDepth)) {
226 minErrConfig.objectMaxDepth = isValidObjectMaxDepth(config.objectMaxDepth) ? config.objectMaxDepth : NaN;
235 * @param {Number} maxDepth
238 function isValidObjectMaxDepth(maxDepth) {
239 return isNumber(maxDepth) && maxDepth > 0;
244 * @name angular.lowercase
249 * sinceVersion="1.5.0"
250 * removeVersion="1.7.0"
251 * Use [String.prototype.toLowerCase](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String/toLowerCase) instead.
253 * @description Converts the specified string to lowercase.
254 * @param {string} string String to be converted to lowercase.
255 * @returns {string} Lowercased string.
257 var lowercase = function(string) {return isString(string) ? string.toLowerCase() : string;};
261 * @name angular.uppercase
266 * sinceVersion="1.5.0"
267 * removeVersion="1.7.0"
268 * Use [String.prototype.toUpperCase](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String/toUpperCase) instead.
270 * @description Converts the specified string to uppercase.
271 * @param {string} string String to be converted to uppercase.
272 * @returns {string} Uppercased string.
274 var uppercase = function(string) {return isString(string) ? string.toUpperCase() : string;};
277 var manualLowercase = function(s) {
278 /* eslint-disable no-bitwise */
280 ? s.replace(/[A-Z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) | 32);})
284 var manualUppercase = function(s) {
285 /* eslint-disable no-bitwise */
287 ? s.replace(/[a-z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) & ~32);})
293 // String#toLowerCase and String#toUpperCase don't produce correct results in browsers with Turkish
294 // locale, for this reason we need to detect this case and redefine lowercase/uppercase methods
295 // with correct but slower alternatives. See https://github.com/angular/angular.js/issues/11387
296 if ('i' !== 'I'.toLowerCase()) {
297 lowercase = manualLowercase;
298 uppercase = manualUppercase;
303 msie, // holds major version number for IE, or NaN if UA is not IE.
304 jqLite, // delay binding since jQuery could be loaded after us.
305 jQuery, // delay binding
309 toString = Object.prototype.toString,
310 getPrototypeOf = Object.getPrototypeOf,
311 ngMinErr = minErr('ng'),
314 angular = window.angular || (window.angular = {}),
318 // Support: IE 9-11 only
320 * documentMode is an IE-only property
321 * http://msdn.microsoft.com/en-us/library/ie/cc196988(v=vs.85).aspx
323 msie = window.document.documentMode;
329 * @return {boolean} Returns true if `obj` is an array or array-like object (NodeList, Arguments,
332 function isArrayLike(obj) {
334 // `null`, `undefined` and `window` are not array-like
335 if (obj == null || isWindow(obj)) return false;
337 // arrays, strings and jQuery/jqLite objects are array like
338 // * jqLite is either the jQuery or jqLite constructor function
339 // * we have to check the existence of jqLite first as this method is called
340 // via the forEach method when constructing the jqLite object in the first place
341 if (isArray(obj) || isString(obj) || (jqLite && obj instanceof jqLite)) return true;
343 // Support: iOS 8.2 (not reproducible in simulator)
344 // "length" in obj used to prevent JIT error (gh-11508)
345 var length = 'length' in Object(obj) && obj.length;
347 // NodeList objects (with `item` method) and
348 // other objects with suitable length characteristics are array-like
349 return isNumber(length) &&
350 (length >= 0 && ((length - 1) in obj || obj instanceof Array) || typeof obj.item === 'function');
356 * @name angular.forEach
361 * Invokes the `iterator` function once for each item in `obj` collection, which can be either an
362 * object or an array. The `iterator` function is invoked with `iterator(value, key, obj)`, where `value`
363 * is the value of an object property or an array element, `key` is the object property key or
364 * array element index and obj is the `obj` itself. Specifying a `context` for the function is optional.
366 * It is worth noting that `.forEach` does not iterate over inherited properties because it filters
367 * using the `hasOwnProperty` method.
370 * [Array.prototype.forEach](http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.18),
371 * providing 'undefined' or 'null' values for `obj` will not throw a TypeError, but rather just
372 * return the value provided.
375 var values = {name: 'misko', gender: 'male'};
377 angular.forEach(values, function(value, key) {
378 this.push(key + ': ' + value);
380 expect(log).toEqual(['name: misko', 'gender: male']);
383 * @param {Object|Array} obj Object to iterate over.
384 * @param {Function} iterator Iterator function.
385 * @param {Object=} context Object to become context (`this`) for the iterator function.
386 * @returns {Object|Array} Reference to `obj`.
389 function forEach(obj, iterator, context) {
392 if (isFunction(obj)) {
394 if (key !== 'prototype' && key !== 'length' && key !== 'name' && obj.hasOwnProperty(key)) {
395 iterator.call(context, obj[key], key, obj);
398 } else if (isArray(obj) || isArrayLike(obj)) {
399 var isPrimitive = typeof obj !== 'object';
400 for (key = 0, length = obj.length; key < length; key++) {
401 if (isPrimitive || key in obj) {
402 iterator.call(context, obj[key], key, obj);
405 } else if (obj.forEach && obj.forEach !== forEach) {
406 obj.forEach(iterator, context, obj);
407 } else if (isBlankObject(obj)) {
408 // createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty
410 iterator.call(context, obj[key], key, obj);
412 } else if (typeof obj.hasOwnProperty === 'function') {
413 // Slow path for objects inheriting Object.prototype, hasOwnProperty check needed
415 if (obj.hasOwnProperty(key)) {
416 iterator.call(context, obj[key], key, obj);
420 // Slow path for objects which do not have a method `hasOwnProperty`
422 if (hasOwnProperty.call(obj, key)) {
423 iterator.call(context, obj[key], key, obj);
431 function forEachSorted(obj, iterator, context) {
432 var keys = Object.keys(obj).sort();
433 for (var i = 0; i < keys.length; i++) {
434 iterator.call(context, obj[keys[i]], keys[i]);
441 * when using forEach the params are value, key, but it is often useful to have key, value.
442 * @param {function(string, *)} iteratorFn
443 * @returns {function(*, string)}
445 function reverseParams(iteratorFn) {
446 return function(value, key) {iteratorFn(key, value);};
450 * A consistent way of creating unique IDs in angular.
452 * Using simple numbers allows us to generate 28.6 million unique ids per second for 10 years before
453 * we hit number precision issues in JavaScript.
455 * Math.pow(2,53) / 60 / 60 / 24 / 365 / 10 = 28.6M
457 * @returns {number} an unique alpha-numeric string
465 * Set or clear the hashkey for an object.
467 * @param h the hashkey (!truthy to delete the hashkey)
469 function setHashKey(obj, h) {
473 delete obj.$$hashKey;
478 function baseExtend(dst, objs, deep) {
479 var h = dst.$$hashKey;
481 for (var i = 0, ii = objs.length; i < ii; ++i) {
483 if (!isObject(obj) && !isFunction(obj)) continue;
484 var keys = Object.keys(obj);
485 for (var j = 0, jj = keys.length; j < jj; j++) {
489 if (deep && isObject(src)) {
491 dst[key] = new Date(src.valueOf());
492 } else if (isRegExp(src)) {
493 dst[key] = new RegExp(src);
494 } else if (src.nodeName) {
495 dst[key] = src.cloneNode(true);
496 } else if (isElement(src)) {
497 dst[key] = src.clone();
499 if (!isObject(dst[key])) dst[key] = isArray(src) ? [] : {};
500 baseExtend(dst[key], [src], true);
514 * @name angular.extend
519 * Extends the destination object `dst` by copying own enumerable properties from the `src` object(s)
520 * to `dst`. You can specify multiple `src` objects. If you want to preserve original objects, you can do so
521 * by passing an empty object as the target: `var object = angular.extend({}, object1, object2)`.
523 * **Note:** Keep in mind that `angular.extend` does not support recursive merge (deep copy). Use
524 * {@link angular.merge} for this.
526 * @param {Object} dst Destination object.
527 * @param {...Object} src Source object(s).
528 * @returns {Object} Reference to `dst`.
530 function extend(dst) {
531 return baseExtend(dst, slice.call(arguments, 1), false);
537 * @name angular.merge
542 * Deeply extends the destination object `dst` by copying own enumerable properties from the `src` object(s)
543 * to `dst`. You can specify multiple `src` objects. If you want to preserve original objects, you can do so
544 * by passing an empty object as the target: `var object = angular.merge({}, object1, object2)`.
546 * Unlike {@link angular.extend extend()}, `merge()` recursively descends into object properties of source
547 * objects, performing a deep copy.
549 * @param {Object} dst Destination object.
550 * @param {...Object} src Source object(s).
551 * @returns {Object} Reference to `dst`.
553 function merge(dst) {
554 return baseExtend(dst, slice.call(arguments, 1), true);
559 function toInt(str) {
560 return parseInt(str, 10);
563 var isNumberNaN = Number.isNaN || function isNumberNaN(num) {
564 // eslint-disable-next-line no-self-compare
569 function inherit(parent, extra) {
570 return extend(Object.create(parent), extra);
580 * A function that performs no operations. This function can be useful when writing code in the
583 function foo(callback) {
584 var result = calculateResult();
585 (callback || angular.noop)(result);
595 * @name angular.identity
600 * A function that returns its first argument. This function is useful when writing code in the
604 function transformer(transformationFn, value) {
605 return (transformationFn || angular.identity)(value);
609 function getResult(fn, input) {
610 return (fn || angular.identity)(input);
613 getResult(function(n) { return n * 2; }, 21); // returns 42
614 getResult(null, 21); // returns 21
615 getResult(undefined, 21); // returns 21
618 * @param {*} value to be returned.
619 * @returns {*} the value passed in.
621 function identity($) {return $;}
622 identity.$inject = [];
625 function valueFn(value) {return function valueRef() {return value;};}
627 function hasCustomToString(obj) {
628 return isFunction(obj.toString) && obj.toString !== toString;
634 * @name angular.isUndefined
639 * Determines if a reference is undefined.
641 * @param {*} value Reference to check.
642 * @returns {boolean} True if `value` is undefined.
644 function isUndefined(value) {return typeof value === 'undefined';}
649 * @name angular.isDefined
654 * Determines if a reference is defined.
656 * @param {*} value Reference to check.
657 * @returns {boolean} True if `value` is defined.
659 function isDefined(value) {return typeof value !== 'undefined';}
664 * @name angular.isObject
669 * Determines if a reference is an `Object`. Unlike `typeof` in JavaScript, `null`s are not
670 * considered to be objects. Note that JavaScript arrays are objects.
672 * @param {*} value Reference to check.
673 * @returns {boolean} True if `value` is an `Object` but not `null`.
675 function isObject(value) {
676 // http://jsperf.com/isobject4
677 return value !== null && typeof value === 'object';
682 * Determine if a value is an object with a null prototype
684 * @returns {boolean} True if `value` is an `Object` with a null prototype
686 function isBlankObject(value) {
687 return value !== null && typeof value === 'object' && !getPrototypeOf(value);
693 * @name angular.isString
698 * Determines if a reference is a `String`.
700 * @param {*} value Reference to check.
701 * @returns {boolean} True if `value` is a `String`.
703 function isString(value) {return typeof value === 'string';}
708 * @name angular.isNumber
713 * Determines if a reference is a `Number`.
715 * This includes the "special" numbers `NaN`, `+Infinity` and `-Infinity`.
717 * If you wish to exclude these then you can use the native
718 * [`isFinite'](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/isFinite)
721 * @param {*} value Reference to check.
722 * @returns {boolean} True if `value` is a `Number`.
724 function isNumber(value) {return typeof value === 'number';}
729 * @name angular.isDate
734 * Determines if a value is a date.
736 * @param {*} value Reference to check.
737 * @returns {boolean} True if `value` is a `Date`.
739 function isDate(value) {
740 return toString.call(value) === '[object Date]';
746 * @name angular.isArray
751 * Determines if a reference is an `Array`. Alias of Array.isArray.
753 * @param {*} value Reference to check.
754 * @returns {boolean} True if `value` is an `Array`.
756 var isArray = Array.isArray;
760 * @name angular.isFunction
765 * Determines if a reference is a `Function`.
767 * @param {*} value Reference to check.
768 * @returns {boolean} True if `value` is a `Function`.
770 function isFunction(value) {return typeof value === 'function';}
774 * Determines if a value is a regular expression object.
777 * @param {*} value Reference to check.
778 * @returns {boolean} True if `value` is a `RegExp`.
780 function isRegExp(value) {
781 return toString.call(value) === '[object RegExp]';
786 * Checks if `obj` is a window object.
789 * @param {*} obj Object to check
790 * @returns {boolean} True if `obj` is a window obj.
792 function isWindow(obj) {
793 return obj && obj.window === obj;
797 function isScope(obj) {
798 return obj && obj.$evalAsync && obj.$watch;
802 function isFile(obj) {
803 return toString.call(obj) === '[object File]';
807 function isFormData(obj) {
808 return toString.call(obj) === '[object FormData]';
812 function isBlob(obj) {
813 return toString.call(obj) === '[object Blob]';
817 function isBoolean(value) {
818 return typeof value === 'boolean';
822 function isPromiseLike(obj) {
823 return obj && isFunction(obj.then);
827 var TYPED_ARRAY_REGEXP = /^\[object (?:Uint8|Uint8Clamped|Uint16|Uint32|Int8|Int16|Int32|Float32|Float64)Array]$/;
828 function isTypedArray(value) {
829 return value && isNumber(value.length) && TYPED_ARRAY_REGEXP.test(toString.call(value));
832 function isArrayBuffer(obj) {
833 return toString.call(obj) === '[object ArrayBuffer]';
837 var trim = function(value) {
838 return isString(value) ? value.trim() : value;
842 // http://docs.closure-library.googlecode.com/git/local_closure_goog_string_string.js.source.html#line1021
843 // Prereq: s is a string.
844 var escapeForRegexp = function(s) {
846 .replace(/([-()[\]{}+?*.$^|,:#<!\\])/g, '\\$1')
847 // eslint-disable-next-line no-control-regex
848 .replace(/\x08/g, '\\x08');
854 * @name angular.isElement
859 * Determines if a reference is a DOM element (or wrapped jQuery element).
861 * @param {*} value Reference to check.
862 * @returns {boolean} True if `value` is a DOM element (or wrapped jQuery element).
864 function isElement(node) {
866 (node.nodeName // We are a direct element.
867 || (node.prop && node.attr && node.find))); // We have an on and find method part of jQuery API.
871 * @param str 'key1,key2,...'
872 * @returns {object} in the form of {key1:true, key2:true, ...}
874 function makeMap(str) {
875 var obj = {}, items = str.split(','), i;
876 for (i = 0; i < items.length; i++) {
877 obj[items[i]] = true;
883 function nodeName_(element) {
884 return lowercase(element.nodeName || (element[0] && element[0].nodeName));
887 function includes(array, obj) {
888 return Array.prototype.indexOf.call(array, obj) !== -1;
891 function arrayRemove(array, value) {
892 var index = array.indexOf(value);
894 array.splice(index, 1);
906 * Creates a deep copy of `source`, which should be an object or an array.
908 * * If no destination is supplied, a copy of the object or array is created.
909 * * If a destination is provided, all of its elements (for arrays) or properties (for objects)
910 * are deleted and then all elements/properties from the source are copied to it.
911 * * If `source` is not an object or array (inc. `null` and `undefined`), `source` is returned.
912 * * If `source` is identical to `destination` an exception will be thrown.
915 * <div class="alert alert-warning">
916 * Only enumerable properties are taken into account. Non-enumerable properties (both on `source`
917 * and on `destination`) will be ignored.
920 * @param {*} source The source that will be used to make a copy.
921 * Can be any type, including primitives, `null`, and `undefined`.
922 * @param {(Object|Array)=} destination Destination into which the source is copied. If
923 * provided, must be of the same type as `source`.
924 * @returns {*} The copy or updated `destination`, if `destination` was specified.
927 <example module="copyExample" name="angular-copy">
928 <file name="index.html">
929 <div ng-controller="ExampleController">
930 <form novalidate class="simple-form">
931 <label>Name: <input type="text" ng-model="user.name" /></label><br />
932 <label>Age: <input type="number" ng-model="user.age" /></label><br />
933 Gender: <label><input type="radio" ng-model="user.gender" value="male" />male</label>
934 <label><input type="radio" ng-model="user.gender" value="female" />female</label><br />
935 <button ng-click="reset()">RESET</button>
936 <button ng-click="update(user)">SAVE</button>
938 <pre>form = {{user | json}}</pre>
939 <pre>master = {{master | json}}</pre>
942 <file name="script.js">
943 // Module: copyExample
945 module('copyExample', []).
946 controller('ExampleController', ['$scope', function($scope) {
949 $scope.reset = function() {
950 // Example with 1 argument
951 $scope.user = angular.copy($scope.master);
954 $scope.update = function(user) {
955 // Example with 2 arguments
956 angular.copy(user, $scope.master);
964 function copy(source, destination, maxDepth) {
965 var stackSource = [];
967 maxDepth = isValidObjectMaxDepth(maxDepth) ? maxDepth : NaN;
970 if (isTypedArray(destination) || isArrayBuffer(destination)) {
971 throw ngMinErr('cpta', 'Can\'t copy! TypedArray destination cannot be mutated.');
973 if (source === destination) {
974 throw ngMinErr('cpi', 'Can\'t copy! Source and destination are identical.');
977 // Empty the destination object
978 if (isArray(destination)) {
979 destination.length = 0;
981 forEach(destination, function(value, key) {
982 if (key !== '$$hashKey') {
983 delete destination[key];
988 stackSource.push(source);
989 stackDest.push(destination);
990 return copyRecurse(source, destination, maxDepth);
993 return copyElement(source, maxDepth);
995 function copyRecurse(source, destination, maxDepth) {
1000 var h = destination.$$hashKey;
1002 if (isArray(source)) {
1003 for (var i = 0, ii = source.length; i < ii; i++) {
1004 destination.push(copyElement(source[i], maxDepth));
1006 } else if (isBlankObject(source)) {
1007 // createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty
1008 for (key in source) {
1009 destination[key] = copyElement(source[key], maxDepth);
1011 } else if (source && typeof source.hasOwnProperty === 'function') {
1012 // Slow path, which must rely on hasOwnProperty
1013 for (key in source) {
1014 if (source.hasOwnProperty(key)) {
1015 destination[key] = copyElement(source[key], maxDepth);
1019 // Slowest path --- hasOwnProperty can't be called as a method
1020 for (key in source) {
1021 if (hasOwnProperty.call(source, key)) {
1022 destination[key] = copyElement(source[key], maxDepth);
1026 setHashKey(destination, h);
1030 function copyElement(source, maxDepth) {
1032 if (!isObject(source)) {
1036 // Already copied values
1037 var index = stackSource.indexOf(source);
1039 return stackDest[index];
1042 if (isWindow(source) || isScope(source)) {
1043 throw ngMinErr('cpws',
1044 'Can\'t copy! Making copies of Window or Scope instances is not supported.');
1047 var needsRecurse = false;
1048 var destination = copyType(source);
1050 if (destination === undefined) {
1051 destination = isArray(source) ? [] : Object.create(getPrototypeOf(source));
1052 needsRecurse = true;
1055 stackSource.push(source);
1056 stackDest.push(destination);
1059 ? copyRecurse(source, destination, maxDepth)
1063 function copyType(source) {
1064 switch (toString.call(source)) {
1065 case '[object Int8Array]':
1066 case '[object Int16Array]':
1067 case '[object Int32Array]':
1068 case '[object Float32Array]':
1069 case '[object Float64Array]':
1070 case '[object Uint8Array]':
1071 case '[object Uint8ClampedArray]':
1072 case '[object Uint16Array]':
1073 case '[object Uint32Array]':
1074 return new source.constructor(copyElement(source.buffer), source.byteOffset, source.length);
1076 case '[object ArrayBuffer]':
1078 if (!source.slice) {
1079 // If we're in this case we know the environment supports ArrayBuffer
1080 /* eslint-disable no-undef */
1081 var copied = new ArrayBuffer(source.byteLength);
1082 new Uint8Array(copied).set(new Uint8Array(source));
1086 return source.slice(0);
1088 case '[object Boolean]':
1089 case '[object Number]':
1090 case '[object String]':
1091 case '[object Date]':
1092 return new source.constructor(source.valueOf());
1094 case '[object RegExp]':
1095 var re = new RegExp(source.source, source.toString().match(/[^/]*$/)[0]);
1096 re.lastIndex = source.lastIndex;
1099 case '[object Blob]':
1100 return new source.constructor([source], {type: source.type});
1103 if (isFunction(source.cloneNode)) {
1104 return source.cloneNode(true);
1112 * @name angular.equals
1117 * Determines if two objects or two values are equivalent. Supports value types, regular
1118 * expressions, arrays and objects.
1120 * Two objects or values are considered equivalent if at least one of the following is true:
1122 * * Both objects or values pass `===` comparison.
1123 * * Both objects or values are of the same type and all of their properties are equal by
1124 * comparing them with `angular.equals`.
1125 * * Both values are NaN. (In JavaScript, NaN == NaN => false. But we consider two NaN as equal)
1126 * * Both values represent the same regular expression (In JavaScript,
1127 * /abc/ == /abc/ => false. But we consider two regular expressions as equal when their textual
1128 * representation matches).
1130 * During a property comparison, properties of `function` type and properties with names
1131 * that begin with `$` are ignored.
1133 * Scope and DOMWindow objects are being compared only by identify (`===`).
1135 * @param {*} o1 Object or value to compare.
1136 * @param {*} o2 Object or value to compare.
1137 * @returns {boolean} True if arguments are equal.
1140 <example module="equalsExample" name="equalsExample">
1141 <file name="index.html">
1142 <div ng-controller="ExampleController">
1145 Name: <input type="text" ng-model="user1.name">
1146 Age: <input type="number" ng-model="user1.age">
1149 Name: <input type="text" ng-model="user2.name">
1150 Age: <input type="number" ng-model="user2.age">
1154 <input type="button" value="Compare" ng-click="compare()">
1156 User 1: <pre>{{user1 | json}}</pre>
1157 User 2: <pre>{{user2 | json}}</pre>
1158 Equal: <pre>{{result}}</pre>
1162 <file name="script.js">
1163 angular.module('equalsExample', []).controller('ExampleController', ['$scope', function($scope) {
1166 $scope.compare = function() {
1167 $scope.result = angular.equals($scope.user1, $scope.user2);
1173 function equals(o1, o2) {
1174 if (o1 === o2) return true;
1175 if (o1 === null || o2 === null) return false;
1176 // eslint-disable-next-line no-self-compare
1177 if (o1 !== o1 && o2 !== o2) return true; // NaN === NaN
1178 var t1 = typeof o1, t2 = typeof o2, length, key, keySet;
1179 if (t1 === t2 && t1 === 'object') {
1181 if (!isArray(o2)) return false;
1182 if ((length = o1.length) === o2.length) {
1183 for (key = 0; key < length; key++) {
1184 if (!equals(o1[key], o2[key])) return false;
1188 } else if (isDate(o1)) {
1189 if (!isDate(o2)) return false;
1190 return equals(o1.getTime(), o2.getTime());
1191 } else if (isRegExp(o1)) {
1192 if (!isRegExp(o2)) return false;
1193 return o1.toString() === o2.toString();
1195 if (isScope(o1) || isScope(o2) || isWindow(o1) || isWindow(o2) ||
1196 isArray(o2) || isDate(o2) || isRegExp(o2)) return false;
1197 keySet = createMap();
1199 if (key.charAt(0) === '$' || isFunction(o1[key])) continue;
1200 if (!equals(o1[key], o2[key])) return false;
1204 if (!(key in keySet) &&
1205 key.charAt(0) !== '$' &&
1206 isDefined(o2[key]) &&
1207 !isFunction(o2[key])) return false;
1215 var csp = function() {
1216 if (!isDefined(csp.rules)) {
1219 var ngCspElement = (window.document.querySelector('[ng-csp]') ||
1220 window.document.querySelector('[data-ng-csp]'));
1223 var ngCspAttribute = ngCspElement.getAttribute('ng-csp') ||
1224 ngCspElement.getAttribute('data-ng-csp');
1226 noUnsafeEval: !ngCspAttribute || (ngCspAttribute.indexOf('no-unsafe-eval') !== -1),
1227 noInlineStyle: !ngCspAttribute || (ngCspAttribute.indexOf('no-inline-style') !== -1)
1231 noUnsafeEval: noUnsafeEval(),
1232 noInlineStyle: false
1239 function noUnsafeEval() {
1241 // eslint-disable-next-line no-new, no-new-func
1256 * @param {string=} ngJq the name of the library available under `window`
1257 * to be used for angular.element
1259 * Use this directive to force the angular.element library. This should be
1260 * used to force either jqLite by leaving ng-jq blank or setting the name of
1261 * the jquery variable under window (eg. jQuery).
1263 * Since angular looks for this directive when it is loaded (doesn't wait for the
1264 * DOMContentLoaded event), it must be placed on an element that comes before the script
1265 * which loads angular. Also, only the first instance of `ng-jq` will be used and all
1269 * This example shows how to force jqLite using the `ngJq` directive to the `html` tag.
1278 * This example shows how to use a jQuery based library of a different name.
1279 * The library name must be available at the top most 'window'.
1282 <html ng-app ng-jq="jQueryLib">
1288 var jq = function() {
1289 if (isDefined(jq.name_)) return jq.name_;
1291 var i, ii = ngAttrPrefixes.length, prefix, name;
1292 for (i = 0; i < ii; ++i) {
1293 prefix = ngAttrPrefixes[i];
1294 el = window.document.querySelector('[' + prefix.replace(':', '\\:') + 'jq]');
1296 name = el.getAttribute(prefix + 'jq');
1301 return (jq.name_ = name);
1304 function concat(array1, array2, index) {
1305 return array1.concat(slice.call(array2, index));
1308 function sliceArgs(args, startIndex) {
1309 return slice.call(args, startIndex || 0);
1315 * @name angular.bind
1320 * Returns a function which calls function `fn` bound to `self` (`self` becomes the `this` for
1321 * `fn`). You can supply optional `args` that are prebound to the function. This feature is also
1322 * known as [partial application](http://en.wikipedia.org/wiki/Partial_application), as
1323 * distinguished from [function currying](http://en.wikipedia.org/wiki/Currying#Contrast_with_partial_function_application).
1325 * @param {Object} self Context which `fn` should be evaluated in.
1326 * @param {function()} fn Function to be bound.
1327 * @param {...*} args Optional arguments to be prebound to the `fn` function call.
1328 * @returns {function()} Function that wraps the `fn` with all the specified bindings.
1330 function bind(self, fn) {
1331 var curryArgs = arguments.length > 2 ? sliceArgs(arguments, 2) : [];
1332 if (isFunction(fn) && !(fn instanceof RegExp)) {
1333 return curryArgs.length
1335 return arguments.length
1336 ? fn.apply(self, concat(curryArgs, arguments, 0))
1337 : fn.apply(self, curryArgs);
1340 return arguments.length
1341 ? fn.apply(self, arguments)
1345 // In IE, native methods are not functions so they cannot be bound (note: they don't need to be).
1351 function toJsonReplacer(key, value) {
1354 if (typeof key === 'string' && key.charAt(0) === '$' && key.charAt(1) === '$') {
1356 } else if (isWindow(value)) {
1358 } else if (value && window.document === value) {
1360 } else if (isScope(value)) {
1370 * @name angular.toJson
1375 * Serializes input into a JSON-formatted string. Properties with leading $$ characters will be
1376 * stripped since angular uses this notation internally.
1378 * @param {Object|Array|Date|string|number|boolean} obj Input to be serialized into JSON.
1379 * @param {boolean|number} [pretty=2] If set to true, the JSON output will contain newlines and whitespace.
1380 * If set to an integer, the JSON output will contain that many spaces per indentation.
1381 * @returns {string|undefined} JSON-ified string representing `obj`.
1384 * The Safari browser throws a `RangeError` instead of returning `null` when it tries to stringify a `Date`
1385 * object with an invalid date value. The only reliable way to prevent this is to monkeypatch the
1386 * `Date.prototype.toJSON` method as follows:
1389 * var _DatetoJSON = Date.prototype.toJSON;
1390 * Date.prototype.toJSON = function() {
1392 * return _DatetoJSON.call(this);
1394 * if (e instanceof RangeError) {
1402 * See https://github.com/angular/angular.js/pull/14221 for more information.
1404 function toJson(obj, pretty) {
1405 if (isUndefined(obj)) return undefined;
1406 if (!isNumber(pretty)) {
1407 pretty = pretty ? 2 : null;
1409 return JSON.stringify(obj, toJsonReplacer, pretty);
1415 * @name angular.fromJson
1420 * Deserializes a JSON string.
1422 * @param {string} json JSON string to deserialize.
1423 * @returns {Object|Array|string|number} Deserialized JSON string.
1425 function fromJson(json) {
1426 return isString(json)
1432 var ALL_COLONS = /:/g;
1433 function timezoneToOffset(timezone, fallback) {
1434 // Support: IE 9-11 only, Edge 13-14+
1435 // IE/Edge do not "understand" colon (`:`) in timezone
1436 timezone = timezone.replace(ALL_COLONS, '');
1437 var requestedTimezoneOffset = Date.parse('Jan 01, 1970 00:00:00 ' + timezone) / 60000;
1438 return isNumberNaN(requestedTimezoneOffset) ? fallback : requestedTimezoneOffset;
1442 function addDateMinutes(date, minutes) {
1443 date = new Date(date.getTime());
1444 date.setMinutes(date.getMinutes() + minutes);
1449 function convertTimezoneToLocal(date, timezone, reverse) {
1450 reverse = reverse ? -1 : 1;
1451 var dateTimezoneOffset = date.getTimezoneOffset();
1452 var timezoneOffset = timezoneToOffset(timezone, dateTimezoneOffset);
1453 return addDateMinutes(date, reverse * (timezoneOffset - dateTimezoneOffset));
1458 * @returns {string} Returns the string representation of the element.
1460 function startingTag(element) {
1461 element = jqLite(element).clone();
1463 // turns out IE does not let you set .html() on elements which
1464 // are not allowed to have children. So we just ignore it.
1466 } catch (e) { /* empty */ }
1467 var elemHtml = jqLite('<div>').append(element).html();
1469 return element[0].nodeType === NODE_TYPE_TEXT ? lowercase(elemHtml) :
1471 match(/^(<[^>]+>)/)[1].
1472 replace(/^<([\w-]+)/, function(match, nodeName) {return '<' + lowercase(nodeName);});
1474 return lowercase(elemHtml);
1480 /////////////////////////////////////////////////
1483 * Tries to decode the URI component without throwing an exception.
1486 * @param str value potential URI component to check.
1487 * @returns {boolean} True if `value` can be decoded
1488 * with the decodeURIComponent function.
1490 function tryDecodeURIComponent(value) {
1492 return decodeURIComponent(value);
1494 // Ignore any invalid uri component.
1500 * Parses an escaped url query string into key-value pairs.
1501 * @returns {Object.<string,boolean|Array>}
1503 function parseKeyValue(/**string*/keyValue) {
1505 forEach((keyValue || '').split('&'), function(keyValue) {
1506 var splitPoint, key, val;
1508 key = keyValue = keyValue.replace(/\+/g,'%20');
1509 splitPoint = keyValue.indexOf('=');
1510 if (splitPoint !== -1) {
1511 key = keyValue.substring(0, splitPoint);
1512 val = keyValue.substring(splitPoint + 1);
1514 key = tryDecodeURIComponent(key);
1515 if (isDefined(key)) {
1516 val = isDefined(val) ? tryDecodeURIComponent(val) : true;
1517 if (!hasOwnProperty.call(obj, key)) {
1519 } else if (isArray(obj[key])) {
1522 obj[key] = [obj[key],val];
1530 function toKeyValue(obj) {
1532 forEach(obj, function(value, key) {
1533 if (isArray(value)) {
1534 forEach(value, function(arrayValue) {
1535 parts.push(encodeUriQuery(key, true) +
1536 (arrayValue === true ? '' : '=' + encodeUriQuery(arrayValue, true)));
1539 parts.push(encodeUriQuery(key, true) +
1540 (value === true ? '' : '=' + encodeUriQuery(value, true)));
1543 return parts.length ? parts.join('&') : '';
1548 * We need our custom method because encodeURIComponent is too aggressive and doesn't follow
1549 * http://www.ietf.org/rfc/rfc3986.txt with regards to the character set (pchar) allowed in path
1552 * pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
1553 * pct-encoded = "%" HEXDIG HEXDIG
1554 * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
1555 * sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
1556 * / "*" / "+" / "," / ";" / "="
1558 function encodeUriSegment(val) {
1559 return encodeUriQuery(val, true).
1560 replace(/%26/gi, '&').
1561 replace(/%3D/gi, '=').
1562 replace(/%2B/gi, '+');
1567 * This method is intended for encoding *key* or *value* parts of query component. We need a custom
1568 * method because encodeURIComponent is too aggressive and encodes stuff that doesn't have to be
1569 * encoded per http://tools.ietf.org/html/rfc3986:
1570 * query = *( pchar / "/" / "?" )
1571 * pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
1572 * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
1573 * pct-encoded = "%" HEXDIG HEXDIG
1574 * sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
1575 * / "*" / "+" / "," / ";" / "="
1577 function encodeUriQuery(val, pctEncodeSpaces) {
1578 return encodeURIComponent(val).
1579 replace(/%40/gi, '@').
1580 replace(/%3A/gi, ':').
1581 replace(/%24/g, '$').
1582 replace(/%2C/gi, ',').
1583 replace(/%3B/gi, ';').
1584 replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
1587 var ngAttrPrefixes = ['ng-', 'data-ng-', 'ng:', 'x-ng-'];
1589 function getNgAttribute(element, ngAttr) {
1590 var attr, i, ii = ngAttrPrefixes.length;
1591 for (i = 0; i < ii; ++i) {
1592 attr = ngAttrPrefixes[i] + ngAttr;
1593 if (isString(attr = element.getAttribute(attr))) {
1600 function allowAutoBootstrap(document) {
1601 var script = document.currentScript;
1604 // IE does not have `document.currentScript`
1608 // If the `currentScript` property has been clobbered just return false, since this indicates a probable attack
1609 if (!(script instanceof window.HTMLScriptElement || script instanceof window.SVGScriptElement)) {
1613 var attributes = script.attributes;
1614 var srcs = [attributes.getNamedItem('src'), attributes.getNamedItem('href'), attributes.getNamedItem('xlink:href')];
1616 return srcs.every(function(src) {
1624 var link = document.createElement('a');
1625 link.href = src.value;
1627 if (document.location.origin === link.origin) {
1628 // Same-origin resources are always allowed, even for non-whitelisted schemes.
1631 // Disabled bootstrapping unless angular.js was loaded from a known scheme used on the web.
1632 // This is to prevent angular.js bundled with browser extensions from being used to bypass the
1633 // content security policy in web pages and other browser extensions.
1634 switch (link.protocol) {
1648 // Cached as it has to run during loading so that document.currentScript is available.
1649 var isAutoBootstrapAllowed = allowAutoBootstrap(window.document);
1657 * @param {angular.Module} ngApp an optional application
1658 * {@link angular.module module} name to load.
1659 * @param {boolean=} ngStrictDi if this attribute is present on the app element, the injector will be
1660 * created in "strict-di" mode. This means that the application will fail to invoke functions which
1661 * do not use explicit function annotation (and are thus unsuitable for minification), as described
1662 * in {@link guide/di the Dependency Injection guide}, and useful debugging info will assist in
1663 * tracking down the root of these bugs.
1667 * Use this directive to **auto-bootstrap** an AngularJS application. The `ngApp` directive
1668 * designates the **root element** of the application and is typically placed near the root element
1669 * of the page - e.g. on the `<body>` or `<html>` tags.
1671 * There are a few things to keep in mind when using `ngApp`:
1672 * - only one AngularJS application can be auto-bootstrapped per HTML document. The first `ngApp`
1673 * found in the document will be used to define the root element to auto-bootstrap as an
1674 * application. To run multiple applications in an HTML document you must manually bootstrap them using
1675 * {@link angular.bootstrap} instead.
1676 * - AngularJS applications cannot be nested within each other.
1677 * - Do not use a directive that uses {@link ng.$compile#transclusion transclusion} on the same element as `ngApp`.
1678 * This includes directives such as {@link ng.ngIf `ngIf`}, {@link ng.ngInclude `ngInclude`} and
1679 * {@link ngRoute.ngView `ngView`}.
1680 * Doing this misplaces the app {@link ng.$rootElement `$rootElement`} and the app's {@link auto.$injector injector},
1681 * causing animations to stop working and making the injector inaccessible from outside the app.
1683 * You can specify an **AngularJS module** to be used as the root module for the application. This
1684 * module will be loaded into the {@link auto.$injector} when the application is bootstrapped. It
1685 * should contain the application code needed or have dependencies on other modules that will
1686 * contain the code. See {@link angular.module} for more information.
1688 * In the example below if the `ngApp` directive were not placed on the `html` element then the
1689 * document would not be compiled, the `AppController` would not be instantiated and the `{{ a+b }}`
1690 * would not be resolved to `3`.
1692 * `ngApp` is the easiest, and most common way to bootstrap an application.
1694 <example module="ngAppDemo" name="ng-app">
1695 <file name="index.html">
1696 <div ng-controller="ngAppDemoController">
1697 I can add: {{a}} + {{b}} = {{ a+b }}
1700 <file name="script.js">
1701 angular.module('ngAppDemo', []).controller('ngAppDemoController', function($scope) {
1708 * Using `ngStrictDi`, you would see something like this:
1710 <example ng-app-included="true" name="strict-di">
1711 <file name="index.html">
1712 <div ng-app="ngAppStrictDemo" ng-strict-di>
1713 <div ng-controller="GoodController1">
1714 I can add: {{a}} + {{b}} = {{ a+b }}
1716 <p>This renders because the controller does not fail to
1717 instantiate, by using explicit annotation style (see
1718 script.js for details)
1722 <div ng-controller="GoodController2">
1723 Name: <input ng-model="name"><br />
1726 <p>This renders because the controller does not fail to
1727 instantiate, by using explicit annotation style
1728 (see script.js for details)
1732 <div ng-controller="BadController">
1733 I can add: {{a}} + {{b}} = {{ a+b }}
1735 <p>The controller could not be instantiated, due to relying
1736 on automatic function annotations (which are disabled in
1737 strict mode). As such, the content of this section is not
1738 interpolated, and there should be an error in your web console.
1743 <file name="script.js">
1744 angular.module('ngAppStrictDemo', [])
1745 // BadController will fail to instantiate, due to relying on automatic function annotation,
1746 // rather than an explicit annotation
1747 .controller('BadController', function($scope) {
1751 // Unlike BadController, GoodController1 and GoodController2 will not fail to be instantiated,
1752 // due to using explicit annotations using the array style and $inject property, respectively.
1753 .controller('GoodController1', ['$scope', function($scope) {
1757 .controller('GoodController2', GoodController2);
1758 function GoodController2($scope) {
1759 $scope.name = 'World';
1761 GoodController2.$inject = ['$scope'];
1763 <file name="style.css">
1764 div[ng-controller] {
1766 -webkit-border-radius: 4px;
1771 div[ng-controller^=Good] {
1772 border-color: #d6e9c6;
1773 background-color: #dff0d8;
1776 div[ng-controller^=Bad] {
1777 border-color: #ebccd1;
1778 background-color: #f2dede;
1785 function angularInit(element, bootstrap) {
1790 // The element `element` has priority over any other element.
1791 forEach(ngAttrPrefixes, function(prefix) {
1792 var name = prefix + 'app';
1794 if (!appElement && element.hasAttribute && element.hasAttribute(name)) {
1795 appElement = element;
1796 module = element.getAttribute(name);
1799 forEach(ngAttrPrefixes, function(prefix) {
1800 var name = prefix + 'app';
1803 if (!appElement && (candidate = element.querySelector('[' + name.replace(':', '\\:') + ']'))) {
1804 appElement = candidate;
1805 module = candidate.getAttribute(name);
1809 if (!isAutoBootstrapAllowed) {
1810 window.console.error('Angular: disabling automatic bootstrap. <script> protocol indicates ' +
1811 'an extension, document.location.href does not match.');
1814 config.strictDi = getNgAttribute(appElement, 'strict-di') !== null;
1815 bootstrap(appElement, module ? [module] : [], config);
1821 * @name angular.bootstrap
1824 * Use this function to manually start up angular application.
1826 * For more information, see the {@link guide/bootstrap Bootstrap guide}.
1828 * Angular will detect if it has been loaded into the browser more than once and only allow the
1829 * first loaded script to be bootstrapped and will report a warning to the browser console for
1830 * each of the subsequent scripts. This prevents strange results in applications, where otherwise
1831 * multiple instances of Angular try to work on the DOM.
1833 * <div class="alert alert-warning">
1834 * **Note:** Protractor based end-to-end tests cannot use this function to bootstrap manually.
1835 * They must use {@link ng.directive:ngApp ngApp}.
1838 * <div class="alert alert-warning">
1839 * **Note:** Do not bootstrap the app on an element with a directive that uses {@link ng.$compile#transclusion transclusion},
1840 * such as {@link ng.ngIf `ngIf`}, {@link ng.ngInclude `ngInclude`} and {@link ngRoute.ngView `ngView`}.
1841 * Doing this misplaces the app {@link ng.$rootElement `$rootElement`} and the app's {@link auto.$injector injector},
1842 * causing animations to stop working and making the injector inaccessible from outside the app.
1849 * <div ng-controller="WelcomeController">
1853 * <script src="angular.js"></script>
1855 * var app = angular.module('demo', [])
1856 * .controller('WelcomeController', function($scope) {
1857 * $scope.greeting = 'Welcome!';
1859 * angular.bootstrap(document, ['demo']);
1865 * @param {DOMElement} element DOM element which is the root of angular application.
1866 * @param {Array<String|Function|Array>=} modules an array of modules to load into the application.
1867 * Each item in the array should be the name of a predefined module or a (DI annotated)
1868 * function that will be invoked by the injector as a `config` block.
1869 * See: {@link angular.module modules}
1870 * @param {Object=} config an object for defining configuration options for the application. The
1871 * following keys are supported:
1873 * * `strictDi` - disable automatic function annotation for the application. This is meant to
1874 * assist in finding bugs which break minified code. Defaults to `false`.
1876 * @returns {auto.$injector} Returns the newly created injector for this app.
1878 function bootstrap(element, modules, config) {
1879 if (!isObject(config)) config = {};
1880 var defaultConfig = {
1883 config = extend(defaultConfig, config);
1884 var doBootstrap = function() {
1885 element = jqLite(element);
1887 if (element.injector()) {
1888 var tag = (element[0] === window.document) ? 'document' : startingTag(element);
1889 // Encode angle brackets to prevent input from being sanitized to empty string #8683.
1892 'App already bootstrapped with this element \'{0}\'',
1893 tag.replace(/</,'<').replace(/>/,'>'));
1896 modules = modules || [];
1897 modules.unshift(['$provide', function($provide) {
1898 $provide.value('$rootElement', element);
1901 if (config.debugInfoEnabled) {
1902 // Pushing so that this overrides `debugInfoEnabled` setting defined in user's `modules`.
1903 modules.push(['$compileProvider', function($compileProvider) {
1904 $compileProvider.debugInfoEnabled(true);
1908 modules.unshift('ng');
1909 var injector = createInjector(modules, config.strictDi);
1910 injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector',
1911 function bootstrapApply(scope, element, compile, injector) {
1912 scope.$apply(function() {
1913 element.data('$injector', injector);
1914 compile(element)(scope);
1921 var NG_ENABLE_DEBUG_INFO = /^NG_ENABLE_DEBUG_INFO!/;
1922 var NG_DEFER_BOOTSTRAP = /^NG_DEFER_BOOTSTRAP!/;
1924 if (window && NG_ENABLE_DEBUG_INFO.test(window.name)) {
1925 config.debugInfoEnabled = true;
1926 window.name = window.name.replace(NG_ENABLE_DEBUG_INFO, '');
1929 if (window && !NG_DEFER_BOOTSTRAP.test(window.name)) {
1930 return doBootstrap();
1933 window.name = window.name.replace(NG_DEFER_BOOTSTRAP, '');
1934 angular.resumeBootstrap = function(extraModules) {
1935 forEach(extraModules, function(module) {
1936 modules.push(module);
1938 return doBootstrap();
1941 if (isFunction(angular.resumeDeferredBootstrap)) {
1942 angular.resumeDeferredBootstrap();
1948 * @name angular.reloadWithDebugInfo
1951 * Use this function to reload the current application with debug information turned on.
1952 * This takes precedence over a call to `$compileProvider.debugInfoEnabled(false)`.
1954 * See {@link ng.$compileProvider#debugInfoEnabled} for more.
1956 function reloadWithDebugInfo() {
1957 window.name = 'NG_ENABLE_DEBUG_INFO!' + window.name;
1958 window.location.reload();
1962 * @name angular.getTestability
1965 * Get the testability service for the instance of Angular on the given
1967 * @param {DOMElement} element DOM element which is the root of angular application.
1969 function getTestability(rootElement) {
1970 var injector = angular.element(rootElement).injector();
1972 throw ngMinErr('test',
1973 'no injector found for element argument to getTestability');
1975 return injector.get('$$testability');
1978 var SNAKE_CASE_REGEXP = /[A-Z]/g;
1979 function snake_case(name, separator) {
1980 separator = separator || '_';
1981 return name.replace(SNAKE_CASE_REGEXP, function(letter, pos) {
1982 return (pos ? separator : '') + letter.toLowerCase();
1986 var bindJQueryFired = false;
1987 function bindJQuery() {
1988 var originalCleanData;
1990 if (bindJQueryFired) {
1994 // bind to jQuery if present;
1996 jQuery = isUndefined(jqName) ? window.jQuery : // use jQuery (if present)
1997 !jqName ? undefined : // use jqLite
1998 window[jqName]; // use jQuery specified by `ngJq`
2000 // Use jQuery if it exists with proper functionality, otherwise default to us.
2001 // Angular 1.2+ requires jQuery 1.7+ for on()/off() support.
2002 // Angular 1.3+ technically requires at least jQuery 2.1+ but it may work with older
2003 // versions. It will not work for sure with jQuery <1.7, though.
2004 if (jQuery && jQuery.fn.on) {
2007 scope: JQLitePrototype.scope,
2008 isolateScope: JQLitePrototype.isolateScope,
2009 controller: /** @type {?} */ (JQLitePrototype).controller,
2010 injector: JQLitePrototype.injector,
2011 inheritedData: JQLitePrototype.inheritedData
2014 // All nodes removed from the DOM via various jQuery APIs like .remove()
2015 // are passed through jQuery.cleanData. Monkey-patch this method to fire
2016 // the $destroy event on all removed nodes.
2017 originalCleanData = jQuery.cleanData;
2018 jQuery.cleanData = function(elems) {
2020 for (var i = 0, elem; (elem = elems[i]) != null; i++) {
2021 events = jQuery._data(elem, 'events');
2022 if (events && events.$destroy) {
2023 jQuery(elem).triggerHandler('$destroy');
2026 originalCleanData(elems);
2032 angular.element = jqLite;
2034 // Prevent double-proxying.
2035 bindJQueryFired = true;
2039 * throw error if the argument is falsy.
2041 function assertArg(arg, name, reason) {
2043 throw ngMinErr('areq', 'Argument \'{0}\' is {1}', (name || '?'), (reason || 'required'));
2048 function assertArgFn(arg, name, acceptArrayAnnotation) {
2049 if (acceptArrayAnnotation && isArray(arg)) {
2050 arg = arg[arg.length - 1];
2053 assertArg(isFunction(arg), name, 'not a function, got ' +
2054 (arg && typeof arg === 'object' ? arg.constructor.name || 'Object' : typeof arg));
2059 * throw error if the name given is hasOwnProperty
2060 * @param {String} name the name to test
2061 * @param {String} context the context in which the name is used, such as module or directive
2063 function assertNotHasOwnProperty(name, context) {
2064 if (name === 'hasOwnProperty') {
2065 throw ngMinErr('badname', 'hasOwnProperty is not a valid {0} name', context);
2070 * Return the value accessible from the object by path. Any undefined traversals are ignored
2071 * @param {Object} obj starting object
2072 * @param {String} path path to traverse
2073 * @param {boolean} [bindFnToScope=true]
2074 * @returns {Object} value as accessible by path
2076 //TODO(misko): this function needs to be removed
2077 function getter(obj, path, bindFnToScope) {
2078 if (!path) return obj;
2079 var keys = path.split('.');
2081 var lastInstance = obj;
2082 var len = keys.length;
2084 for (var i = 0; i < len; i++) {
2087 obj = (lastInstance = obj)[key];
2090 if (!bindFnToScope && isFunction(obj)) {
2091 return bind(lastInstance, obj);
2097 * Return the DOM siblings between the first and last node in the given array.
2098 * @param {Array} array like object
2099 * @returns {Array} the inputted object or a jqLite collection containing the nodes
2101 function getBlockNodes(nodes) {
2102 // TODO(perf): update `nodes` instead of creating a new object?
2103 var node = nodes[0];
2104 var endNode = nodes[nodes.length - 1];
2107 for (var i = 1; node !== endNode && (node = node.nextSibling); i++) {
2108 if (blockNodes || nodes[i] !== node) {
2110 blockNodes = jqLite(slice.call(nodes, 0, i));
2112 blockNodes.push(node);
2116 return blockNodes || nodes;
2121 * Creates a new object without a prototype. This object is useful for lookup without having to
2122 * guard against prototypically inherited properties via hasOwnProperty.
2124 * Related micro-benchmarks:
2125 * - http://jsperf.com/object-create2
2126 * - http://jsperf.com/proto-map-lookup/2
2127 * - http://jsperf.com/for-in-vs-object-keys2
2131 function createMap() {
2132 return Object.create(null);
2135 function stringify(value) {
2136 if (value == null) { // null || undefined
2139 switch (typeof value) {
2146 if (hasCustomToString(value) && !isArray(value) && !isDate(value)) {
2147 value = value.toString();
2149 value = toJson(value);
2156 var NODE_TYPE_ELEMENT = 1;
2157 var NODE_TYPE_ATTRIBUTE = 2;
2158 var NODE_TYPE_TEXT = 3;
2159 var NODE_TYPE_COMMENT = 8;
2160 var NODE_TYPE_DOCUMENT = 9;
2161 var NODE_TYPE_DOCUMENT_FRAGMENT = 11;
2165 * @name angular.Module
2169 * Interface for configuring angular {@link angular.module modules}.
2172 function setupModuleLoader(window) {
2174 var $injectorMinErr = minErr('$injector');
2175 var ngMinErr = minErr('ng');
2177 function ensure(obj, name, factory) {
2178 return obj[name] || (obj[name] = factory());
2181 var angular = ensure(window, 'angular', Object);
2183 // We need to expose `angular.$$minErr` to modules such as `ngResource` that reference it during bootstrap
2184 angular.$$minErr = angular.$$minErr || minErr;
2186 return ensure(angular, 'module', function() {
2187 /** @type {Object.<string, angular.Module>} */
2192 * @name angular.module
2196 * The `angular.module` is a global place for creating, registering and retrieving Angular
2198 * All modules (angular core or 3rd party) that should be available to an application must be
2199 * registered using this mechanism.
2201 * Passing one argument retrieves an existing {@link angular.Module},
2202 * whereas passing more than one argument creates a new {@link angular.Module}
2207 * A module is a collection of services, directives, controllers, filters, and configuration information.
2208 * `angular.module` is used to configure the {@link auto.$injector $injector}.
2211 * // Create a new module
2212 * var myModule = angular.module('myModule', []);
2214 * // register a new service
2215 * myModule.value('appName', 'MyCoolApp');
2217 * // configure existing services inside initialization blocks.
2218 * myModule.config(['$locationProvider', function($locationProvider) {
2219 * // Configure existing providers
2220 * $locationProvider.hashPrefix('!');
2224 * Then you can create an injector and load your modules like this:
2227 * var injector = angular.injector(['ng', 'myModule'])
2230 * However it's more likely that you'll just use
2231 * {@link ng.directive:ngApp ngApp} or
2232 * {@link angular.bootstrap} to simplify this process for you.
2234 * @param {!string} name The name of the module to create or retrieve.
2235 * @param {!Array.<string>=} requires If specified then new module is being created. If
2236 * unspecified then the module is being retrieved for further configuration.
2237 * @param {Function=} configFn Optional configuration function for the module. Same as
2238 * {@link angular.Module#config Module#config()}.
2239 * @returns {angular.Module} new module with the {@link angular.Module} api.
2241 return function module(name, requires, configFn) {
2245 var assertNotHasOwnProperty = function(name, context) {
2246 if (name === 'hasOwnProperty') {
2247 throw ngMinErr('badname', 'hasOwnProperty is not a valid {0} name', context);
2251 assertNotHasOwnProperty(name, 'module');
2252 if (requires && modules.hasOwnProperty(name)) {
2253 modules[name] = null;
2255 return ensure(modules, name, function() {
2257 throw $injectorMinErr('nomod', 'Module \'{0}\' is not available! You either misspelled ' +
2258 'the module name or forgot to load it. If registering a module ensure that you ' +
2259 'specify the dependencies as the second argument.', name);
2262 /** @type {!Array.<Array.<*>>} */
2263 var invokeQueue = [];
2265 /** @type {!Array.<Function>} */
2266 var configBlocks = [];
2268 /** @type {!Array.<Function>} */
2271 var config = invokeLater('$injector', 'invoke', 'push', configBlocks);
2273 /** @type {angular.Module} */
2274 var moduleInstance = {
2276 _invokeQueue: invokeQueue,
2277 _configBlocks: configBlocks,
2278 _runBlocks: runBlocks,
2282 * @name angular.Module#info
2285 * @param {Object=} info Information about the module
2286 * @returns {Object|Module} The current info object for this module if called as a getter,
2287 * or `this` if called as a setter.
2290 * Read and write custom information about this module.
2291 * For example you could put the version of the module in here.
2294 * angular.module('myModule', []).info({ version: '1.0.0' });
2297 * The version could then be read back out by accessing the module elsewhere:
2300 * var version = angular.module('myModule').info().version;
2303 * You can also retrieve this information during runtime via the
2304 * {@link $injector#modules `$injector.modules`} property:
2307 * var version = $injector.modules['myModule'].info().version;
2310 info: function(value) {
2311 if (isDefined(value)) {
2312 if (!isObject(value)) throw ngMinErr('aobj', 'Argument \'{0}\' must be an object', 'value');
2321 * @name angular.Module#requires
2325 * Holds the list of modules which the injector will load before the current module is
2332 * @name angular.Module#name
2336 * Name of the module.
2343 * @name angular.Module#provider
2345 * @param {string} name service name
2346 * @param {Function} providerType Construction function for creating new instance of the
2349 * See {@link auto.$provide#provider $provide.provider()}.
2351 provider: invokeLaterAndSetModuleName('$provide', 'provider'),
2355 * @name angular.Module#factory
2357 * @param {string} name service name
2358 * @param {Function} providerFunction Function for creating new instance of the service.
2360 * See {@link auto.$provide#factory $provide.factory()}.
2362 factory: invokeLaterAndSetModuleName('$provide', 'factory'),
2366 * @name angular.Module#service
2368 * @param {string} name service name
2369 * @param {Function} constructor A constructor function that will be instantiated.
2371 * See {@link auto.$provide#service $provide.service()}.
2373 service: invokeLaterAndSetModuleName('$provide', 'service'),
2377 * @name angular.Module#value
2379 * @param {string} name service name
2380 * @param {*} object Service instance object.
2382 * See {@link auto.$provide#value $provide.value()}.
2384 value: invokeLater('$provide', 'value'),
2388 * @name angular.Module#constant
2390 * @param {string} name constant name
2391 * @param {*} object Constant value.
2393 * Because the constants are fixed, they get applied before other provide methods.
2394 * See {@link auto.$provide#constant $provide.constant()}.
2396 constant: invokeLater('$provide', 'constant', 'unshift'),
2400 * @name angular.Module#decorator
2402 * @param {string} name The name of the service to decorate.
2403 * @param {Function} decorFn This function will be invoked when the service needs to be
2404 * instantiated and should return the decorated service instance.
2406 * See {@link auto.$provide#decorator $provide.decorator()}.
2408 decorator: invokeLaterAndSetModuleName('$provide', 'decorator', configBlocks),
2412 * @name angular.Module#animation
2414 * @param {string} name animation name
2415 * @param {Function} animationFactory Factory function for creating new instance of an
2419 * **NOTE**: animations take effect only if the **ngAnimate** module is loaded.
2422 * Defines an animation hook that can be later used with
2423 * {@link $animate $animate} service and directives that use this service.
2426 * module.animation('.animation-name', function($inject1, $inject2) {
2428 * eventName : function(element, done) {
2429 * //code to run the animation
2430 * //once complete, then run done()
2431 * return function cancellationFunction(element) {
2432 * //code to cancel the animation
2439 * See {@link ng.$animateProvider#register $animateProvider.register()} and
2440 * {@link ngAnimate ngAnimate module} for more information.
2442 animation: invokeLaterAndSetModuleName('$animateProvider', 'register'),
2446 * @name angular.Module#filter
2448 * @param {string} name Filter name - this must be a valid angular expression identifier
2449 * @param {Function} filterFactory Factory function for creating new instance of filter.
2451 * See {@link ng.$filterProvider#register $filterProvider.register()}.
2453 * <div class="alert alert-warning">
2454 * **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`.
2455 * Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace
2456 * your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
2457 * (`myapp_subsection_filterx`).
2460 filter: invokeLaterAndSetModuleName('$filterProvider', 'register'),
2464 * @name angular.Module#controller
2466 * @param {string|Object} name Controller name, or an object map of controllers where the
2467 * keys are the names and the values are the constructors.
2468 * @param {Function} constructor Controller constructor function.
2470 * See {@link ng.$controllerProvider#register $controllerProvider.register()}.
2472 controller: invokeLaterAndSetModuleName('$controllerProvider', 'register'),
2476 * @name angular.Module#directive
2478 * @param {string|Object} name Directive name, or an object map of directives where the
2479 * keys are the names and the values are the factories.
2480 * @param {Function} directiveFactory Factory function for creating new instance of
2483 * See {@link ng.$compileProvider#directive $compileProvider.directive()}.
2485 directive: invokeLaterAndSetModuleName('$compileProvider', 'directive'),
2489 * @name angular.Module#component
2491 * @param {string} name Name of the component in camel-case (i.e. myComp which will match as my-comp)
2492 * @param {Object} options Component definition object (a simplified
2493 * {@link ng.$compile#directive-definition-object directive definition object})
2496 * See {@link ng.$compileProvider#component $compileProvider.component()}.
2498 component: invokeLaterAndSetModuleName('$compileProvider', 'component'),
2502 * @name angular.Module#config
2504 * @param {Function} configFn Execute this function on module load. Useful for service
2507 * Use this method to register work which needs to be performed on module loading.
2508 * For more about how to configure services, see
2509 * {@link providers#provider-recipe Provider Recipe}.
2515 * @name angular.Module#run
2517 * @param {Function} initializationFn Execute this function after injector creation.
2518 * Useful for application initialization.
2520 * Use this method to register work which should be performed when the injector is done
2521 * loading all modules.
2523 run: function(block) {
2524 runBlocks.push(block);
2533 return moduleInstance;
2536 * @param {string} provider
2537 * @param {string} method
2538 * @param {String=} insertMethod
2539 * @returns {angular.Module}
2541 function invokeLater(provider, method, insertMethod, queue) {
2542 if (!queue) queue = invokeQueue;
2544 queue[insertMethod || 'push']([provider, method, arguments]);
2545 return moduleInstance;
2550 * @param {string} provider
2551 * @param {string} method
2552 * @returns {angular.Module}
2554 function invokeLaterAndSetModuleName(provider, method, queue) {
2555 if (!queue) queue = invokeQueue;
2556 return function(recipeName, factoryFunction) {
2557 if (factoryFunction && isFunction(factoryFunction)) factoryFunction.$$moduleName = name;
2558 queue.push([provider, method, arguments]);
2559 return moduleInstance;
2568 /* global shallowCopy: true */
2571 * Creates a shallow copy of an object, an array or a primitive.
2573 * Assumes that there are no proto properties for objects.
2575 function shallowCopy(src, dst) {
2579 for (var i = 0, ii = src.length; i < ii; i++) {
2582 } else if (isObject(src)) {
2585 for (var key in src) {
2586 if (!(key.charAt(0) === '$' && key.charAt(1) === '$')) {
2587 dst[key] = src[key];
2595 /* global toDebugString: true */
2597 function serializeObject(obj, maxDepth) {
2600 // There is no direct way to stringify object until reaching a specific depth
2601 // and a very deep object can cause a performance issue, so we copy the object
2602 // based on this specific depth and then stringify it.
2603 if (isValidObjectMaxDepth(maxDepth)) {
2604 obj = copy(obj, null, maxDepth);
2606 return JSON.stringify(obj, function(key, val) {
2607 val = toJsonReplacer(key, val);
2608 if (isObject(val)) {
2610 if (seen.indexOf(val) >= 0) return '...';
2618 function toDebugString(obj, maxDepth) {
2619 if (typeof obj === 'function') {
2620 return obj.toString().replace(/ \{[\s\S]*$/, '');
2621 } else if (isUndefined(obj)) {
2623 } else if (typeof obj !== 'string') {
2624 return serializeObject(obj, maxDepth);
2629 /* global angularModule: true,
2634 htmlAnchorDirective,
2642 ngBindHtmlDirective,
2643 ngBindTemplateDirective,
2645 ngClassEvenDirective,
2646 ngClassOddDirective,
2648 ngControllerDirective,
2653 ngIncludeFillContentDirective,
2655 ngNonBindableDirective,
2656 ngPluralizeDirective,
2661 ngSwitchWhenDirective,
2662 ngSwitchDefaultDirective,
2664 ngTranscludeDirective,
2677 ngModelOptionsDirective,
2678 ngAttributeAliasDirectives,
2681 $AnchorScrollProvider,
2683 $CoreAnimateCssProvider,
2684 $$CoreAnimateJsProvider,
2685 $$CoreAnimateQueueProvider,
2686 $$AnimateRunnerFactoryProvider,
2687 $$AnimateAsyncRunFactoryProvider,
2689 $CacheFactoryProvider,
2690 $ControllerProvider,
2693 $$IsDocumentHiddenProvider,
2694 $ExceptionHandlerProvider,
2696 $$ForceReflowProvider,
2697 $InterpolateProvider,
2700 $HttpParamSerializerProvider,
2701 $HttpParamSerializerJQLikeProvider,
2702 $HttpBackendProvider,
2703 $xhrFactoryProvider,
2704 $jsonpCallbacksProvider,
2712 $$SanitizeUriProvider,
2714 $SceDelegateProvider,
2716 $TemplateCacheProvider,
2717 $TemplateRequestProvider,
2718 $$TestabilityProvider,
2723 $$CookieReaderProvider
2729 * @name angular.version
2732 * An object that contains information about the current AngularJS version.
2734 * This object has the following properties:
2736 * - `full` – `{string}` – Full version string, such as "0.9.18".
2737 * - `major` – `{number}` – Major version number, such as "0".
2738 * - `minor` – `{number}` – Minor version number, such as "9".
2739 * - `dot` – `{number}` – Dot version number, such as "18".
2740 * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat".
2743 // These placeholder strings will be replaced by grunt's `build` task.
2744 // They need to be double- or single-quoted.
2749 codeName: 'scriptalicious-bootstrapping'
2753 function publishExternalAPI(angular) {
2755 'errorHandlingConfig': errorHandlingConfig,
2756 'bootstrap': bootstrap,
2763 'injector': createInjector,
2767 'fromJson': fromJson,
2768 'identity': identity,
2769 'isUndefined': isUndefined,
2770 'isDefined': isDefined,
2771 'isString': isString,
2772 'isFunction': isFunction,
2773 'isObject': isObject,
2774 'isNumber': isNumber,
2775 'isElement': isElement,
2779 'lowercase': lowercase,
2780 'uppercase': uppercase,
2781 'callbacks': {$$counter: 0},
2782 'getTestability': getTestability,
2783 'reloadWithDebugInfo': reloadWithDebugInfo,
2786 '$$encodeUriSegment': encodeUriSegment,
2787 '$$encodeUriQuery': encodeUriQuery,
2788 '$$stringify': stringify
2791 angularModule = setupModuleLoader(window);
2793 angularModule('ng', ['ngLocale'], ['$provide',
2794 function ngModule($provide) {
2795 // $$sanitizeUriProvider needs to be before $compileProvider as it is used by it.
2797 $$sanitizeUri: $$SanitizeUriProvider
2799 $provide.provider('$compile', $CompileProvider).
2801 a: htmlAnchorDirective,
2802 input: inputDirective,
2803 textarea: inputDirective,
2804 form: formDirective,
2805 script: scriptDirective,
2806 select: selectDirective,
2807 option: optionDirective,
2808 ngBind: ngBindDirective,
2809 ngBindHtml: ngBindHtmlDirective,
2810 ngBindTemplate: ngBindTemplateDirective,
2811 ngClass: ngClassDirective,
2812 ngClassEven: ngClassEvenDirective,
2813 ngClassOdd: ngClassOddDirective,
2814 ngCloak: ngCloakDirective,
2815 ngController: ngControllerDirective,
2816 ngForm: ngFormDirective,
2817 ngHide: ngHideDirective,
2818 ngIf: ngIfDirective,
2819 ngInclude: ngIncludeDirective,
2820 ngInit: ngInitDirective,
2821 ngNonBindable: ngNonBindableDirective,
2822 ngPluralize: ngPluralizeDirective,
2823 ngRepeat: ngRepeatDirective,
2824 ngShow: ngShowDirective,
2825 ngStyle: ngStyleDirective,
2826 ngSwitch: ngSwitchDirective,
2827 ngSwitchWhen: ngSwitchWhenDirective,
2828 ngSwitchDefault: ngSwitchDefaultDirective,
2829 ngOptions: ngOptionsDirective,
2830 ngTransclude: ngTranscludeDirective,
2831 ngModel: ngModelDirective,
2832 ngList: ngListDirective,
2833 ngChange: ngChangeDirective,
2834 pattern: patternDirective,
2835 ngPattern: patternDirective,
2836 required: requiredDirective,
2837 ngRequired: requiredDirective,
2838 minlength: minlengthDirective,
2839 ngMinlength: minlengthDirective,
2840 maxlength: maxlengthDirective,
2841 ngMaxlength: maxlengthDirective,
2842 ngValue: ngValueDirective,
2843 ngModelOptions: ngModelOptionsDirective
2846 ngInclude: ngIncludeFillContentDirective
2848 directive(ngAttributeAliasDirectives).
2849 directive(ngEventDirectives);
2851 $anchorScroll: $AnchorScrollProvider,
2852 $animate: $AnimateProvider,
2853 $animateCss: $CoreAnimateCssProvider,
2854 $$animateJs: $$CoreAnimateJsProvider,
2855 $$animateQueue: $$CoreAnimateQueueProvider,
2856 $$AnimateRunner: $$AnimateRunnerFactoryProvider,
2857 $$animateAsyncRun: $$AnimateAsyncRunFactoryProvider,
2858 $browser: $BrowserProvider,
2859 $cacheFactory: $CacheFactoryProvider,
2860 $controller: $ControllerProvider,
2861 $document: $DocumentProvider,
2862 $$isDocumentHidden: $$IsDocumentHiddenProvider,
2863 $exceptionHandler: $ExceptionHandlerProvider,
2864 $filter: $FilterProvider,
2865 $$forceReflow: $$ForceReflowProvider,
2866 $interpolate: $InterpolateProvider,
2867 $interval: $IntervalProvider,
2868 $http: $HttpProvider,
2869 $httpParamSerializer: $HttpParamSerializerProvider,
2870 $httpParamSerializerJQLike: $HttpParamSerializerJQLikeProvider,
2871 $httpBackend: $HttpBackendProvider,
2872 $xhrFactory: $xhrFactoryProvider,
2873 $jsonpCallbacks: $jsonpCallbacksProvider,
2874 $location: $LocationProvider,
2876 $parse: $ParseProvider,
2877 $rootScope: $RootScopeProvider,
2881 $sceDelegate: $SceDelegateProvider,
2882 $sniffer: $SnifferProvider,
2883 $templateCache: $TemplateCacheProvider,
2884 $templateRequest: $TemplateRequestProvider,
2885 $$testability: $$TestabilityProvider,
2886 $timeout: $TimeoutProvider,
2887 $window: $WindowProvider,
2888 $$rAF: $$RAFProvider,
2889 $$jqLite: $$jqLiteProvider,
2890 $$Map: $$MapProvider,
2891 $$cookieReader: $$CookieReaderProvider
2895 .info({ angularVersion: '1.6.3' });
2898 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2899 * Any commits to this file should be reviewed with security in mind. *
2900 * Changes to this file can potentially create security vulnerabilities. *
2901 * An approval from 2 Core members with history of modifying *
2902 * this file is required. *
2904 * Does the change somehow allow for arbitrary javascript to be executed? *
2905 * Or allows for someone to change the prototype of built-in objects? *
2906 * Or gives undesired access to variables likes document or window? *
2907 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2910 JQLitePrototype: true,
2915 //////////////////////////////////
2917 //////////////////////////////////
2921 * @name angular.element
2926 * Wraps a raw DOM element or HTML string as a [jQuery](http://jquery.com) element.
2928 * If jQuery is available, `angular.element` is an alias for the
2929 * [jQuery](http://api.jquery.com/jQuery/) function. If jQuery is not available, `angular.element`
2930 * delegates to Angular's built-in subset of jQuery, called "jQuery lite" or **jqLite**.
2932 * jqLite is a tiny, API-compatible subset of jQuery that allows
2933 * Angular to manipulate the DOM in a cross-browser compatible way. jqLite implements only the most
2934 * commonly needed functionality with the goal of having a very small footprint.
2936 * To use `jQuery`, simply ensure it is loaded before the `angular.js` file. You can also use the
2937 * {@link ngJq `ngJq`} directive to specify that jqlite should be used over jQuery, or to use a
2938 * specific version of jQuery if multiple versions exist on the page.
2940 * <div class="alert alert-info">**Note:** All element references in Angular are always wrapped with jQuery or
2941 * jqLite (such as the element argument in a directive's compile / link function). They are never raw DOM references.</div>
2943 * <div class="alert alert-warning">**Note:** Keep in mind that this function will not find elements
2944 * by tag name / CSS selector. For lookups by tag name, try instead `angular.element(document).find(...)`
2945 * or `$document.find()`, or use the standard DOM APIs, e.g. `document.querySelectorAll()`.</div>
2947 * ## Angular's jqLite
2948 * jqLite provides only the following jQuery methods:
2950 * - [`addClass()`](http://api.jquery.com/addClass/) - Does not support a function as first argument
2951 * - [`after()`](http://api.jquery.com/after/)
2952 * - [`append()`](http://api.jquery.com/append/)
2953 * - [`attr()`](http://api.jquery.com/attr/) - Does not support functions as parameters
2954 * - [`bind()`](http://api.jquery.com/bind/) (_deprecated_, use [`on()`](http://api.jquery.com/on/)) - Does not support namespaces, selectors or eventData
2955 * - [`children()`](http://api.jquery.com/children/) - Does not support selectors
2956 * - [`clone()`](http://api.jquery.com/clone/)
2957 * - [`contents()`](http://api.jquery.com/contents/)
2958 * - [`css()`](http://api.jquery.com/css/) - Only retrieves inline-styles, does not call `getComputedStyle()`.
2959 * As a setter, does not convert numbers to strings or append 'px', and also does not have automatic property prefixing.
2960 * - [`data()`](http://api.jquery.com/data/)
2961 * - [`detach()`](http://api.jquery.com/detach/)
2962 * - [`empty()`](http://api.jquery.com/empty/)
2963 * - [`eq()`](http://api.jquery.com/eq/)
2964 * - [`find()`](http://api.jquery.com/find/) - Limited to lookups by tag name
2965 * - [`hasClass()`](http://api.jquery.com/hasClass/)
2966 * - [`html()`](http://api.jquery.com/html/)
2967 * - [`next()`](http://api.jquery.com/next/) - Does not support selectors
2968 * - [`on()`](http://api.jquery.com/on/) - Does not support namespaces, selectors or eventData
2969 * - [`off()`](http://api.jquery.com/off/) - Does not support namespaces, selectors or event object as parameter
2970 * - [`one()`](http://api.jquery.com/one/) - Does not support namespaces or selectors
2971 * - [`parent()`](http://api.jquery.com/parent/) - Does not support selectors
2972 * - [`prepend()`](http://api.jquery.com/prepend/)
2973 * - [`prop()`](http://api.jquery.com/prop/)
2974 * - [`ready()`](http://api.jquery.com/ready/) (_deprecated_, use `angular.element(callback)` instead of `angular.element(document).ready(callback)`)
2975 * - [`remove()`](http://api.jquery.com/remove/)
2976 * - [`removeAttr()`](http://api.jquery.com/removeAttr/) - Does not support multiple attributes
2977 * - [`removeClass()`](http://api.jquery.com/removeClass/) - Does not support a function as first argument
2978 * - [`removeData()`](http://api.jquery.com/removeData/)
2979 * - [`replaceWith()`](http://api.jquery.com/replaceWith/)
2980 * - [`text()`](http://api.jquery.com/text/)
2981 * - [`toggleClass()`](http://api.jquery.com/toggleClass/) - Does not support a function as first argument
2982 * - [`triggerHandler()`](http://api.jquery.com/triggerHandler/) - Passes a dummy event object to handlers
2983 * - [`unbind()`](http://api.jquery.com/unbind/) (_deprecated_, use [`off()`](http://api.jquery.com/off/)) - Does not support namespaces or event object as parameter
2984 * - [`val()`](http://api.jquery.com/val/)
2985 * - [`wrap()`](http://api.jquery.com/wrap/)
2987 * ## jQuery/jqLite Extras
2988 * Angular also provides the following additional methods and events to both jQuery and jqLite:
2991 * - `$destroy` - AngularJS intercepts all jqLite/jQuery's DOM destruction apis and fires this event
2992 * on all DOM nodes being removed. This can be used to clean up any 3rd party bindings to the DOM
2993 * element before it is removed.
2996 * - `controller(name)` - retrieves the controller of the current element or its parent. By default
2997 * retrieves controller associated with the `ngController` directive. If `name` is provided as
2998 * camelCase directive name, then the controller for this directive will be retrieved (e.g.
3000 * - `injector()` - retrieves the injector of the current element or its parent.
3001 * - `scope()` - retrieves the {@link ng.$rootScope.Scope scope} of the current
3002 * element or its parent. Requires {@link guide/production#disabling-debug-data Debug Data} to
3004 * - `isolateScope()` - retrieves an isolate {@link ng.$rootScope.Scope scope} if one is attached directly to the
3005 * current element. This getter should be used only on elements that contain a directive which starts a new isolate
3006 * scope. Calling `scope()` on this element always returns the original non-isolate scope.
3007 * Requires {@link guide/production#disabling-debug-data Debug Data} to be enabled.
3008 * - `inheritedData()` - same as `data()`, but walks up the DOM until a value is found or the top
3009 * parent element is reached.
3011 * @knownIssue You cannot spy on `angular.element` if you are using Jasmine version 1.x. See
3012 * https://github.com/angular/angular.js/issues/14251 for more information.
3014 * @param {string|DOMElement} element HTML string or DOMElement to be wrapped into jQuery.
3015 * @returns {Object} jQuery object.
3018 JQLite.expando = 'ng339';
3020 var jqCache = JQLite.cache = {},
3024 * !!! This is an undocumented "private" function !!!
3026 JQLite._data = function(node) {
3027 //jQuery always returns an object on cache miss
3028 return this.cache[node[this.expando]] || {};
3031 function jqNextId() { return ++jqId; }
3034 var DASH_LOWERCASE_REGEXP = /-([a-z])/g;
3035 var MS_HACK_REGEXP = /^-ms-/;
3036 var MOUSE_EVENT_MAP = { mouseleave: 'mouseout', mouseenter: 'mouseover' };
3037 var jqLiteMinErr = minErr('jqLite');
3040 * Converts kebab-case to camelCase.
3041 * There is also a special case for the ms prefix starting with a lowercase letter.
3042 * @param name Name to normalize
3044 function cssKebabToCamel(name) {
3045 return kebabToCamel(name.replace(MS_HACK_REGEXP, 'ms-'));
3048 function fnCamelCaseReplace(all, letter) {
3049 return letter.toUpperCase();
3053 * Converts kebab-case to camelCase.
3054 * @param name Name to normalize
3056 function kebabToCamel(name) {
3058 .replace(DASH_LOWERCASE_REGEXP, fnCamelCaseReplace);
3061 var SINGLE_TAG_REGEXP = /^<([\w-]+)\s*\/?>(?:<\/\1>|)$/;
3062 var HTML_REGEXP = /<|&#?\w+;/;
3063 var TAG_NAME_REGEXP = /<([\w:-]+)/;
3064 var XHTML_TAG_REGEXP = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^>]*)\/>/gi;
3067 'option': [1, '<select multiple="multiple">', '</select>'],
3069 'thead': [1, '<table>', '</table>'],
3070 'col': [2, '<table><colgroup>', '</colgroup></table>'],
3071 'tr': [2, '<table><tbody>', '</tbody></table>'],
3072 'td': [3, '<table><tbody><tr>', '</tr></tbody></table>'],
3073 '_default': [0, '', '']
3076 wrapMap.optgroup = wrapMap.option;
3077 wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
3078 wrapMap.th = wrapMap.td;
3081 function jqLiteIsTextNode(html) {
3082 return !HTML_REGEXP.test(html);
3085 function jqLiteAcceptsData(node) {
3086 // The window object can accept data but has no nodeType
3087 // Otherwise we are only interested in elements (1) and documents (9)
3088 var nodeType = node.nodeType;
3089 return nodeType === NODE_TYPE_ELEMENT || !nodeType || nodeType === NODE_TYPE_DOCUMENT;
3092 function jqLiteHasData(node) {
3093 for (var key in jqCache[node.ng339]) {
3099 function jqLiteCleanData(nodes) {
3100 for (var i = 0, ii = nodes.length; i < ii; i++) {
3101 jqLiteRemoveData(nodes[i]);
3105 function jqLiteBuildFragment(html, context) {
3107 fragment = context.createDocumentFragment(),
3110 if (jqLiteIsTextNode(html)) {
3111 // Convert non-html into a text node
3112 nodes.push(context.createTextNode(html));
3114 // Convert html into DOM nodes
3115 tmp = fragment.appendChild(context.createElement('div'));
3116 tag = (TAG_NAME_REGEXP.exec(html) || ['', ''])[1].toLowerCase();
3117 wrap = wrapMap[tag] || wrapMap._default;
3118 tmp.innerHTML = wrap[1] + html.replace(XHTML_TAG_REGEXP, '<$1></$2>') + wrap[2];
3120 // Descend through wrappers to the right content
3123 tmp = tmp.lastChild;
3126 nodes = concat(nodes, tmp.childNodes);
3128 tmp = fragment.firstChild;
3129 tmp.textContent = '';
3132 // Remove wrapper from fragment
3133 fragment.textContent = '';
3134 fragment.innerHTML = ''; // Clear inner HTML
3135 forEach(nodes, function(node) {
3136 fragment.appendChild(node);
3142 function jqLiteParseHTML(html, context) {
3143 context = context || window.document;
3146 if ((parsed = SINGLE_TAG_REGEXP.exec(html))) {
3147 return [context.createElement(parsed[1])];
3150 if ((parsed = jqLiteBuildFragment(html, context))) {
3151 return parsed.childNodes;
3157 function jqLiteWrapNode(node, wrapper) {
3158 var parent = node.parentNode;
3161 parent.replaceChild(wrapper, node);
3164 wrapper.appendChild(node);
3168 // IE9-11 has no method "contains" in SVG element and in Node.prototype. Bug #10259.
3169 var jqLiteContains = window.Node.prototype.contains || /** @this */ function(arg) {
3170 // eslint-disable-next-line no-bitwise
3171 return !!(this.compareDocumentPosition(arg) & 16);
3174 /////////////////////////////////////////////
3175 function JQLite(element) {
3176 if (element instanceof JQLite) {
3182 if (isString(element)) {
3183 element = trim(element);
3186 if (!(this instanceof JQLite)) {
3187 if (argIsString && element.charAt(0) !== '<') {
3188 throw jqLiteMinErr('nosel', 'Looking up elements via selectors is not supported by jqLite! See: http://docs.angularjs.org/api/angular.element');
3190 return new JQLite(element);
3194 jqLiteAddNodes(this, jqLiteParseHTML(element));
3195 } else if (isFunction(element)) {
3196 jqLiteReady(element);
3198 jqLiteAddNodes(this, element);
3202 function jqLiteClone(element) {
3203 return element.cloneNode(true);
3206 function jqLiteDealoc(element, onlyDescendants) {
3207 if (!onlyDescendants) jqLiteRemoveData(element);
3209 if (element.querySelectorAll) {
3210 var descendants = element.querySelectorAll('*');
3211 for (var i = 0, l = descendants.length; i < l; i++) {
3212 jqLiteRemoveData(descendants[i]);
3217 function jqLiteOff(element, type, fn, unsupported) {
3218 if (isDefined(unsupported)) throw jqLiteMinErr('offargs', 'jqLite#off() does not support the `selector` argument');
3220 var expandoStore = jqLiteExpandoStore(element);
3221 var events = expandoStore && expandoStore.events;
3222 var handle = expandoStore && expandoStore.handle;
3224 if (!handle) return; //no listeners registered
3227 for (type in events) {
3228 if (type !== '$destroy') {
3229 element.removeEventListener(type, handle);
3231 delete events[type];
3235 var removeHandler = function(type) {
3236 var listenerFns = events[type];
3237 if (isDefined(fn)) {
3238 arrayRemove(listenerFns || [], fn);
3240 if (!(isDefined(fn) && listenerFns && listenerFns.length > 0)) {
3241 element.removeEventListener(type, handle);
3242 delete events[type];
3246 forEach(type.split(' '), function(type) {
3247 removeHandler(type);
3248 if (MOUSE_EVENT_MAP[type]) {
3249 removeHandler(MOUSE_EVENT_MAP[type]);
3255 function jqLiteRemoveData(element, name) {
3256 var expandoId = element.ng339;
3257 var expandoStore = expandoId && jqCache[expandoId];
3261 delete expandoStore.data[name];
3265 if (expandoStore.handle) {
3266 if (expandoStore.events.$destroy) {
3267 expandoStore.handle({}, '$destroy');
3271 delete jqCache[expandoId];
3272 element.ng339 = undefined; // don't delete DOM expandos. IE and Chrome don't like it
3277 function jqLiteExpandoStore(element, createIfNecessary) {
3278 var expandoId = element.ng339,
3279 expandoStore = expandoId && jqCache[expandoId];
3281 if (createIfNecessary && !expandoStore) {
3282 element.ng339 = expandoId = jqNextId();
3283 expandoStore = jqCache[expandoId] = {events: {}, data: {}, handle: undefined};
3286 return expandoStore;
3290 function jqLiteData(element, key, value) {
3291 if (jqLiteAcceptsData(element)) {
3294 var isSimpleSetter = isDefined(value);
3295 var isSimpleGetter = !isSimpleSetter && key && !isObject(key);
3296 var massGetter = !key;
3297 var expandoStore = jqLiteExpandoStore(element, !isSimpleGetter);
3298 var data = expandoStore && expandoStore.data;
3300 if (isSimpleSetter) { // data('key', value)
3301 data[kebabToCamel(key)] = value;
3303 if (massGetter) { // data()
3306 if (isSimpleGetter) { // data('key')
3307 // don't force creation of expandoStore if it doesn't exist yet
3308 return data && data[kebabToCamel(key)];
3309 } else { // mass-setter: data({key1: val1, key2: val2})
3311 data[kebabToCamel(prop)] = key[prop];
3319 function jqLiteHasClass(element, selector) {
3320 if (!element.getAttribute) return false;
3321 return ((' ' + (element.getAttribute('class') || '') + ' ').replace(/[\n\t]/g, ' ').
3322 indexOf(' ' + selector + ' ') > -1);
3325 function jqLiteRemoveClass(element, cssClasses) {
3326 if (cssClasses && element.setAttribute) {
3327 forEach(cssClasses.split(' '), function(cssClass) {
3328 element.setAttribute('class', trim(
3329 (' ' + (element.getAttribute('class') || '') + ' ')
3330 .replace(/[\n\t]/g, ' ')
3331 .replace(' ' + trim(cssClass) + ' ', ' '))
3337 function jqLiteAddClass(element, cssClasses) {
3338 if (cssClasses && element.setAttribute) {
3339 var existingClasses = (' ' + (element.getAttribute('class') || '') + ' ')
3340 .replace(/[\n\t]/g, ' ');
3342 forEach(cssClasses.split(' '), function(cssClass) {
3343 cssClass = trim(cssClass);
3344 if (existingClasses.indexOf(' ' + cssClass + ' ') === -1) {
3345 existingClasses += cssClass + ' ';
3349 element.setAttribute('class', trim(existingClasses));
3354 function jqLiteAddNodes(root, elements) {
3355 // THIS CODE IS VERY HOT. Don't make changes without benchmarking.
3359 // if a Node (the most common case)
3360 if (elements.nodeType) {
3361 root[root.length++] = elements;
3363 var length = elements.length;
3365 // if an Array or NodeList and not a Window
3366 if (typeof length === 'number' && elements.window !== elements) {
3368 for (var i = 0; i < length; i++) {
3369 root[root.length++] = elements[i];
3373 root[root.length++] = elements;
3380 function jqLiteController(element, name) {
3381 return jqLiteInheritedData(element, '$' + (name || 'ngController') + 'Controller');
3384 function jqLiteInheritedData(element, name, value) {
3385 // if element is the document object work with the html element instead
3386 // this makes $(document).scope() possible
3387 if (element.nodeType === NODE_TYPE_DOCUMENT) {
3388 element = element.documentElement;
3390 var names = isArray(name) ? name : [name];
3393 for (var i = 0, ii = names.length; i < ii; i++) {
3394 if (isDefined(value = jqLite.data(element, names[i]))) return value;
3397 // If dealing with a document fragment node with a host element, and no parent, use the host
3398 // element as the parent. This enables directives within a Shadow DOM or polyfilled Shadow DOM
3399 // to lookup parent controllers.
3400 element = element.parentNode || (element.nodeType === NODE_TYPE_DOCUMENT_FRAGMENT && element.host);
3404 function jqLiteEmpty(element) {
3405 jqLiteDealoc(element, true);
3406 while (element.firstChild) {
3407 element.removeChild(element.firstChild);
3411 function jqLiteRemove(element, keepData) {
3412 if (!keepData) jqLiteDealoc(element);
3413 var parent = element.parentNode;
3414 if (parent) parent.removeChild(element);
3418 function jqLiteDocumentLoaded(action, win) {
3419 win = win || window;
3420 if (win.document.readyState === 'complete') {
3421 // Force the action to be run async for consistent behavior
3422 // from the action's point of view
3423 // i.e. it will definitely not be in a $apply
3424 win.setTimeout(action);
3426 // No need to unbind this handler as load is only ever called once
3427 jqLite(win).on('load', action);
3431 function jqLiteReady(fn) {
3432 function trigger() {
3433 window.document.removeEventListener('DOMContentLoaded', trigger);
3434 window.removeEventListener('load', trigger);
3438 // check if document is already loaded
3439 if (window.document.readyState === 'complete') {
3440 window.setTimeout(fn);
3442 // We can not use jqLite since we are not done loading and jQuery could be loaded later.
3444 // Works for modern browsers and IE9
3445 window.document.addEventListener('DOMContentLoaded', trigger);
3447 // Fallback to window.onload for others
3448 window.addEventListener('load', trigger);
3452 //////////////////////////////////////////
3453 // Functions which are declared directly.
3454 //////////////////////////////////////////
3455 var JQLitePrototype = JQLite.prototype = {
3457 toString: function() {
3459 forEach(this, function(e) { value.push('' + e);});
3460 return '[' + value.join(', ') + ']';
3463 eq: function(index) {
3464 return (index >= 0) ? jqLite(this[index]) : jqLite(this[this.length + index]);
3473 //////////////////////////////////////////
3474 // Functions iterating getter/setters.
3475 // these functions return self on setter and
3477 //////////////////////////////////////////
3478 var BOOLEAN_ATTR = {};
3479 forEach('multiple,selected,checked,disabled,readOnly,required,open'.split(','), function(value) {
3480 BOOLEAN_ATTR[lowercase(value)] = value;
3482 var BOOLEAN_ELEMENTS = {};
3483 forEach('input,select,option,textarea,button,form,details'.split(','), function(value) {
3484 BOOLEAN_ELEMENTS[value] = true;
3486 var ALIASED_ATTR = {
3487 'ngMinlength': 'minlength',
3488 'ngMaxlength': 'maxlength',
3491 'ngPattern': 'pattern',
3495 function getBooleanAttrName(element, name) {
3496 // check dom last since we will most likely fail on name
3497 var booleanAttr = BOOLEAN_ATTR[name.toLowerCase()];
3499 // booleanAttr is here twice to minimize DOM access
3500 return booleanAttr && BOOLEAN_ELEMENTS[nodeName_(element)] && booleanAttr;
3503 function getAliasedAttrName(name) {
3504 return ALIASED_ATTR[name];
3509 removeData: jqLiteRemoveData,
3510 hasData: jqLiteHasData,
3511 cleanData: jqLiteCleanData
3512 }, function(fn, name) {
3518 inheritedData: jqLiteInheritedData,
3520 scope: function(element) {
3521 // Can't use jqLiteData here directly so we stay compatible with jQuery!
3522 return jqLite.data(element, '$scope') || jqLiteInheritedData(element.parentNode || element, ['$isolateScope', '$scope']);
3525 isolateScope: function(element) {
3526 // Can't use jqLiteData here directly so we stay compatible with jQuery!
3527 return jqLite.data(element, '$isolateScope') || jqLite.data(element, '$isolateScopeNoTemplate');
3530 controller: jqLiteController,
3532 injector: function(element) {
3533 return jqLiteInheritedData(element, '$injector');
3536 removeAttr: function(element, name) {
3537 element.removeAttribute(name);
3540 hasClass: jqLiteHasClass,
3542 css: function(element, name, value) {
3543 name = cssKebabToCamel(name);
3545 if (isDefined(value)) {
3546 element.style[name] = value;
3548 return element.style[name];
3552 attr: function(element, name, value) {
3554 var nodeType = element.nodeType;
3555 if (nodeType === NODE_TYPE_TEXT || nodeType === NODE_TYPE_ATTRIBUTE || nodeType === NODE_TYPE_COMMENT ||
3556 !element.getAttribute) {
3560 var lowercasedName = lowercase(name);
3561 var isBooleanAttr = BOOLEAN_ATTR[lowercasedName];
3563 if (isDefined(value)) {
3566 if (value === null || (value === false && isBooleanAttr)) {
3567 element.removeAttribute(name);
3569 element.setAttribute(name, isBooleanAttr ? lowercasedName : value);
3574 ret = element.getAttribute(name);
3576 if (isBooleanAttr && ret !== null) {
3577 ret = lowercasedName;
3579 // Normalize non-existing attributes to undefined (as jQuery).
3580 return ret === null ? undefined : ret;
3584 prop: function(element, name, value) {
3585 if (isDefined(value)) {
3586 element[name] = value;
3588 return element[name];
3596 function getText(element, value) {
3597 if (isUndefined(value)) {
3598 var nodeType = element.nodeType;
3599 return (nodeType === NODE_TYPE_ELEMENT || nodeType === NODE_TYPE_TEXT) ? element.textContent : '';
3601 element.textContent = value;
3605 val: function(element, value) {
3606 if (isUndefined(value)) {
3607 if (element.multiple && nodeName_(element) === 'select') {
3609 forEach(element.options, function(option) {
3610 if (option.selected) {
3611 result.push(option.value || option.text);
3616 return element.value;
3618 element.value = value;
3621 html: function(element, value) {
3622 if (isUndefined(value)) {
3623 return element.innerHTML;
3625 jqLiteDealoc(element, true);
3626 element.innerHTML = value;
3630 }, function(fn, name) {
3632 * Properties: writes return selection, reads return first value
3634 JQLite.prototype[name] = function(arg1, arg2) {
3636 var nodeCount = this.length;
3638 // jqLiteHasClass has only two arguments, but is a getter-only fn, so we need to special-case it
3639 // in a way that survives minification.
3640 // jqLiteEmpty takes no arguments but is a setter.
3641 if (fn !== jqLiteEmpty &&
3642 (isUndefined((fn.length === 2 && (fn !== jqLiteHasClass && fn !== jqLiteController)) ? arg1 : arg2))) {
3643 if (isObject(arg1)) {
3645 // we are a write, but the object properties are the key/values
3646 for (i = 0; i < nodeCount; i++) {
3647 if (fn === jqLiteData) {
3648 // data() takes the whole object in jQuery
3652 fn(this[i], key, arg1[key]);
3656 // return self for chaining
3659 // we are a read, so read the first child.
3660 // TODO: do we still need this?
3662 // Only if we have $dv do we iterate over all, otherwise it is just the first element.
3663 var jj = (isUndefined(value)) ? Math.min(nodeCount, 1) : nodeCount;
3664 for (var j = 0; j < jj; j++) {
3665 var nodeValue = fn(this[j], arg1, arg2);
3666 value = value ? value + nodeValue : nodeValue;
3671 // we are a write, so apply to all children
3672 for (i = 0; i < nodeCount; i++) {
3673 fn(this[i], arg1, arg2);
3675 // return self for chaining
3681 function createEventHandler(element, events) {
3682 var eventHandler = function(event, type) {
3683 // jQuery specific api
3684 event.isDefaultPrevented = function() {
3685 return event.defaultPrevented;
3688 var eventFns = events[type || event.type];
3689 var eventFnsLength = eventFns ? eventFns.length : 0;
3691 if (!eventFnsLength) return;
3693 if (isUndefined(event.immediatePropagationStopped)) {
3694 var originalStopImmediatePropagation = event.stopImmediatePropagation;
3695 event.stopImmediatePropagation = function() {
3696 event.immediatePropagationStopped = true;
3698 if (event.stopPropagation) {
3699 event.stopPropagation();
3702 if (originalStopImmediatePropagation) {
3703 originalStopImmediatePropagation.call(event);
3708 event.isImmediatePropagationStopped = function() {
3709 return event.immediatePropagationStopped === true;
3712 // Some events have special handlers that wrap the real handler
3713 var handlerWrapper = eventFns.specialHandlerWrapper || defaultHandlerWrapper;
3715 // Copy event handlers in case event handlers array is modified during execution.
3716 if ((eventFnsLength > 1)) {
3717 eventFns = shallowCopy(eventFns);
3720 for (var i = 0; i < eventFnsLength; i++) {
3721 if (!event.isImmediatePropagationStopped()) {
3722 handlerWrapper(element, event, eventFns[i]);
3727 // TODO: this is a hack for angularMocks/clearDataCache that makes it possible to deregister all
3728 // events on `element`
3729 eventHandler.elem = element;
3730 return eventHandler;
3733 function defaultHandlerWrapper(element, event, handler) {
3734 handler.call(element, event);
3737 function specialMouseHandlerWrapper(target, event, handler) {
3738 // Refer to jQuery's implementation of mouseenter & mouseleave
3739 // Read about mouseenter and mouseleave:
3740 // http://www.quirksmode.org/js/events_mouse.html#link8
3741 var related = event.relatedTarget;
3742 // For mousenter/leave call the handler if related is outside the target.
3743 // NB: No relatedTarget if the mouse left/entered the browser window
3744 if (!related || (related !== target && !jqLiteContains.call(target, related))) {
3745 handler.call(target, event);
3749 //////////////////////////////////////////
3750 // Functions iterating traversal.
3751 // These functions chain results into a single
3753 //////////////////////////////////////////
3755 removeData: jqLiteRemoveData,
3757 on: function jqLiteOn(element, type, fn, unsupported) {
3758 if (isDefined(unsupported)) throw jqLiteMinErr('onargs', 'jqLite#on() does not support the `selector` or `eventData` parameters');
3760 // Do not add event handlers to non-elements because they will not be cleaned up.
3761 if (!jqLiteAcceptsData(element)) {
3765 var expandoStore = jqLiteExpandoStore(element, true);
3766 var events = expandoStore.events;
3767 var handle = expandoStore.handle;
3770 handle = expandoStore.handle = createEventHandler(element, events);
3773 // http://jsperf.com/string-indexof-vs-split
3774 var types = type.indexOf(' ') >= 0 ? type.split(' ') : [type];
3775 var i = types.length;
3777 var addHandler = function(type, specialHandlerWrapper, noEventListener) {
3778 var eventFns = events[type];
3781 eventFns = events[type] = [];
3782 eventFns.specialHandlerWrapper = specialHandlerWrapper;
3783 if (type !== '$destroy' && !noEventListener) {
3784 element.addEventListener(type, handle);
3793 if (MOUSE_EVENT_MAP[type]) {
3794 addHandler(MOUSE_EVENT_MAP[type], specialMouseHandlerWrapper);
3795 addHandler(type, undefined, true);
3804 one: function(element, type, fn) {
3805 element = jqLite(element);
3807 //add the listener twice so that when it is called
3808 //you can remove the original function and still be
3809 //able to call element.off(ev, fn) normally
3810 element.on(type, function onFn() {
3811 element.off(type, fn);
3812 element.off(type, onFn);
3814 element.on(type, fn);
3817 replaceWith: function(element, replaceNode) {
3818 var index, parent = element.parentNode;
3819 jqLiteDealoc(element);
3820 forEach(new JQLite(replaceNode), function(node) {
3822 parent.insertBefore(node, index.nextSibling);
3824 parent.replaceChild(node, element);
3830 children: function(element) {
3832 forEach(element.childNodes, function(element) {
3833 if (element.nodeType === NODE_TYPE_ELEMENT) {
3834 children.push(element);
3840 contents: function(element) {
3841 return element.contentDocument || element.childNodes || [];
3844 append: function(element, node) {
3845 var nodeType = element.nodeType;
3846 if (nodeType !== NODE_TYPE_ELEMENT && nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT) return;
3848 node = new JQLite(node);
3850 for (var i = 0, ii = node.length; i < ii; i++) {
3851 var child = node[i];
3852 element.appendChild(child);
3856 prepend: function(element, node) {
3857 if (element.nodeType === NODE_TYPE_ELEMENT) {
3858 var index = element.firstChild;
3859 forEach(new JQLite(node), function(child) {
3860 element.insertBefore(child, index);
3865 wrap: function(element, wrapNode) {
3866 jqLiteWrapNode(element, jqLite(wrapNode).eq(0).clone()[0]);
3869 remove: jqLiteRemove,
3871 detach: function(element) {
3872 jqLiteRemove(element, true);
3875 after: function(element, newElement) {
3876 var index = element, parent = element.parentNode;
3879 newElement = new JQLite(newElement);
3881 for (var i = 0, ii = newElement.length; i < ii; i++) {
3882 var node = newElement[i];
3883 parent.insertBefore(node, index.nextSibling);
3889 addClass: jqLiteAddClass,
3890 removeClass: jqLiteRemoveClass,
3892 toggleClass: function(element, selector, condition) {
3894 forEach(selector.split(' '), function(className) {
3895 var classCondition = condition;
3896 if (isUndefined(classCondition)) {
3897 classCondition = !jqLiteHasClass(element, className);
3899 (classCondition ? jqLiteAddClass : jqLiteRemoveClass)(element, className);
3904 parent: function(element) {
3905 var parent = element.parentNode;
3906 return parent && parent.nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT ? parent : null;
3909 next: function(element) {
3910 return element.nextElementSibling;
3913 find: function(element, selector) {
3914 if (element.getElementsByTagName) {
3915 return element.getElementsByTagName(selector);
3923 triggerHandler: function(element, event, extraParameters) {
3925 var dummyEvent, eventFnsCopy, handlerArgs;
3926 var eventName = event.type || event;
3927 var expandoStore = jqLiteExpandoStore(element);
3928 var events = expandoStore && expandoStore.events;
3929 var eventFns = events && events[eventName];
3932 // Create a dummy event to pass to the handlers
3934 preventDefault: function() { this.defaultPrevented = true; },
3935 isDefaultPrevented: function() { return this.defaultPrevented === true; },
3936 stopImmediatePropagation: function() { this.immediatePropagationStopped = true; },
3937 isImmediatePropagationStopped: function() { return this.immediatePropagationStopped === true; },
3938 stopPropagation: noop,
3943 // If a custom event was provided then extend our dummy event with it
3945 dummyEvent = extend(dummyEvent, event);
3948 // Copy event handlers in case event handlers array is modified during execution.
3949 eventFnsCopy = shallowCopy(eventFns);
3950 handlerArgs = extraParameters ? [dummyEvent].concat(extraParameters) : [dummyEvent];
3952 forEach(eventFnsCopy, function(fn) {
3953 if (!dummyEvent.isImmediatePropagationStopped()) {
3954 fn.apply(element, handlerArgs);
3959 }, function(fn, name) {
3961 * chaining functions
3963 JQLite.prototype[name] = function(arg1, arg2, arg3) {
3966 for (var i = 0, ii = this.length; i < ii; i++) {
3967 if (isUndefined(value)) {
3968 value = fn(this[i], arg1, arg2, arg3);
3969 if (isDefined(value)) {
3970 // any function which returns a value needs to be wrapped
3971 value = jqLite(value);
3974 jqLiteAddNodes(value, fn(this[i], arg1, arg2, arg3));
3977 return isDefined(value) ? value : this;
3981 // bind legacy bind/unbind to on/off
3982 JQLite.prototype.bind = JQLite.prototype.on;
3983 JQLite.prototype.unbind = JQLite.prototype.off;
3986 // Provider for private $$jqLite service
3988 function $$jqLiteProvider() {
3989 this.$get = function $$jqLite() {
3990 return extend(JQLite, {
3991 hasClass: function(node, classes) {
3992 if (node.attr) node = node[0];
3993 return jqLiteHasClass(node, classes);
3995 addClass: function(node, classes) {
3996 if (node.attr) node = node[0];
3997 return jqLiteAddClass(node, classes);
3999 removeClass: function(node, classes) {
4000 if (node.attr) node = node[0];
4001 return jqLiteRemoveClass(node, classes);
4008 * Computes a hash of an 'obj'.
4011 * number is number as string
4012 * object is either result of calling $$hashKey function on the object or uniquely generated id,
4013 * that is also assigned to the $$hashKey property of the object.
4016 * @returns {string} hash string such that the same input will have the same hash string.
4017 * The resulting string key is in 'type:hashKey' format.
4019 function hashKey(obj, nextUidFn) {
4020 var key = obj && obj.$$hashKey;
4023 if (typeof key === 'function') {
4024 key = obj.$$hashKey();
4029 var objType = typeof obj;
4030 if (objType === 'function' || (objType === 'object' && obj !== null)) {
4031 key = obj.$$hashKey = objType + ':' + (nextUidFn || nextUid)();
4033 key = objType + ':' + obj;
4039 // A minimal ES2015 Map implementation.
4040 // Should be bug/feature equivalent to the native implementations of supported browsers
4041 // (for the features required in Angular).
4042 // See https://kangax.github.io/compat-table/es6/#test-Map
4043 var nanKey = Object.create(null);
4044 function NgMapShim() {
4047 this._lastKey = NaN;
4048 this._lastIndex = -1;
4050 NgMapShim.prototype = {
4051 _idx: function(key) {
4052 if (key === this._lastKey) {
4053 return this._lastIndex;
4055 this._lastKey = key;
4056 this._lastIndex = this._keys.indexOf(key);
4057 return this._lastIndex;
4059 _transformKey: function(key) {
4060 return isNumberNaN(key) ? nanKey : key;
4062 get: function(key) {
4063 key = this._transformKey(key);
4064 var idx = this._idx(key);
4066 return this._values[idx];
4069 set: function(key, value) {
4070 key = this._transformKey(key);
4071 var idx = this._idx(key);
4073 idx = this._lastIndex = this._keys.length;
4075 this._keys[idx] = key;
4076 this._values[idx] = value;
4079 // Do not `return this` to simulate the partial IE11 implementation
4081 delete: function(key) {
4082 key = this._transformKey(key);
4083 var idx = this._idx(key);
4087 this._keys.splice(idx, 1);
4088 this._values.splice(idx, 1);
4089 this._lastKey = NaN;
4090 this._lastIndex = -1;
4095 // For now, always use `NgMapShim`, even if `window.Map` is available. Some native implementations
4096 // are still buggy (often in subtle ways) and can cause hard-to-debug failures. When native `Map`
4097 // implementations get more stable, we can reconsider switching to `window.Map` (when available).
4098 var NgMap = NgMapShim;
4100 var $$MapProvider = [/** @this */function() {
4101 this.$get = [function() {
4109 * @name angular.injector
4113 * Creates an injector object that can be used for retrieving services as well as for
4114 * dependency injection (see {@link guide/di dependency injection}).
4116 * @param {Array.<string|Function>} modules A list of module functions or their aliases. See
4117 * {@link angular.module}. The `ng` module must be explicitly added.
4118 * @param {boolean=} [strictDi=false] Whether the injector should be in strict mode, which
4119 * disallows argument name annotation inference.
4120 * @returns {injector} Injector object. See {@link auto.$injector $injector}.
4125 * // create an injector
4126 * var $injector = angular.injector(['ng']);
4128 * // use the injector to kick off your application
4129 * // use the type inference to auto inject arguments, or use implicit injection
4130 * $injector.invoke(function($rootScope, $compile, $document) {
4131 * $compile($document)($rootScope);
4132 * $rootScope.$digest();
4136 * Sometimes you want to get access to the injector of a currently running Angular app
4137 * from outside Angular. Perhaps, you want to inject and compile some markup after the
4138 * application has been bootstrapped. You can do this using the extra `injector()` added
4139 * to JQuery/jqLite elements. See {@link angular.element}.
4141 * *This is fairly rare but could be the case if a third party library is injecting the
4144 * In the following example a new block of HTML containing a `ng-controller`
4145 * directive is added to the end of the document body by JQuery. We then compile and link
4146 * it into the current AngularJS scope.
4149 * var $div = $('<div ng-controller="MyCtrl">{{content.label}}</div>');
4150 * $(document.body).append($div);
4152 * angular.element(document).injector().invoke(function($compile) {
4153 * var scope = angular.element($div).scope();
4154 * $compile($div)(scope);
4166 * Implicit module which gets automatically added to each {@link auto.$injector $injector}.
4169 var ARROW_ARG = /^([^(]+?)=>/;
4170 var FN_ARGS = /^[^(]*\(\s*([^)]*)\)/m;
4171 var FN_ARG_SPLIT = /,/;
4172 var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/;
4173 var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
4174 var $injectorMinErr = minErr('$injector');
4176 function stringifyFn(fn) {
4177 return Function.prototype.toString.call(fn);
4180 function extractArgs(fn) {
4181 var fnText = stringifyFn(fn).replace(STRIP_COMMENTS, ''),
4182 args = fnText.match(ARROW_ARG) || fnText.match(FN_ARGS);
4186 function anonFn(fn) {
4187 // For anonymous functions, showing at the very least the function signature can help in
4189 var args = extractArgs(fn);
4191 return 'function(' + (args[1] || '').replace(/[\s\r\n]+/, ' ') + ')';
4196 function annotate(fn, strictDi, name) {
4201 if (typeof fn === 'function') {
4202 if (!($inject = fn.$inject)) {
4206 if (!isString(name) || !name) {
4207 name = fn.name || anonFn(fn);
4209 throw $injectorMinErr('strictdi',
4210 '{0} is not using explicit annotation and cannot be invoked in strict mode', name);
4212 argDecl = extractArgs(fn);
4213 forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg) {
4214 arg.replace(FN_ARG, function(all, underscore, name) {
4219 fn.$inject = $inject;
4221 } else if (isArray(fn)) {
4222 last = fn.length - 1;
4223 assertArgFn(fn[last], 'fn');
4224 $inject = fn.slice(0, last);
4226 assertArgFn(fn, 'fn', true);
4231 ///////////////////////////////////////
4239 * `$injector` is used to retrieve object instances as defined by
4240 * {@link auto.$provide provider}, instantiate types, invoke methods,
4243 * The following always holds true:
4246 * var $injector = angular.injector();
4247 * expect($injector.get('$injector')).toBe($injector);
4248 * expect($injector.invoke(function($injector) {
4250 * })).toBe($injector);
4253 * # Injection Function Annotation
4255 * JavaScript does not have annotations, and annotations are needed for dependency injection. The
4256 * following are all valid ways of annotating function with injection arguments and are equivalent.
4259 * // inferred (only works if code not minified/obfuscated)
4260 * $injector.invoke(function(serviceA){});
4263 * function explicit(serviceA) {};
4264 * explicit.$inject = ['serviceA'];
4265 * $injector.invoke(explicit);
4268 * $injector.invoke(['serviceA', function(serviceA){}]);
4273 * In JavaScript calling `toString()` on a function returns the function definition. The definition
4274 * can then be parsed and the function arguments can be extracted. This method of discovering
4275 * annotations is disallowed when the injector is in strict mode.
4276 * *NOTE:* This does not work with minification, and obfuscation tools since these tools change the
4279 * ## `$inject` Annotation
4280 * By adding an `$inject` property onto a function the injection parameters can be specified.
4283 * As an array of injection names, where the last item in the array is the function to call.
4288 * @name $injector#modules
4291 * A hash containing all the modules that have been loaded into the
4294 * You can use this property to find out information about a module via the
4295 * {@link angular.Module#info `myModule.info(...)`} method.
4300 * var info = $injector.modules['ngAnimate'].info();
4303 * **Do not use this property to attempt to modify the modules after the application
4304 * has been bootstrapped.**
4310 * @name $injector#get
4313 * Return an instance of the service.
4315 * @param {string} name The name of the instance to retrieve.
4316 * @param {string=} caller An optional string to provide the origin of the function call for error messages.
4317 * @return {*} The instance.
4322 * @name $injector#invoke
4325 * Invoke the method and supply the method arguments from the `$injector`.
4327 * @param {Function|Array.<string|Function>} fn The injectable function to invoke. Function parameters are
4328 * injected according to the {@link guide/di $inject Annotation} rules.
4329 * @param {Object=} self The `this` for the invoked method.
4330 * @param {Object=} locals Optional object. If preset then any argument names are read from this
4331 * object first, before the `$injector` is consulted.
4332 * @returns {*} the value returned by the invoked `fn` function.
4337 * @name $injector#has
4340 * Allows the user to query if the particular service exists.
4342 * @param {string} name Name of the service to query.
4343 * @returns {boolean} `true` if injector has given service.
4348 * @name $injector#instantiate
4350 * Create a new instance of JS type. The method takes a constructor function, invokes the new
4351 * operator, and supplies all of the arguments to the constructor function as specified by the
4352 * constructor annotation.
4354 * @param {Function} Type Annotated constructor function.
4355 * @param {Object=} locals Optional object. If preset then any argument names are read from this
4356 * object first, before the `$injector` is consulted.
4357 * @returns {Object} new instance of `Type`.
4362 * @name $injector#annotate
4365 * Returns an array of service names which the function is requesting for injection. This API is
4366 * used by the injector to determine which services need to be injected into the function when the
4367 * function is invoked. There are three ways in which the function can be annotated with the needed
4372 * The simplest form is to extract the dependencies from the arguments of the function. This is done
4373 * by converting the function into a string using `toString()` method and extracting the argument
4377 * function MyController($scope, $route) {
4382 * expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
4385 * You can disallow this method by using strict injection mode.
4387 * This method does not work with code minification / obfuscation. For this reason the following
4388 * annotation strategies are supported.
4390 * # The `$inject` property
4392 * If a function has an `$inject` property and its value is an array of strings, then the strings
4393 * represent names of services to be injected into the function.
4396 * var MyController = function(obfuscatedScope, obfuscatedRoute) {
4399 * // Define function dependencies
4400 * MyController['$inject'] = ['$scope', '$route'];
4403 * expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
4406 * # The array notation
4408 * It is often desirable to inline Injected functions and that's when setting the `$inject` property
4409 * is very inconvenient. In these situations using the array notation to specify the dependencies in
4410 * a way that survives minification is a better choice:
4413 * // We wish to write this (not minification / obfuscation safe)
4414 * injector.invoke(function($compile, $rootScope) {
4418 * // We are forced to write break inlining
4419 * var tmpFn = function(obfuscatedCompile, obfuscatedRootScope) {
4422 * tmpFn.$inject = ['$compile', '$rootScope'];
4423 * injector.invoke(tmpFn);
4425 * // To better support inline function the inline annotation is supported
4426 * injector.invoke(['$compile', '$rootScope', function(obfCompile, obfRootScope) {
4431 * expect(injector.annotate(
4432 * ['$compile', '$rootScope', function(obfus_$compile, obfus_$rootScope) {}])
4433 * ).toEqual(['$compile', '$rootScope']);
4436 * @param {Function|Array.<string|Function>} fn Function for which dependent service names need to
4437 * be retrieved as described above.
4439 * @param {boolean=} [strictDi=false] Disallow argument name annotation inference.
4441 * @returns {Array.<string>} The names of the services which the function requires.
4452 * The {@link auto.$provide $provide} service has a number of methods for registering components
4453 * with the {@link auto.$injector $injector}. Many of these functions are also exposed on
4454 * {@link angular.Module}.
4456 * An Angular **service** is a singleton object created by a **service factory**. These **service
4457 * factories** are functions which, in turn, are created by a **service provider**.
4458 * The **service providers** are constructor functions. When instantiated they must contain a
4459 * property called `$get`, which holds the **service factory** function.
4461 * When you request a service, the {@link auto.$injector $injector} is responsible for finding the
4462 * correct **service provider**, instantiating it and then calling its `$get` **service factory**
4463 * function to get the instance of the **service**.
4465 * Often services have no configuration options and there is no need to add methods to the service
4466 * provider. The provider will be no more than a constructor function with a `$get` property. For
4467 * these cases the {@link auto.$provide $provide} service has additional helper methods to register
4468 * services without specifying a provider.
4470 * * {@link auto.$provide#provider provider(name, provider)} - registers a **service provider** with the
4471 * {@link auto.$injector $injector}
4472 * * {@link auto.$provide#constant constant(name, obj)} - registers a value/object that can be accessed by
4473 * providers and services.
4474 * * {@link auto.$provide#value value(name, obj)} - registers a value/object that can only be accessed by
4475 * services, not providers.
4476 * * {@link auto.$provide#factory factory(name, fn)} - registers a service **factory function**
4477 * that will be wrapped in a **service provider** object, whose `$get` property will contain the
4478 * given factory function.
4479 * * {@link auto.$provide#service service(name, Fn)} - registers a **constructor function**
4480 * that will be wrapped in a **service provider** object, whose `$get` property will instantiate
4481 * a new object using the given constructor function.
4482 * * {@link auto.$provide#decorator decorator(name, decorFn)} - registers a **decorator function** that
4483 * will be able to modify or replace the implementation of another service.
4485 * See the individual methods for more information and examples.
4490 * @name $provide#provider
4493 * Register a **provider function** with the {@link auto.$injector $injector}. Provider functions
4494 * are constructor functions, whose instances are responsible for "providing" a factory for a
4497 * Service provider names start with the name of the service they provide followed by `Provider`.
4498 * For example, the {@link ng.$log $log} service has a provider called
4499 * {@link ng.$logProvider $logProvider}.
4501 * Service provider objects can have additional methods which allow configuration of the provider
4502 * and its service. Importantly, you can configure what kind of service is created by the `$get`
4503 * method, or how that service will act. For example, the {@link ng.$logProvider $logProvider} has a
4504 * method {@link ng.$logProvider#debugEnabled debugEnabled}
4505 * which lets you specify whether the {@link ng.$log $log} service will log debug messages to the
4508 * @param {string} name The name of the instance. NOTE: the provider will be available under `name +
4510 * @param {(Object|function())} provider If the provider is:
4512 * - `Object`: then it should have a `$get` method. The `$get` method will be invoked using
4513 * {@link auto.$injector#invoke $injector.invoke()} when an instance needs to be created.
4514 * - `Constructor`: a new instance of the provider will be created using
4515 * {@link auto.$injector#instantiate $injector.instantiate()}, then treated as `object`.
4517 * @returns {Object} registered provider instance
4521 * The following example shows how to create a simple event tracking service and register it using
4522 * {@link auto.$provide#provider $provide.provider()}.
4525 * // Define the eventTracker provider
4526 * function EventTrackerProvider() {
4527 * var trackingUrl = '/track';
4529 * // A provider method for configuring where the tracked events should been saved
4530 * this.setTrackingUrl = function(url) {
4531 * trackingUrl = url;
4534 * // The service factory function
4535 * this.$get = ['$http', function($http) {
4536 * var trackedEvents = {};
4538 * // Call this to track an event
4539 * event: function(event) {
4540 * var count = trackedEvents[event] || 0;
4542 * trackedEvents[event] = count;
4545 * // Call this to save the tracked events to the trackingUrl
4546 * save: function() {
4547 * $http.post(trackingUrl, trackedEvents);
4553 * describe('eventTracker', function() {
4556 * beforeEach(module(function($provide) {
4557 * // Register the eventTracker provider
4558 * $provide.provider('eventTracker', EventTrackerProvider);
4561 * beforeEach(module(function(eventTrackerProvider) {
4562 * // Configure eventTracker provider
4563 * eventTrackerProvider.setTrackingUrl('/custom-track');
4566 * it('tracks events', inject(function(eventTracker) {
4567 * expect(eventTracker.event('login')).toEqual(1);
4568 * expect(eventTracker.event('login')).toEqual(2);
4571 * it('saves to the tracking url', inject(function(eventTracker, $http) {
4572 * postSpy = spyOn($http, 'post');
4573 * eventTracker.event('login');
4574 * eventTracker.save();
4575 * expect(postSpy).toHaveBeenCalled();
4576 * expect(postSpy.mostRecentCall.args[0]).not.toEqual('/track');
4577 * expect(postSpy.mostRecentCall.args[0]).toEqual('/custom-track');
4578 * expect(postSpy.mostRecentCall.args[1]).toEqual({ 'login': 1 });
4586 * @name $provide#factory
4589 * Register a **service factory**, which will be called to return the service instance.
4590 * This is short for registering a service where its provider consists of only a `$get` property,
4591 * which is the given service factory function.
4592 * You should use {@link auto.$provide#factory $provide.factory(getFn)} if you do not need to
4593 * configure your service in a provider.
4595 * @param {string} name The name of the instance.
4596 * @param {Function|Array.<string|Function>} $getFn The injectable $getFn for the instance creation.
4597 * Internally this is a short hand for `$provide.provider(name, {$get: $getFn})`.
4598 * @returns {Object} registered provider instance
4601 * Here is an example of registering a service
4603 * $provide.factory('ping', ['$http', function($http) {
4604 * return function ping() {
4605 * return $http.send('/ping');
4609 * You would then inject and use this service like this:
4611 * someModule.controller('Ctrl', ['ping', function(ping) {
4620 * @name $provide#service
4623 * Register a **service constructor**, which will be invoked with `new` to create the service
4625 * This is short for registering a service where its provider's `$get` property is a factory
4626 * function that returns an instance instantiated by the injector from the service constructor
4629 * Internally it looks a bit like this:
4633 * $get: function() {
4634 * return $injector.instantiate(constructor);
4640 * You should use {@link auto.$provide#service $provide.service(class)} if you define your service
4643 * @param {string} name The name of the instance.
4644 * @param {Function|Array.<string|Function>} constructor An injectable class (constructor function)
4645 * that will be instantiated.
4646 * @returns {Object} registered provider instance
4649 * Here is an example of registering a service using
4650 * {@link auto.$provide#service $provide.service(class)}.
4652 * var Ping = function($http) {
4653 * this.$http = $http;
4656 * Ping.$inject = ['$http'];
4658 * Ping.prototype.send = function() {
4659 * return this.$http.get('/ping');
4661 * $provide.service('ping', Ping);
4663 * You would then inject and use this service like this:
4665 * someModule.controller('Ctrl', ['ping', function(ping) {
4674 * @name $provide#value
4677 * Register a **value service** with the {@link auto.$injector $injector}, such as a string, a
4678 * number, an array, an object or a function. This is short for registering a service where its
4679 * provider's `$get` property is a factory function that takes no arguments and returns the **value
4680 * service**. That also means it is not possible to inject other services into a value service.
4682 * Value services are similar to constant services, except that they cannot be injected into a
4683 * module configuration function (see {@link angular.Module#config}) but they can be overridden by
4684 * an Angular {@link auto.$provide#decorator decorator}.
4686 * @param {string} name The name of the instance.
4687 * @param {*} value The value.
4688 * @returns {Object} registered provider instance
4691 * Here are some examples of creating value services.
4693 * $provide.value('ADMIN_USER', 'admin');
4695 * $provide.value('RoleLookup', { admin: 0, writer: 1, reader: 2 });
4697 * $provide.value('halfOf', function(value) {
4706 * @name $provide#constant
4709 * Register a **constant service** with the {@link auto.$injector $injector}, such as a string,
4710 * a number, an array, an object or a function. Like the {@link auto.$provide#value value}, it is not
4711 * possible to inject other services into a constant.
4713 * But unlike {@link auto.$provide#value value}, a constant can be
4714 * injected into a module configuration function (see {@link angular.Module#config}) and it cannot
4715 * be overridden by an Angular {@link auto.$provide#decorator decorator}.
4717 * @param {string} name The name of the constant.
4718 * @param {*} value The constant value.
4719 * @returns {Object} registered instance
4722 * Here a some examples of creating constants:
4724 * $provide.constant('SHARD_HEIGHT', 306);
4726 * $provide.constant('MY_COLOURS', ['red', 'blue', 'grey']);
4728 * $provide.constant('double', function(value) {
4737 * @name $provide#decorator
4740 * Register a **decorator function** with the {@link auto.$injector $injector}. A decorator function
4741 * intercepts the creation of a service, allowing it to override or modify the behavior of the
4742 * service. The return value of the decorator function may be the original service, or a new service
4743 * that replaces (or wraps and delegates to) the original service.
4745 * You can find out more about using decorators in the {@link guide/decorators} guide.
4747 * @param {string} name The name of the service to decorate.
4748 * @param {Function|Array.<string|Function>} decorator This function will be invoked when the service needs to be
4749 * provided and should return the decorated service instance. The function is called using
4750 * the {@link auto.$injector#invoke injector.invoke} method and is therefore fully injectable.
4751 * Local injection arguments:
4753 * * `$delegate` - The original service instance, which can be replaced, monkey patched, configured,
4754 * decorated or delegated to.
4757 * Here we decorate the {@link ng.$log $log} service to convert warnings to errors by intercepting
4758 * calls to {@link ng.$log#error $log.warn()}.
4760 * $provide.decorator('$log', ['$delegate', function($delegate) {
4761 * $delegate.warn = $delegate.error;
4768 function createInjector(modulesToLoad, strictDi) {
4769 strictDi = (strictDi === true);
4770 var INSTANTIATING = {},
4771 providerSuffix = 'Provider',
4773 loadedModules = new NgMap(),
4776 provider: supportObject(provider),
4777 factory: supportObject(factory),
4778 service: supportObject(service),
4779 value: supportObject(value),
4780 constant: supportObject(constant),
4781 decorator: decorator
4784 providerInjector = (providerCache.$injector =
4785 createInternalInjector(providerCache, function(serviceName, caller) {
4786 if (angular.isString(caller)) {
4789 throw $injectorMinErr('unpr', 'Unknown provider: {0}', path.join(' <- '));
4792 protoInstanceInjector =
4793 createInternalInjector(instanceCache, function(serviceName, caller) {
4794 var provider = providerInjector.get(serviceName + providerSuffix, caller);
4795 return instanceInjector.invoke(
4796 provider.$get, provider, undefined, serviceName);
4798 instanceInjector = protoInstanceInjector;
4800 providerCache['$injector' + providerSuffix] = { $get: valueFn(protoInstanceInjector) };
4801 instanceInjector.modules = providerInjector.modules = createMap();
4802 var runBlocks = loadModules(modulesToLoad);
4803 instanceInjector = protoInstanceInjector.get('$injector');
4804 instanceInjector.strictDi = strictDi;
4805 forEach(runBlocks, function(fn) { if (fn) instanceInjector.invoke(fn); });
4807 return instanceInjector;
4809 ////////////////////////////////////
4811 ////////////////////////////////////
4813 function supportObject(delegate) {
4814 return function(key, value) {
4815 if (isObject(key)) {
4816 forEach(key, reverseParams(delegate));
4818 return delegate(key, value);
4823 function provider(name, provider_) {
4824 assertNotHasOwnProperty(name, 'service');
4825 if (isFunction(provider_) || isArray(provider_)) {
4826 provider_ = providerInjector.instantiate(provider_);
4828 if (!provider_.$get) {
4829 throw $injectorMinErr('pget', 'Provider \'{0}\' must define $get factory method.', name);
4831 return (providerCache[name + providerSuffix] = provider_);
4834 function enforceReturnValue(name, factory) {
4835 return /** @this */ function enforcedReturnValue() {
4836 var result = instanceInjector.invoke(factory, this);
4837 if (isUndefined(result)) {
4838 throw $injectorMinErr('undef', 'Provider \'{0}\' must return a value from $get factory method.', name);
4844 function factory(name, factoryFn, enforce) {
4845 return provider(name, {
4846 $get: enforce !== false ? enforceReturnValue(name, factoryFn) : factoryFn
4850 function service(name, constructor) {
4851 return factory(name, ['$injector', function($injector) {
4852 return $injector.instantiate(constructor);
4856 function value(name, val) { return factory(name, valueFn(val), false); }
4858 function constant(name, value) {
4859 assertNotHasOwnProperty(name, 'constant');
4860 providerCache[name] = value;
4861 instanceCache[name] = value;
4864 function decorator(serviceName, decorFn) {
4865 var origProvider = providerInjector.get(serviceName + providerSuffix),
4866 orig$get = origProvider.$get;
4868 origProvider.$get = function() {
4869 var origInstance = instanceInjector.invoke(orig$get, origProvider);
4870 return instanceInjector.invoke(decorFn, null, {$delegate: origInstance});
4874 ////////////////////////////////////
4876 ////////////////////////////////////
4877 function loadModules(modulesToLoad) {
4878 assertArg(isUndefined(modulesToLoad) || isArray(modulesToLoad), 'modulesToLoad', 'not an array');
4879 var runBlocks = [], moduleFn;
4880 forEach(modulesToLoad, function(module) {
4881 if (loadedModules.get(module)) return;
4882 loadedModules.set(module, true);
4884 function runInvokeQueue(queue) {
4886 for (i = 0, ii = queue.length; i < ii; i++) {
4887 var invokeArgs = queue[i],
4888 provider = providerInjector.get(invokeArgs[0]);
4890 provider[invokeArgs[1]].apply(provider, invokeArgs[2]);
4895 if (isString(module)) {
4896 moduleFn = angularModule(module);
4897 instanceInjector.modules[module] = moduleFn;
4898 runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks);
4899 runInvokeQueue(moduleFn._invokeQueue);
4900 runInvokeQueue(moduleFn._configBlocks);
4901 } else if (isFunction(module)) {
4902 runBlocks.push(providerInjector.invoke(module));
4903 } else if (isArray(module)) {
4904 runBlocks.push(providerInjector.invoke(module));
4906 assertArgFn(module, 'module');
4909 if (isArray(module)) {
4910 module = module[module.length - 1];
4912 if (e.message && e.stack && e.stack.indexOf(e.message) === -1) {
4913 // Safari & FF's stack traces don't contain error.message content
4914 // unlike those of Chrome and IE
4915 // So if stack doesn't contain message, we create a new string that contains both.
4916 // Since error.stack is read-only in Safari, I'm overriding e and not e.stack here.
4917 // eslint-disable-next-line no-ex-assign
4918 e = e.message + '\n' + e.stack;
4920 throw $injectorMinErr('modulerr', 'Failed to instantiate module {0} due to:\n{1}',
4921 module, e.stack || e.message || e);
4927 ////////////////////////////////////
4928 // internal Injector
4929 ////////////////////////////////////
4931 function createInternalInjector(cache, factory) {
4933 function getService(serviceName, caller) {
4934 if (cache.hasOwnProperty(serviceName)) {
4935 if (cache[serviceName] === INSTANTIATING) {
4936 throw $injectorMinErr('cdep', 'Circular dependency found: {0}',
4937 serviceName + ' <- ' + path.join(' <- '));
4939 return cache[serviceName];
4942 path.unshift(serviceName);
4943 cache[serviceName] = INSTANTIATING;
4944 cache[serviceName] = factory(serviceName, caller);
4945 return cache[serviceName];
4947 if (cache[serviceName] === INSTANTIATING) {
4948 delete cache[serviceName];
4958 function injectionArgs(fn, locals, serviceName) {
4960 $inject = createInjector.$$annotate(fn, strictDi, serviceName);
4962 for (var i = 0, length = $inject.length; i < length; i++) {
4963 var key = $inject[i];
4964 if (typeof key !== 'string') {
4965 throw $injectorMinErr('itkn',
4966 'Incorrect injection token! Expected service name as string, got {0}', key);
4968 args.push(locals && locals.hasOwnProperty(key) ? locals[key] :
4969 getService(key, serviceName));
4974 function isClass(func) {
4975 // Support: IE 9-11 only
4976 // IE 9-11 do not support classes and IE9 leaks with the code below.
4977 if (msie || typeof func !== 'function') {
4980 var result = func.$$ngIsClass;
4981 if (!isBoolean(result)) {
4982 // Support: Edge 12-13 only
4983 // See: https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/6156135/
4984 result = func.$$ngIsClass = /^(?:class\b|constructor\()/.test(stringifyFn(func));
4989 function invoke(fn, self, locals, serviceName) {
4990 if (typeof locals === 'string') {
4991 serviceName = locals;
4995 var args = injectionArgs(fn, locals, serviceName);
4997 fn = fn[fn.length - 1];
5001 // http://jsperf.com/angularjs-invoke-apply-vs-switch
5003 return fn.apply(self, args);
5006 return new (Function.prototype.bind.apply(fn, args))();
5011 function instantiate(Type, locals, serviceName) {
5012 // Check if Type is annotated and use just the given function at n-1 as parameter
5013 // e.g. someModule.factory('greeter', ['$window', function(renamed$window) {}]);
5014 var ctor = (isArray(Type) ? Type[Type.length - 1] : Type);
5015 var args = injectionArgs(Type, locals, serviceName);
5016 // Empty object at position 0 is ignored for invocation with `new`, but required.
5018 return new (Function.prototype.bind.apply(ctor, args))();
5024 instantiate: instantiate,
5026 annotate: createInjector.$$annotate,
5027 has: function(name) {
5028 return providerCache.hasOwnProperty(name + providerSuffix) || cache.hasOwnProperty(name);
5034 createInjector.$$annotate = annotate;
5038 * @name $anchorScrollProvider
5042 * Use `$anchorScrollProvider` to disable automatic scrolling whenever
5043 * {@link ng.$location#hash $location.hash()} changes.
5045 function $AnchorScrollProvider() {
5047 var autoScrollingEnabled = true;
5051 * @name $anchorScrollProvider#disableAutoScrolling
5054 * By default, {@link ng.$anchorScroll $anchorScroll()} will automatically detect changes to
5055 * {@link ng.$location#hash $location.hash()} and scroll to the element matching the new hash.<br />
5056 * Use this method to disable automatic scrolling.
5058 * If automatic scrolling is disabled, one must explicitly call
5059 * {@link ng.$anchorScroll $anchorScroll()} in order to scroll to the element related to the
5062 this.disableAutoScrolling = function() {
5063 autoScrollingEnabled = false;
5068 * @name $anchorScroll
5071 * @requires $location
5072 * @requires $rootScope
5075 * When called, it scrolls to the element related to the specified `hash` or (if omitted) to the
5076 * current value of {@link ng.$location#hash $location.hash()}, according to the rules specified
5078 * [HTML5 spec](http://www.w3.org/html/wg/drafts/html/master/browsers.html#an-indicated-part-of-the-document).
5080 * It also watches the {@link ng.$location#hash $location.hash()} and automatically scrolls to
5081 * match any anchor whenever it changes. This can be disabled by calling
5082 * {@link ng.$anchorScrollProvider#disableAutoScrolling $anchorScrollProvider.disableAutoScrolling()}.
5084 * Additionally, you can use its {@link ng.$anchorScroll#yOffset yOffset} property to specify a
5085 * vertical scroll-offset (either fixed or dynamic).
5087 * @param {string=} hash The hash specifying the element to scroll to. If omitted, the value of
5088 * {@link ng.$location#hash $location.hash()} will be used.
5090 * @property {(number|function|jqLite)} yOffset
5091 * If set, specifies a vertical scroll-offset. This is often useful when there are fixed
5092 * positioned elements at the top of the page, such as navbars, headers etc.
5094 * `yOffset` can be specified in various ways:
5095 * - **number**: A fixed number of pixels to be used as offset.<br /><br />
5096 * - **function**: A getter function called everytime `$anchorScroll()` is executed. Must return
5097 * a number representing the offset (in pixels).<br /><br />
5098 * - **jqLite**: A jqLite/jQuery element to be used for specifying the offset. The distance from
5099 * the top of the page to the element's bottom will be used as offset.<br />
5100 * **Note**: The element will be taken into account only as long as its `position` is set to
5101 * `fixed`. This option is useful, when dealing with responsive navbars/headers that adjust
5102 * their height and/or positioning according to the viewport's size.
5105 * <div class="alert alert-warning">
5106 * In order for `yOffset` to work properly, scrolling should take place on the document's root and
5107 * not some child element.
5111 <example module="anchorScrollExample" name="anchor-scroll">
5112 <file name="index.html">
5113 <div id="scrollArea" ng-controller="ScrollController">
5114 <a ng-click="gotoBottom()">Go to bottom</a>
5115 <a id="bottom"></a> You're at the bottom!
5118 <file name="script.js">
5119 angular.module('anchorScrollExample', [])
5120 .controller('ScrollController', ['$scope', '$location', '$anchorScroll',
5121 function($scope, $location, $anchorScroll) {
5122 $scope.gotoBottom = function() {
5123 // set the location.hash to the id of
5124 // the element you wish to scroll to.
5125 $location.hash('bottom');
5127 // call $anchorScroll()
5132 <file name="style.css">
5146 * The example below illustrates the use of a vertical scroll-offset (specified as a fixed value).
5147 * See {@link ng.$anchorScroll#yOffset $anchorScroll.yOffset} for more details.
5150 <example module="anchorScrollOffsetExample" name="anchor-scroll-offset">
5151 <file name="index.html">
5152 <div class="fixed-header" ng-controller="headerCtrl">
5153 <a href="" ng-click="gotoAnchor(x)" ng-repeat="x in [1,2,3,4,5]">
5157 <div id="anchor{{x}}" class="anchor" ng-repeat="x in [1,2,3,4,5]">
5161 <file name="script.js">
5162 angular.module('anchorScrollOffsetExample', [])
5163 .run(['$anchorScroll', function($anchorScroll) {
5164 $anchorScroll.yOffset = 50; // always scroll by 50 extra pixels
5166 .controller('headerCtrl', ['$anchorScroll', '$location', '$scope',
5167 function($anchorScroll, $location, $scope) {
5168 $scope.gotoAnchor = function(x) {
5169 var newHash = 'anchor' + x;
5170 if ($location.hash() !== newHash) {
5171 // set the $location.hash to `newHash` and
5172 // $anchorScroll will automatically scroll to it
5173 $location.hash('anchor' + x);
5175 // call $anchorScroll() explicitly,
5176 // since $location.hash hasn't changed
5183 <file name="style.css">
5189 border: 2px dashed DarkOrchid;
5190 padding: 10px 10px 200px 10px;
5194 background-color: rgba(0, 0, 0, 0.2);
5197 top: 0; left: 0; right: 0;
5201 display: inline-block;
5207 this.$get = ['$window', '$location', '$rootScope', function($window, $location, $rootScope) {
5208 var document = $window.document;
5210 // Helper function to get first anchor from a NodeList
5211 // (using `Array#some()` instead of `angular#forEach()` since it's more performant
5212 // and working in all supported browsers.)
5213 function getFirstAnchor(list) {
5215 Array.prototype.some.call(list, function(element) {
5216 if (nodeName_(element) === 'a') {
5224 function getYOffset() {
5226 var offset = scroll.yOffset;
5228 if (isFunction(offset)) {
5230 } else if (isElement(offset)) {
5231 var elem = offset[0];
5232 var style = $window.getComputedStyle(elem);
5233 if (style.position !== 'fixed') {
5236 offset = elem.getBoundingClientRect().bottom;
5238 } else if (!isNumber(offset)) {
5245 function scrollTo(elem) {
5247 elem.scrollIntoView();
5249 var offset = getYOffset();
5252 // `offset` is the number of pixels we should scroll UP in order to align `elem` properly.
5253 // This is true ONLY if the call to `elem.scrollIntoView()` initially aligns `elem` at the
5254 // top of the viewport.
5256 // IF the number of pixels from the top of `elem` to the end of the page's content is less
5257 // than the height of the viewport, then `elem.scrollIntoView()` will align the `elem` some
5258 // way down the page.
5260 // This is often the case for elements near the bottom of the page.
5262 // In such cases we do not need to scroll the whole `offset` up, just the difference between
5263 // the top of the element and the offset, which is enough to align the top of `elem` at the
5264 // desired position.
5265 var elemTop = elem.getBoundingClientRect().top;
5266 $window.scrollBy(0, elemTop - offset);
5269 $window.scrollTo(0, 0);
5273 function scroll(hash) {
5274 // Allow numeric hashes
5275 hash = isString(hash) ? hash : isNumber(hash) ? hash.toString() : $location.hash();
5278 // empty hash, scroll to the top of the page
5279 if (!hash) scrollTo(null);
5281 // element with given id
5282 else if ((elm = document.getElementById(hash))) scrollTo(elm);
5284 // first anchor with given name :-D
5285 else if ((elm = getFirstAnchor(document.getElementsByName(hash)))) scrollTo(elm);
5287 // no element and hash === 'top', scroll to the top of the page
5288 else if (hash === 'top') scrollTo(null);
5291 // does not scroll when user clicks on anchor link that is currently on
5292 // (no url change, no $location.hash() change), browser native does scroll
5293 if (autoScrollingEnabled) {
5294 $rootScope.$watch(function autoScrollWatch() {return $location.hash();},
5295 function autoScrollWatchAction(newVal, oldVal) {
5296 // skip the initial scroll if $location.hash is empty
5297 if (newVal === oldVal && newVal === '') return;
5299 jqLiteDocumentLoaded(function() {
5300 $rootScope.$evalAsync(scroll);
5309 var $animateMinErr = minErr('$animate');
5310 var ELEMENT_NODE = 1;
5311 var NG_ANIMATE_CLASSNAME = 'ng-animate';
5313 function mergeClasses(a,b) {
5314 if (!a && !b) return '';
5317 if (isArray(a)) a = a.join(' ');
5318 if (isArray(b)) b = b.join(' ');
5322 function extractElementNode(element) {
5323 for (var i = 0; i < element.length; i++) {
5324 var elm = element[i];
5325 if (elm.nodeType === ELEMENT_NODE) {
5331 function splitClasses(classes) {
5332 if (isString(classes)) {
5333 classes = classes.split(' ');
5336 // Use createMap() to prevent class assumptions involving property names in
5338 var obj = createMap();
5339 forEach(classes, function(klass) {
5340 // sometimes the split leaves empty string values
5341 // incase extra spaces were applied to the options
5349 // if any other type of options value besides an Object value is
5350 // passed into the $animate.method() animation then this helper code
5351 // will be run which will ignore it. While this patch is not the
5352 // greatest solution to this, a lot of existing plugins depend on
5353 // $animate to either call the callback (< 1.2) or return a promise
5354 // that can be changed. This helper function ensures that the options
5355 // are wiped clean incase a callback function is provided.
5356 function prepareAnimateOptions(options) {
5357 return isObject(options)
5362 var $$CoreAnimateJsProvider = /** @this */ function() {
5366 // this is prefixed with Core since it conflicts with
5367 // the animateQueueProvider defined in ngAnimate/animateQueue.js
5368 var $$CoreAnimateQueueProvider = /** @this */ function() {
5369 var postDigestQueue = new NgMap();
5370 var postDigestElements = [];
5372 this.$get = ['$$AnimateRunner', '$rootScope',
5373 function($$AnimateRunner, $rootScope) {
5380 push: function(element, event, options, domOperation) {
5385 options = options || {};
5387 element.css(options.from);
5390 element.css(options.to);
5393 if (options.addClass || options.removeClass) {
5394 addRemoveClassesPostDigest(element, options.addClass, options.removeClass);
5397 var runner = new $$AnimateRunner();
5399 // since there are no animations to run the runner needs to be
5400 // notified that the animation call is complete.
5407 function updateData(data, classes, value) {
5408 var changed = false;
5410 classes = isString(classes) ? classes.split(' ') :
5411 isArray(classes) ? classes : [];
5412 forEach(classes, function(className) {
5415 data[className] = value;
5422 function handleCSSClassChanges() {
5423 forEach(postDigestElements, function(element) {
5424 var data = postDigestQueue.get(element);
5426 var existing = splitClasses(element.attr('class'));
5429 forEach(data, function(status, className) {
5430 var hasClass = !!existing[className];
5431 if (status !== hasClass) {
5433 toAdd += (toAdd.length ? ' ' : '') + className;
5435 toRemove += (toRemove.length ? ' ' : '') + className;
5440 forEach(element, function(elm) {
5442 jqLiteAddClass(elm, toAdd);
5445 jqLiteRemoveClass(elm, toRemove);
5448 postDigestQueue.delete(element);
5451 postDigestElements.length = 0;
5455 function addRemoveClassesPostDigest(element, add, remove) {
5456 var data = postDigestQueue.get(element) || {};
5458 var classesAdded = updateData(data, add, true);
5459 var classesRemoved = updateData(data, remove, false);
5461 if (classesAdded || classesRemoved) {
5463 postDigestQueue.set(element, data);
5464 postDigestElements.push(element);
5466 if (postDigestElements.length === 1) {
5467 $rootScope.$$postDigest(handleCSSClassChanges);
5476 * @name $animateProvider
5479 * Default implementation of $animate that doesn't perform any animations, instead just
5480 * synchronously performs DOM updates and resolves the returned runner promise.
5482 * In order to enable animations the `ngAnimate` module has to be loaded.
5484 * To see the functional implementation check out `src/ngAnimate/animate.js`.
5486 var $AnimateProvider = ['$provide', /** @this */ function($provide) {
5487 var provider = this;
5488 var classNameFilter = null;
5490 this.$$registeredAnimations = Object.create(null);
5494 * @name $animateProvider#register
5497 * Registers a new injectable animation factory function. The factory function produces the
5498 * animation object which contains callback functions for each event that is expected to be
5501 * * `eventFn`: `function(element, ... , doneFunction, options)`
5502 * The element to animate, the `doneFunction` and the options fed into the animation. Depending
5503 * on the type of animation additional arguments will be injected into the animation function. The
5504 * list below explains the function signatures for the different animation methods:
5506 * - setClass: function(element, addedClasses, removedClasses, doneFunction, options)
5507 * - addClass: function(element, addedClasses, doneFunction, options)
5508 * - removeClass: function(element, removedClasses, doneFunction, options)
5509 * - enter, leave, move: function(element, doneFunction, options)
5510 * - animate: function(element, fromStyles, toStyles, doneFunction, options)
5512 * Make sure to trigger the `doneFunction` once the animation is fully complete.
5516 * //enter, leave, move signature
5517 * eventFn : function(element, done, options) {
5518 * //code to run the animation
5519 * //once complete, then run done()
5520 * return function endFunction(wasCancelled) {
5521 * //code to cancel the animation
5527 * @param {string} name The name of the animation (this is what the class-based CSS value will be compared to).
5528 * @param {Function} factory The factory function that will be executed to return the animation
5531 this.register = function(name, factory) {
5532 if (name && name.charAt(0) !== '.') {
5533 throw $animateMinErr('notcsel', 'Expecting class selector starting with \'.\' got \'{0}\'.', name);
5536 var key = name + '-animation';
5537 provider.$$registeredAnimations[name.substr(1)] = key;
5538 $provide.factory(key, factory);
5543 * @name $animateProvider#classNameFilter
5546 * Sets and/or returns the CSS class regular expression that is checked when performing
5547 * an animation. Upon bootstrap the classNameFilter value is not set at all and will
5548 * therefore enable $animate to attempt to perform an animation on any element that is triggered.
5549 * When setting the `classNameFilter` value, animations will only be performed on elements
5550 * that successfully match the filter expression. This in turn can boost performance
5551 * for low-powered devices as well as applications containing a lot of structural operations.
5552 * @param {RegExp=} expression The className expression which will be checked against all animations
5553 * @return {RegExp} The current CSS className expression value. If null then there is no expression value
5555 this.classNameFilter = function(expression) {
5556 if (arguments.length === 1) {
5557 classNameFilter = (expression instanceof RegExp) ? expression : null;
5558 if (classNameFilter) {
5559 var reservedRegex = new RegExp('[(\\s|\\/)]' + NG_ANIMATE_CLASSNAME + '[(\\s|\\/)]');
5560 if (reservedRegex.test(classNameFilter.toString())) {
5561 classNameFilter = null;
5562 throw $animateMinErr('nongcls', '$animateProvider.classNameFilter(regex) prohibits accepting a regex value which matches/contains the "{0}" CSS class.', NG_ANIMATE_CLASSNAME);
5566 return classNameFilter;
5569 this.$get = ['$$animateQueue', function($$animateQueue) {
5570 function domInsert(element, parentElement, afterElement) {
5571 // if for some reason the previous element was removed
5572 // from the dom sometime before this code runs then let's
5573 // just stick to using the parent element as the anchor
5575 var afterNode = extractElementNode(afterElement);
5576 if (afterNode && !afterNode.parentNode && !afterNode.previousElementSibling) {
5577 afterElement = null;
5581 afterElement.after(element);
5583 parentElement.prepend(element);
5590 * @description The $animate service exposes a series of DOM utility methods that provide support
5591 * for animation hooks. The default behavior is the application of DOM operations, however,
5592 * when an animation is detected (and animations are enabled), $animate will do the heavy lifting
5593 * to ensure that animation runs with the triggered DOM operation.
5595 * By default $animate doesn't trigger any animations. This is because the `ngAnimate` module isn't
5596 * included and only when it is active then the animation hooks that `$animate` triggers will be
5597 * functional. Once active then all structural `ng-` directives will trigger animations as they perform
5598 * their DOM-related operations (enter, leave and move). Other directives such as `ngClass`,
5599 * `ngShow`, `ngHide` and `ngMessages` also provide support for animations.
5601 * It is recommended that the`$animate` service is always used when executing DOM-related procedures within directives.
5603 * To learn more about enabling animation support, click here to visit the
5604 * {@link ngAnimate ngAnimate module page}.
5607 // we don't call it directly since non-existant arguments may
5608 // be interpreted as null within the sub enabled function
5615 * @description Sets up an event listener to fire whenever the animation event (enter, leave, move, etc...)
5616 * has fired on the given element or among any of its children. Once the listener is fired, the provided callback
5617 * is fired with the following params:
5620 * $animate.on('enter', container,
5621 * function callback(element, phase) {
5622 * // cool we detected an enter animation within the container
5627 * @param {string} event the animation event that will be captured (e.g. enter, leave, move, addClass, removeClass, etc...)
5628 * @param {DOMElement} container the container element that will capture each of the animation events that are fired on itself
5629 * as well as among its children
5630 * @param {Function} callback the callback function that will be fired when the listener is triggered
5632 * The arguments present in the callback function are:
5633 * * `element` - The captured DOM element that the animation was fired on.
5634 * * `phase` - The phase of the animation. The two possible phases are **start** (when the animation starts) and **close** (when it ends).
5636 on: $$animateQueue.on,
5641 * @name $animate#off
5643 * @description Deregisters an event listener based on the event which has been associated with the provided element. This method
5644 * can be used in three different ways depending on the arguments:
5647 * // remove all the animation event listeners listening for `enter`
5648 * $animate.off('enter');
5650 * // remove listeners for all animation events from the container element
5651 * $animate.off(container);
5653 * // remove all the animation event listeners listening for `enter` on the given element and its children
5654 * $animate.off('enter', container);
5656 * // remove the event listener function provided by `callback` that is set
5657 * // to listen for `enter` on the given `container` as well as its children
5658 * $animate.off('enter', container, callback);
5661 * @param {string|DOMElement} event|container the animation event (e.g. enter, leave, move,
5662 * addClass, removeClass, etc...), or the container element. If it is the element, all other
5663 * arguments are ignored.
5664 * @param {DOMElement=} container the container element the event listener was placed on
5665 * @param {Function=} callback the callback function that was registered as the listener
5667 off: $$animateQueue.off,
5671 * @name $animate#pin
5673 * @description Associates the provided element with a host parent element to allow the element to be animated even if it exists
5674 * outside of the DOM structure of the Angular application. By doing so, any animation triggered via `$animate` can be issued on the
5675 * element despite being outside the realm of the application or within another application. Say for example if the application
5676 * was bootstrapped on an element that is somewhere inside of the `<body>` tag, but we wanted to allow for an element to be situated
5677 * as a direct child of `document.body`, then this can be achieved by pinning the element via `$animate.pin(element)`. Keep in mind
5678 * that calling `$animate.pin(element, parentElement)` will not actually insert into the DOM anywhere; it will just create the association.
5680 * Note that this feature is only active when the `ngAnimate` module is used.
5682 * @param {DOMElement} element the external element that will be pinned
5683 * @param {DOMElement} parentElement the host parent element that will be associated with the external element
5685 pin: $$animateQueue.pin,
5690 * @name $animate#enabled
5692 * @description Used to get and set whether animations are enabled or not on the entire application or on an element and its children. This
5693 * function can be called in four ways:
5696 * // returns true or false
5697 * $animate.enabled();
5699 * // changes the enabled state for all animations
5700 * $animate.enabled(false);
5701 * $animate.enabled(true);
5703 * // returns true or false if animations are enabled for an element
5704 * $animate.enabled(element);
5706 * // changes the enabled state for an element and its children
5707 * $animate.enabled(element, true);
5708 * $animate.enabled(element, false);
5711 * @param {DOMElement=} element the element that will be considered for checking/setting the enabled state
5712 * @param {boolean=} enabled whether or not the animations will be enabled for the element
5714 * @return {boolean} whether or not animations are enabled
5716 enabled: $$animateQueue.enabled,
5720 * @name $animate#cancel
5722 * @description Cancels the provided animation.
5724 * @param {Promise} animationPromise The animation promise that is returned when an animation is started.
5726 cancel: function(runner) {
5735 * @name $animate#enter
5737 * @description Inserts the element into the DOM either after the `after` element (if provided) or
5738 * as the first child within the `parent` element and then triggers an animation.
5739 * A promise is returned that will be resolved during the next digest once the animation
5742 * @param {DOMElement} element the element which will be inserted into the DOM
5743 * @param {DOMElement} parent the parent element which will append the element as
5744 * a child (so long as the after element is not present)
5745 * @param {DOMElement=} after the sibling element after which the element will be appended
5746 * @param {object=} options an optional collection of options/styles that will be applied to the element.
5747 * The object can have the following properties:
5749 * - **addClass** - `{string}` - space-separated CSS classes to add to element
5750 * - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to`
5751 * - **removeClass** - `{string}` - space-separated CSS classes to remove from element
5752 * - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from`
5754 * @return {Promise} the animation callback promise
5756 enter: function(element, parent, after, options) {
5757 parent = parent && jqLite(parent);
5758 after = after && jqLite(after);
5759 parent = parent || after.parent();
5760 domInsert(element, parent, after);
5761 return $$animateQueue.push(element, 'enter', prepareAnimateOptions(options));
5767 * @name $animate#move
5769 * @description Inserts (moves) the element into its new position in the DOM either after
5770 * the `after` element (if provided) or as the first child within the `parent` element
5771 * and then triggers an animation. A promise is returned that will be resolved
5772 * during the next digest once the animation has completed.
5774 * @param {DOMElement} element the element which will be moved into the new DOM position
5775 * @param {DOMElement} parent the parent element which will append the element as
5776 * a child (so long as the after element is not present)
5777 * @param {DOMElement=} after the sibling element after which the element will be appended
5778 * @param {object=} options an optional collection of options/styles that will be applied to the element.
5779 * The object can have the following properties:
5781 * - **addClass** - `{string}` - space-separated CSS classes to add to element
5782 * - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to`
5783 * - **removeClass** - `{string}` - space-separated CSS classes to remove from element
5784 * - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from`
5786 * @return {Promise} the animation callback promise
5788 move: function(element, parent, after, options) {
5789 parent = parent && jqLite(parent);
5790 after = after && jqLite(after);
5791 parent = parent || after.parent();
5792 domInsert(element, parent, after);
5793 return $$animateQueue.push(element, 'move', prepareAnimateOptions(options));
5798 * @name $animate#leave
5800 * @description Triggers an animation and then removes the element from the DOM.
5801 * When the function is called a promise is returned that will be resolved during the next
5802 * digest once the animation has completed.
5804 * @param {DOMElement} element the element which will be removed from the DOM
5805 * @param {object=} options an optional collection of options/styles that will be applied to the element.
5806 * The object can have the following properties:
5808 * - **addClass** - `{string}` - space-separated CSS classes to add to element
5809 * - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to`
5810 * - **removeClass** - `{string}` - space-separated CSS classes to remove from element
5811 * - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from`
5813 * @return {Promise} the animation callback promise
5815 leave: function(element, options) {
5816 return $$animateQueue.push(element, 'leave', prepareAnimateOptions(options), function() {
5823 * @name $animate#addClass
5826 * @description Triggers an addClass animation surrounding the addition of the provided CSS class(es). Upon
5827 * execution, the addClass operation will only be handled after the next digest and it will not trigger an
5828 * animation if element already contains the CSS class or if the class is removed at a later step.
5829 * Note that class-based animations are treated differently compared to structural animations
5830 * (like enter, move and leave) since the CSS classes may be added/removed at different points
5831 * depending if CSS or JavaScript animations are used.
5833 * @param {DOMElement} element the element which the CSS classes will be applied to
5834 * @param {string} className the CSS class(es) that will be added (multiple classes are separated via spaces)
5835 * @param {object=} options an optional collection of options/styles that will be applied to the element.
5836 * The object can have the following properties:
5838 * - **addClass** - `{string}` - space-separated CSS classes to add to element
5839 * - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to`
5840 * - **removeClass** - `{string}` - space-separated CSS classes to remove from element
5841 * - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from`
5843 * @return {Promise} the animation callback promise
5845 addClass: function(element, className, options) {
5846 options = prepareAnimateOptions(options);
5847 options.addClass = mergeClasses(options.addclass, className);
5848 return $$animateQueue.push(element, 'addClass', options);
5853 * @name $animate#removeClass
5856 * @description Triggers a removeClass animation surrounding the removal of the provided CSS class(es). Upon
5857 * execution, the removeClass operation will only be handled after the next digest and it will not trigger an
5858 * animation if element does not contain the CSS class or if the class is added at a later step.
5859 * Note that class-based animations are treated differently compared to structural animations
5860 * (like enter, move and leave) since the CSS classes may be added/removed at different points
5861 * depending if CSS or JavaScript animations are used.
5863 * @param {DOMElement} element the element which the CSS classes will be applied to
5864 * @param {string} className the CSS class(es) that will be removed (multiple classes are separated via spaces)
5865 * @param {object=} options an optional collection of options/styles that will be applied to the element.
5866 * The object can have the following properties:
5868 * - **addClass** - `{string}` - space-separated CSS classes to add to element
5869 * - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to`
5870 * - **removeClass** - `{string}` - space-separated CSS classes to remove from element
5871 * - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from`
5873 * @return {Promise} the animation callback promise
5875 removeClass: function(element, className, options) {
5876 options = prepareAnimateOptions(options);
5877 options.removeClass = mergeClasses(options.removeClass, className);
5878 return $$animateQueue.push(element, 'removeClass', options);
5883 * @name $animate#setClass
5886 * @description Performs both the addition and removal of a CSS classes on an element and (during the process)
5887 * triggers an animation surrounding the class addition/removal. Much like `$animate.addClass` and
5888 * `$animate.removeClass`, `setClass` will only evaluate the classes being added/removed once a digest has
5889 * passed. Note that class-based animations are treated differently compared to structural animations
5890 * (like enter, move and leave) since the CSS classes may be added/removed at different points
5891 * depending if CSS or JavaScript animations are used.
5893 * @param {DOMElement} element the element which the CSS classes will be applied to
5894 * @param {string} add the CSS class(es) that will be added (multiple classes are separated via spaces)
5895 * @param {string} remove the CSS class(es) that will be removed (multiple classes are separated via spaces)
5896 * @param {object=} options an optional collection of options/styles that will be applied to the element.
5897 * The object can have the following properties:
5899 * - **addClass** - `{string}` - space-separated CSS classes to add to element
5900 * - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to`
5901 * - **removeClass** - `{string}` - space-separated CSS classes to remove from element
5902 * - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from`
5904 * @return {Promise} the animation callback promise
5906 setClass: function(element, add, remove, options) {
5907 options = prepareAnimateOptions(options);
5908 options.addClass = mergeClasses(options.addClass, add);
5909 options.removeClass = mergeClasses(options.removeClass, remove);
5910 return $$animateQueue.push(element, 'setClass', options);
5915 * @name $animate#animate
5918 * @description Performs an inline animation on the element which applies the provided to and from CSS styles to the element.
5919 * If any detected CSS transition, keyframe or JavaScript matches the provided className value, then the animation will take
5920 * on the provided styles. For example, if a transition animation is set for the given className, then the provided `from` and
5921 * `to` styles will be applied alongside the given transition. If the CSS style provided in `from` does not have a corresponding
5922 * style in `to`, the style in `from` is applied immediately, and no animation is run.
5923 * If a JavaScript animation is detected then the provided styles will be given in as function parameters into the `animate`
5924 * method (or as part of the `options` parameter):
5927 * ngModule.animation('.my-inline-animation', function() {
5929 * animate : function(element, from, to, done, options) {
5937 * @param {DOMElement} element the element which the CSS styles will be applied to
5938 * @param {object} from the from (starting) CSS styles that will be applied to the element and across the animation.
5939 * @param {object} to the to (destination) CSS styles that will be applied to the element and across the animation.
5940 * @param {string=} className an optional CSS class that will be applied to the element for the duration of the animation. If
5941 * this value is left as empty then a CSS class of `ng-inline-animate` will be applied to the element.
5942 * (Note that if no animation is detected then this value will not be applied to the element.)
5943 * @param {object=} options an optional collection of options/styles that will be applied to the element.
5944 * The object can have the following properties:
5946 * - **addClass** - `{string}` - space-separated CSS classes to add to element
5947 * - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to`
5948 * - **removeClass** - `{string}` - space-separated CSS classes to remove from element
5949 * - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from`
5951 * @return {Promise} the animation callback promise
5953 animate: function(element, from, to, className, options) {
5954 options = prepareAnimateOptions(options);
5955 options.from = options.from ? extend(options.from, from) : from;
5956 options.to = options.to ? extend(options.to, to) : to;
5958 className = className || 'ng-inline-animate';
5959 options.tempClasses = mergeClasses(options.tempClasses, className);
5960 return $$animateQueue.push(element, 'animate', options);
5966 var $$AnimateAsyncRunFactoryProvider = /** @this */ function() {
5967 this.$get = ['$$rAF', function($$rAF) {
5970 function waitForTick(fn) {
5972 if (waitQueue.length > 1) return;
5974 for (var i = 0; i < waitQueue.length; i++) {
5983 waitForTick(function() {
5986 return function(callback) {
5990 waitForTick(callback);
5997 var $$AnimateRunnerFactoryProvider = /** @this */ function() {
5998 this.$get = ['$q', '$sniffer', '$$animateAsyncRun', '$$isDocumentHidden', '$timeout',
5999 function($q, $sniffer, $$animateAsyncRun, $$isDocumentHidden, $timeout) {
6001 var INITIAL_STATE = 0;
6002 var DONE_PENDING_STATE = 1;
6003 var DONE_COMPLETE_STATE = 2;
6005 AnimateRunner.chain = function(chain, callback) {
6010 if (index === chain.length) {
6015 chain[index](function(response) {
6016 if (response === false) {
6026 AnimateRunner.all = function(runners, callback) {
6029 forEach(runners, function(runner) {
6030 runner.done(onProgress);
6033 function onProgress(response) {
6034 status = status && response;
6035 if (++count === runners.length) {
6041 function AnimateRunner(host) {
6044 var rafTick = $$animateAsyncRun();
6045 var timeoutTick = function(fn) {
6046 $timeout(fn, 0, false);
6049 this._doneCallbacks = [];
6050 this._tick = function(fn) {
6051 if ($$isDocumentHidden()) {
6060 AnimateRunner.prototype = {
6061 setHost: function(host) {
6062 this.host = host || {};
6065 done: function(fn) {
6066 if (this._state === DONE_COMPLETE_STATE) {
6069 this._doneCallbacks.push(fn);
6075 getPromise: function() {
6076 if (!this.promise) {
6078 this.promise = $q(function(resolve, reject) {
6079 self.done(function(status) {
6080 if (status === false) {
6088 return this.promise;
6091 then: function(resolveHandler, rejectHandler) {
6092 return this.getPromise().then(resolveHandler, rejectHandler);
6095 'catch': function(handler) {
6096 return this.getPromise()['catch'](handler);
6099 'finally': function(handler) {
6100 return this.getPromise()['finally'](handler);
6104 if (this.host.pause) {
6109 resume: function() {
6110 if (this.host.resume) {
6116 if (this.host.end) {
6119 this._resolve(true);
6122 cancel: function() {
6123 if (this.host.cancel) {
6126 this._resolve(false);
6129 complete: function(response) {
6131 if (self._state === INITIAL_STATE) {
6132 self._state = DONE_PENDING_STATE;
6133 self._tick(function() {
6134 self._resolve(response);
6139 _resolve: function(response) {
6140 if (this._state !== DONE_COMPLETE_STATE) {
6141 forEach(this._doneCallbacks, function(fn) {
6144 this._doneCallbacks.length = 0;
6145 this._state = DONE_COMPLETE_STATE;
6150 return AnimateRunner;
6154 /* exported $CoreAnimateCssProvider */
6163 * This is the core version of `$animateCss`. By default, only when the `ngAnimate` is included,
6164 * then the `$animateCss` service will actually perform animations.
6166 * Click here {@link ngAnimate.$animateCss to read the documentation for $animateCss}.
6168 var $CoreAnimateCssProvider = function() {
6169 this.$get = ['$$rAF', '$q', '$$AnimateRunner', function($$rAF, $q, $$AnimateRunner) {
6171 return function(element, initialOptions) {
6172 // all of the animation functions should create
6173 // a copy of the options data, however, if a
6174 // parent service has already created a copy then
6175 // we should stick to using that
6176 var options = initialOptions || {};
6177 if (!options.$$prepared) {
6178 options = copy(options);
6181 // there is no point in applying the styles since
6182 // there is no animation that goes on at all in
6183 // this version of $animateCss.
6184 if (options.cleanupStyles) {
6185 options.from = options.to = null;
6189 element.css(options.from);
6190 options.from = null;
6193 var closed, runner = new $$AnimateRunner();
6201 applyAnimationContents();
6210 function applyAnimationContents() {
6211 if (options.addClass) {
6212 element.addClass(options.addClass);
6213 options.addClass = null;
6215 if (options.removeClass) {
6216 element.removeClass(options.removeClass);
6217 options.removeClass = null;
6220 element.css(options.to);
6228 /* global stripHash: true */
6231 * ! This is a private undocumented service !
6236 * This object has two goals:
6238 * - hide all the global state in the browser caused by the window object
6239 * - abstract away all the browser specific features and inconsistencies
6241 * For tests we provide {@link ngMock.$browser mock implementation} of the `$browser`
6242 * service, which can be used for convenient testing of the application without the interaction with
6243 * the real browser apis.
6246 * @param {object} window The global window object.
6247 * @param {object} document jQuery wrapped document.
6248 * @param {object} $log window.console or an object with the same interface.
6249 * @param {object} $sniffer $sniffer service
6251 function Browser(window, document, $log, $sniffer) {
6253 location = window.location,
6254 history = window.history,
6255 setTimeout = window.setTimeout,
6256 clearTimeout = window.clearTimeout,
6257 pendingDeferIds = {};
6259 self.isMock = false;
6261 var outstandingRequestCount = 0;
6262 var outstandingRequestCallbacks = [];
6264 // TODO(vojta): remove this temporary api
6265 self.$$completeOutstandingRequest = completeOutstandingRequest;
6266 self.$$incOutstandingRequestCount = function() { outstandingRequestCount++; };
6269 * Executes the `fn` function(supports currying) and decrements the `outstandingRequestCallbacks`
6270 * counter. If the counter reaches 0, all the `outstandingRequestCallbacks` are executed.
6272 function completeOutstandingRequest(fn) {
6274 fn.apply(null, sliceArgs(arguments, 1));
6276 outstandingRequestCount--;
6277 if (outstandingRequestCount === 0) {
6278 while (outstandingRequestCallbacks.length) {
6280 outstandingRequestCallbacks.pop()();
6289 function getHash(url) {
6290 var index = url.indexOf('#');
6291 return index === -1 ? '' : url.substr(index);
6296 * Note: this method is used only by scenario runner
6297 * TODO(vojta): prefix this method with $$ ?
6298 * @param {function()} callback Function that will be called when no outstanding request
6300 self.notifyWhenNoOutstandingRequests = function(callback) {
6301 if (outstandingRequestCount === 0) {
6304 outstandingRequestCallbacks.push(callback);
6308 //////////////////////////////////////////////////////////////
6310 //////////////////////////////////////////////////////////////
6312 var cachedState, lastHistoryState,
6313 lastBrowserUrl = location.href,
6314 baseElement = document.find('base'),
6315 pendingLocation = null,
6316 getCurrentState = !$sniffer.history ? noop : function getCurrentState() {
6318 return history.state;
6320 // MSIE can reportedly throw when there is no state (UNCONFIRMED).
6327 * @name $browser#url
6331 * Without any argument, this method just returns current value of location.href.
6334 * With at least one argument, this method sets url to new value.
6335 * If html5 history api supported, pushState/replaceState is used, otherwise
6336 * location.href/location.replace is used.
6337 * Returns its own instance to allow chaining
6339 * NOTE: this api is intended for use only by the $location service. Please use the
6340 * {@link ng.$location $location service} to change url.
6342 * @param {string} url New url (when used as setter)
6343 * @param {boolean=} replace Should new url replace current history record?
6344 * @param {object=} state object to use with pushState/replaceState
6346 self.url = function(url, replace, state) {
6347 // In modern browsers `history.state` is `null` by default; treating it separately
6348 // from `undefined` would cause `$browser.url('/foo')` to change `history.state`
6349 // to undefined via `pushState`. Instead, let's change `undefined` to `null` here.
6350 if (isUndefined(state)) {
6354 // Android Browser BFCache causes location, history reference to become stale.
6355 if (location !== window.location) location = window.location;
6356 if (history !== window.history) history = window.history;
6360 var sameState = lastHistoryState === state;
6362 // Don't change anything if previous and current URLs and states match. This also prevents
6363 // IE<10 from getting into redirect loop when in LocationHashbangInHtml5Url mode.
6364 // See https://github.com/angular/angular.js/commit/ffb2701
6365 if (lastBrowserUrl === url && (!$sniffer.history || sameState)) {
6368 var sameBase = lastBrowserUrl && stripHash(lastBrowserUrl) === stripHash(url);
6369 lastBrowserUrl = url;
6370 lastHistoryState = state;
6371 // Don't use history API if only the hash changed
6372 // due to a bug in IE10/IE11 which leads
6373 // to not firing a `hashchange` nor `popstate` event
6374 // in some cases (see #9143).
6375 if ($sniffer.history && (!sameBase || !sameState)) {
6376 history[replace ? 'replaceState' : 'pushState'](state, '', url);
6380 pendingLocation = url;
6383 location.replace(url);
6384 } else if (!sameBase) {
6385 location.href = url;
6387 location.hash = getHash(url);
6389 if (location.href !== url) {
6390 pendingLocation = url;
6393 if (pendingLocation) {
6394 pendingLocation = url;
6399 // - pendingLocation is needed as browsers don't allow to read out
6400 // the new location.href if a reload happened or if there is a bug like in iOS 9 (see
6401 // https://openradar.appspot.com/22186109).
6402 // - the replacement is a workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=407172
6403 return pendingLocation || location.href.replace(/%27/g,'\'');
6408 * @name $browser#state
6411 * This method is a getter.
6413 * Return history.state or null if history.state is undefined.
6415 * @returns {object} state
6417 self.state = function() {
6421 var urlChangeListeners = [],
6422 urlChangeInit = false;
6424 function cacheStateAndFireUrlChange() {
6425 pendingLocation = null;
6426 fireStateOrUrlChange();
6429 // This variable should be used *only* inside the cacheState function.
6430 var lastCachedState = null;
6431 function cacheState() {
6432 // This should be the only place in $browser where `history.state` is read.
6433 cachedState = getCurrentState();
6434 cachedState = isUndefined(cachedState) ? null : cachedState;
6436 // Prevent callbacks fo fire twice if both hashchange & popstate were fired.
6437 if (equals(cachedState, lastCachedState)) {
6438 cachedState = lastCachedState;
6441 lastCachedState = cachedState;
6442 lastHistoryState = cachedState;
6445 function fireStateOrUrlChange() {
6446 var prevLastHistoryState = lastHistoryState;
6449 if (lastBrowserUrl === self.url() && prevLastHistoryState === cachedState) {
6453 lastBrowserUrl = self.url();
6454 lastHistoryState = cachedState;
6455 forEach(urlChangeListeners, function(listener) {
6456 listener(self.url(), cachedState);
6461 * @name $browser#onUrlChange
6464 * Register callback function that will be called, when url changes.
6466 * It's only called when the url is changed from outside of angular:
6467 * - user types different url into address bar
6468 * - user clicks on history (forward/back) button
6469 * - user clicks on a link
6471 * It's not called when url is changed by $browser.url() method
6473 * The listener gets called with new url as parameter.
6475 * NOTE: this api is intended for use only by the $location service. Please use the
6476 * {@link ng.$location $location service} to monitor url changes in angular apps.
6478 * @param {function(string)} listener Listener function to be called when url changes.
6479 * @return {function(string)} Returns the registered listener fn - handy if the fn is anonymous.
6481 self.onUrlChange = function(callback) {
6482 // TODO(vojta): refactor to use node's syntax for events
6483 if (!urlChangeInit) {
6484 // We listen on both (hashchange/popstate) when available, as some browsers don't
6485 // fire popstate when user changes the address bar and don't fire hashchange when url
6486 // changed by push/replaceState
6488 // html5 history api - popstate event
6489 if ($sniffer.history) jqLite(window).on('popstate', cacheStateAndFireUrlChange);
6491 jqLite(window).on('hashchange', cacheStateAndFireUrlChange);
6493 urlChangeInit = true;
6496 urlChangeListeners.push(callback);
6502 * Remove popstate and hashchange handler from window.
6504 * NOTE: this api is intended for use only by $rootScope.
6506 self.$$applicationDestroyed = function() {
6507 jqLite(window).off('hashchange popstate', cacheStateAndFireUrlChange);
6511 * Checks whether the url has changed outside of Angular.
6512 * Needs to be exported to be able to check for changes that have been done in sync,
6513 * as hashchange/popstate events fire in async.
6515 self.$$checkUrlChange = fireStateOrUrlChange;
6517 //////////////////////////////////////////////////////////////
6519 //////////////////////////////////////////////////////////////
6522 * @name $browser#baseHref
6525 * Returns current <base href>
6526 * (always relative - without domain)
6528 * @returns {string} The current base href
6530 self.baseHref = function() {
6531 var href = baseElement.attr('href');
6532 return href ? href.replace(/^(https?:)?\/\/[^/]*/, '') : '';
6536 * @name $browser#defer
6537 * @param {function()} fn A function, who's execution should be deferred.
6538 * @param {number=} [delay=0] of milliseconds to defer the function execution.
6539 * @returns {*} DeferId that can be used to cancel the task via `$browser.defer.cancel()`.
6542 * Executes a fn asynchronously via `setTimeout(fn, delay)`.
6544 * Unlike when calling `setTimeout` directly, in test this function is mocked and instead of using
6545 * `setTimeout` in tests, the fns are queued in an array, which can be programmatically flushed
6546 * via `$browser.defer.flush()`.
6549 self.defer = function(fn, delay) {
6551 outstandingRequestCount++;
6552 timeoutId = setTimeout(function() {
6553 delete pendingDeferIds[timeoutId];
6554 completeOutstandingRequest(fn);
6556 pendingDeferIds[timeoutId] = true;
6562 * @name $browser#defer.cancel
6565 * Cancels a deferred task identified with `deferId`.
6567 * @param {*} deferId Token returned by the `$browser.defer` function.
6568 * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully
6571 self.defer.cancel = function(deferId) {
6572 if (pendingDeferIds[deferId]) {
6573 delete pendingDeferIds[deferId];
6574 clearTimeout(deferId);
6575 completeOutstandingRequest(noop);
6584 function $BrowserProvider() {
6585 this.$get = ['$window', '$log', '$sniffer', '$document',
6586 function($window, $log, $sniffer, $document) {
6587 return new Browser($window, $document, $log, $sniffer);
6593 * @name $cacheFactory
6597 * Factory that constructs {@link $cacheFactory.Cache Cache} objects and gives access to
6602 * var cache = $cacheFactory('cacheId');
6603 * expect($cacheFactory.get('cacheId')).toBe(cache);
6604 * expect($cacheFactory.get('noSuchCacheId')).not.toBeDefined();
6606 * cache.put("key", "value");
6607 * cache.put("another key", "another value");
6609 * // We've specified no options on creation
6610 * expect(cache.info()).toEqual({id: 'cacheId', size: 2});
6615 * @param {string} cacheId Name or id of the newly created cache.
6616 * @param {object=} options Options object that specifies the cache behavior. Properties:
6618 * - `{number=}` `capacity` — turns the cache into LRU cache.
6620 * @returns {object} Newly created cache object with the following set of methods:
6622 * - `{object}` `info()` — Returns id, size, and options of cache.
6623 * - `{{*}}` `put({string} key, {*} value)` — Puts a new key-value pair into the cache and returns
6625 * - `{{*}}` `get({string} key)` — Returns cached value for `key` or undefined for cache miss.
6626 * - `{void}` `remove({string} key)` — Removes a key-value pair from the cache.
6627 * - `{void}` `removeAll()` — Removes all cached values.
6628 * - `{void}` `destroy()` — Removes references to this cache from $cacheFactory.
6631 <example module="cacheExampleApp" name="cache-factory">
6632 <file name="index.html">
6633 <div ng-controller="CacheController">
6634 <input ng-model="newCacheKey" placeholder="Key">
6635 <input ng-model="newCacheValue" placeholder="Value">
6636 <button ng-click="put(newCacheKey, newCacheValue)">Cache</button>
6638 <p ng-if="keys.length">Cached Values</p>
6639 <div ng-repeat="key in keys">
6640 <span ng-bind="key"></span>
6642 <b ng-bind="cache.get(key)"></b>
6646 <div ng-repeat="(key, value) in cache.info()">
6647 <span ng-bind="key"></span>
6649 <b ng-bind="value"></b>
6653 <file name="script.js">
6654 angular.module('cacheExampleApp', []).
6655 controller('CacheController', ['$scope', '$cacheFactory', function($scope, $cacheFactory) {
6657 $scope.cache = $cacheFactory('cacheId');
6658 $scope.put = function(key, value) {
6659 if (angular.isUndefined($scope.cache.get(key))) {
6660 $scope.keys.push(key);
6662 $scope.cache.put(key, angular.isUndefined(value) ? null : value);
6666 <file name="style.css">
6673 function $CacheFactoryProvider() {
6675 this.$get = function() {
6678 function cacheFactory(cacheId, options) {
6679 if (cacheId in caches) {
6680 throw minErr('$cacheFactory')('iid', 'CacheId \'{0}\' is already taken!', cacheId);
6684 stats = extend({}, options, {id: cacheId}),
6686 capacity = (options && options.capacity) || Number.MAX_VALUE,
6687 lruHash = createMap(),
6693 * @name $cacheFactory.Cache
6696 * A cache object used to store and retrieve data, primarily used by
6697 * {@link $http $http} and the {@link ng.directive:script script} directive to cache
6698 * templates and other data.
6701 * angular.module('superCache')
6702 * .factory('superCache', ['$cacheFactory', function($cacheFactory) {
6703 * return $cacheFactory('super-cache');
6710 * it('should behave like a cache', inject(function(superCache) {
6711 * superCache.put('key', 'value');
6712 * superCache.put('another key', 'another value');
6714 * expect(superCache.info()).toEqual({
6715 * id: 'super-cache',
6719 * superCache.remove('another key');
6720 * expect(superCache.get('another key')).toBeUndefined();
6722 * superCache.removeAll();
6723 * expect(superCache.info()).toEqual({
6724 * id: 'super-cache',
6730 return (caches[cacheId] = {
6734 * @name $cacheFactory.Cache#put
6738 * Inserts a named entry into the {@link $cacheFactory.Cache Cache} object to be
6739 * retrieved later, and incrementing the size of the cache if the key was not already
6740 * present in the cache. If behaving like an LRU cache, it will also remove stale
6741 * entries from the set.
6743 * It will not insert undefined values into the cache.
6745 * @param {string} key the key under which the cached data is stored.
6746 * @param {*} value the value to store alongside the key. If it is undefined, the key
6747 * will not be stored.
6748 * @returns {*} the value stored.
6750 put: function(key, value) {
6751 if (isUndefined(value)) return;
6752 if (capacity < Number.MAX_VALUE) {
6753 var lruEntry = lruHash[key] || (lruHash[key] = {key: key});
6758 if (!(key in data)) size++;
6761 if (size > capacity) {
6762 this.remove(staleEnd.key);
6770 * @name $cacheFactory.Cache#get
6774 * Retrieves named data stored in the {@link $cacheFactory.Cache Cache} object.
6776 * @param {string} key the key of the data to be retrieved
6777 * @returns {*} the value stored.
6779 get: function(key) {
6780 if (capacity < Number.MAX_VALUE) {
6781 var lruEntry = lruHash[key];
6783 if (!lruEntry) return;
6794 * @name $cacheFactory.Cache#remove
6798 * Removes an entry from the {@link $cacheFactory.Cache Cache} object.
6800 * @param {string} key the key of the entry to be removed
6802 remove: function(key) {
6803 if (capacity < Number.MAX_VALUE) {
6804 var lruEntry = lruHash[key];
6806 if (!lruEntry) return;
6808 if (lruEntry === freshEnd) freshEnd = lruEntry.p;
6809 if (lruEntry === staleEnd) staleEnd = lruEntry.n;
6810 link(lruEntry.n,lruEntry.p);
6812 delete lruHash[key];
6815 if (!(key in data)) return;
6824 * @name $cacheFactory.Cache#removeAll
6828 * Clears the cache object of any entries.
6830 removeAll: function() {
6833 lruHash = createMap();
6834 freshEnd = staleEnd = null;
6840 * @name $cacheFactory.Cache#destroy
6844 * Destroys the {@link $cacheFactory.Cache Cache} object entirely,
6845 * removing it from the {@link $cacheFactory $cacheFactory} set.
6847 destroy: function() {
6851 delete caches[cacheId];
6857 * @name $cacheFactory.Cache#info
6861 * Retrieve information regarding a particular {@link $cacheFactory.Cache Cache}.
6863 * @returns {object} an object with the following properties:
6865 * <li>**id**: the id of the cache instance</li>
6866 * <li>**size**: the number of entries kept in the cache instance</li>
6867 * <li>**...**: any additional properties from the options object when creating the
6872 return extend({}, stats, {size: size});
6878 * makes the `entry` the freshEnd of the LRU linked list
6880 function refresh(entry) {
6881 if (entry !== freshEnd) {
6884 } else if (staleEnd === entry) {
6888 link(entry.n, entry.p);
6889 link(entry, freshEnd);
6897 * bidirectionally links two entries of the LRU linked list
6899 function link(nextEntry, prevEntry) {
6900 if (nextEntry !== prevEntry) {
6901 if (nextEntry) nextEntry.p = prevEntry; //p stands for previous, 'prev' didn't minify
6902 if (prevEntry) prevEntry.n = nextEntry; //n stands for next, 'next' didn't minify
6910 * @name $cacheFactory#info
6913 * Get information about all the caches that have been created
6915 * @returns {Object} - key-value map of `cacheId` to the result of calling `cache#info`
6917 cacheFactory.info = function() {
6919 forEach(caches, function(cache, cacheId) {
6920 info[cacheId] = cache.info();
6928 * @name $cacheFactory#get
6931 * Get access to a cache object by the `cacheId` used when it was created.
6933 * @param {string} cacheId Name or id of a cache to access.
6934 * @returns {object} Cache object identified by the cacheId or undefined if no such cache.
6936 cacheFactory.get = function(cacheId) {
6937 return caches[cacheId];
6941 return cacheFactory;
6947 * @name $templateCache
6951 * The first time a template is used, it is loaded in the template cache for quick retrieval. You
6952 * can load templates directly into the cache in a `script` tag, or by consuming the
6953 * `$templateCache` service directly.
6955 * Adding via the `script` tag:
6958 * <script type="text/ng-template" id="templateId.html">
6959 * <p>This is the content of the template</p>
6963 * **Note:** the `script` tag containing the template does not need to be included in the `head` of
6964 * the document, but it must be a descendent of the {@link ng.$rootElement $rootElement} (IE,
6965 * element with ng-app attribute), otherwise the template will be ignored.
6967 * Adding via the `$templateCache` service:
6970 * var myApp = angular.module('myApp', []);
6971 * myApp.run(function($templateCache) {
6972 * $templateCache.put('templateId.html', 'This is the content of the template');
6976 * To retrieve the template later, simply use it in your component:
6978 * myApp.component('myComponent', {
6979 * templateUrl: 'templateId.html'
6983 * or get it via the `$templateCache` service:
6985 * $templateCache.get('templateId.html')
6988 * See {@link ng.$cacheFactory $cacheFactory}.
6991 function $TemplateCacheProvider() {
6992 this.$get = ['$cacheFactory', function($cacheFactory) {
6993 return $cacheFactory('templates');
6997 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
6998 * Any commits to this file should be reviewed with security in mind. *
6999 * Changes to this file can potentially create security vulnerabilities. *
7000 * An approval from 2 Core members with history of modifying *
7001 * this file is required. *
7003 * Does the change somehow allow for arbitrary javascript to be executed? *
7004 * Or allows for someone to change the prototype of built-in objects? *
7005 * Or gives undesired access to variables like document or window? *
7006 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
7008 /* ! VARIABLE/FUNCTION NAMING CONVENTIONS THAT APPLY TO THIS FILE!
7010 * DOM-related variables:
7012 * - "node" - DOM Node
7013 * - "element" - DOM Element or Node
7014 * - "$node" or "$element" - jqLite-wrapped node or element
7017 * Compiler related stuff:
7019 * - "linkFn" - linking fn of a single directive
7020 * - "nodeLinkFn" - function that aggregates all linking fns for a particular node
7021 * - "childLinkFn" - function that aggregates all linking fns for child nodes of a particular node
7022 * - "compositeLinkFn" - function that aggregates all linking fns for a compilation root (nodeList)
7032 * Compiles an HTML string or DOM into a template and produces a template function, which
7033 * can then be used to link {@link ng.$rootScope.Scope `scope`} and the template together.
7035 * The compilation is a process of walking the DOM tree and matching DOM elements to
7036 * {@link ng.$compileProvider#directive directives}.
7038 * <div class="alert alert-warning">
7039 * **Note:** This document is an in-depth reference of all directive options.
7040 * For a gentle introduction to directives with examples of common use cases,
7041 * see the {@link guide/directive directive guide}.
7044 * ## Comprehensive Directive API
7046 * There are many different options for a directive.
7048 * The difference resides in the return value of the factory function.
7049 * You can either return a {@link $compile#directive-definition-object Directive Definition Object (see below)}
7050 * that defines the directive properties, or just the `postLink` function (all other properties will have
7051 * the default values).
7053 * <div class="alert alert-success">
7054 * **Best Practice:** It's recommended to use the "directive definition object" form.
7057 * Here's an example directive declared with a Directive Definition Object:
7060 * var myModule = angular.module(...);
7062 * myModule.directive('directiveName', function factory(injectables) {
7063 * var directiveDefinitionObject = {
7064 * {@link $compile#-priority- priority}: 0,
7065 * {@link $compile#-template- template}: '<div></div>', // or // function(tElement, tAttrs) { ... },
7067 * // {@link $compile#-templateurl- templateUrl}: 'directive.html', // or // function(tElement, tAttrs) { ... },
7068 * {@link $compile#-transclude- transclude}: false,
7069 * {@link $compile#-restrict- restrict}: 'A',
7070 * {@link $compile#-templatenamespace- templateNamespace}: 'html',
7071 * {@link $compile#-scope- scope}: false,
7072 * {@link $compile#-controller- controller}: function($scope, $element, $attrs, $transclude, otherInjectables) { ... },
7073 * {@link $compile#-controlleras- controllerAs}: 'stringIdentifier',
7074 * {@link $compile#-bindtocontroller- bindToController}: false,
7075 * {@link $compile#-require- require}: 'siblingDirectiveName', // or // ['^parentDirectiveName', '?optionalDirectiveName', '?^optionalParent'],
7076 * {@link $compile#-multielement- multiElement}: false,
7077 * {@link $compile#-compile- compile}: function compile(tElement, tAttrs, transclude) {
7079 * {@link $compile#pre-linking-function pre}: function preLink(scope, iElement, iAttrs, controller) { ... },
7080 * {@link $compile#post-linking-function post}: function postLink(scope, iElement, iAttrs, controller) { ... }
7083 * // return function postLink( ... ) { ... }
7086 * // {@link $compile#-link- link}: {
7087 * // {@link $compile#pre-linking-function pre}: function preLink(scope, iElement, iAttrs, controller) { ... },
7088 * // {@link $compile#post-linking-function post}: function postLink(scope, iElement, iAttrs, controller) { ... }
7091 * // {@link $compile#-link- link}: function postLink( ... ) { ... }
7093 * return directiveDefinitionObject;
7097 * <div class="alert alert-warning">
7098 * **Note:** Any unspecified options will use the default value. You can see the default values below.
7101 * Therefore the above can be simplified as:
7104 * var myModule = angular.module(...);
7106 * myModule.directive('directiveName', function factory(injectables) {
7107 * var directiveDefinitionObject = {
7108 * link: function postLink(scope, iElement, iAttrs) { ... }
7110 * return directiveDefinitionObject;
7112 * // return function postLink(scope, iElement, iAttrs) { ... }
7116 * ### Life-cycle hooks
7117 * Directive controllers can provide the following methods that are called by Angular at points in the life-cycle of the
7119 * * `$onInit()` - Called on each controller after all the controllers on an element have been constructed and
7120 * had their bindings initialized (and before the pre & post linking functions for the directives on
7121 * this element). This is a good place to put initialization code for your controller.
7122 * * `$onChanges(changesObj)` - Called whenever one-way (`<`) or interpolation (`@`) bindings are updated. The
7123 * `changesObj` is a hash whose keys are the names of the bound properties that have changed, and the values are an
7124 * object of the form `{ currentValue, previousValue, isFirstChange() }`. Use this hook to trigger updates within a
7125 * component such as cloning the bound value to prevent accidental mutation of the outer value. Note that this will
7126 * also be called when your bindings are initialized.
7127 * * `$doCheck()` - Called on each turn of the digest cycle. Provides an opportunity to detect and act on
7128 * changes. Any actions that you wish to take in response to the changes that you detect must be
7129 * invoked from this hook; implementing this has no effect on when `$onChanges` is called. For example, this hook
7130 * could be useful if you wish to perform a deep equality check, or to check a Date object, changes to which would not
7131 * be detected by Angular's change detector and thus not trigger `$onChanges`. This hook is invoked with no arguments;
7132 * if detecting changes, you must store the previous value(s) for comparison to the current values.
7133 * * `$onDestroy()` - Called on a controller when its containing scope is destroyed. Use this hook for releasing
7134 * external resources, watches and event handlers. Note that components have their `$onDestroy()` hooks called in
7135 * the same order as the `$scope.$broadcast` events are triggered, which is top down. This means that parent
7136 * components will have their `$onDestroy()` hook called before child components.
7137 * * `$postLink()` - Called after this controller's element and its children have been linked. Similar to the post-link
7138 * function this hook can be used to set up DOM event handlers and do direct DOM manipulation.
7139 * Note that child elements that contain `templateUrl` directives will not have been compiled and linked since
7140 * they are waiting for their template to load asynchronously and their own compilation and linking has been
7141 * suspended until that occurs.
7143 * #### Comparison with Angular 2 life-cycle hooks
7144 * Angular 2 also uses life-cycle hooks for its components. While the Angular 1 life-cycle hooks are similar there are
7145 * some differences that you should be aware of, especially when it comes to moving your code from Angular 1 to Angular 2:
7147 * * Angular 1 hooks are prefixed with `$`, such as `$onInit`. Angular 2 hooks are prefixed with `ng`, such as `ngOnInit`.
7148 * * Angular 1 hooks can be defined on the controller prototype or added to the controller inside its constructor.
7149 * In Angular 2 you can only define hooks on the prototype of the Component class.
7150 * * Due to the differences in change-detection, you may get many more calls to `$doCheck` in Angular 1 than you would to
7151 * `ngDoCheck` in Angular 2
7152 * * Changes to the model inside `$doCheck` will trigger new turns of the digest loop, which will cause the changes to be
7153 * propagated throughout the application.
7154 * Angular 2 does not allow the `ngDoCheck` hook to trigger a change outside of the component. It will either throw an
7155 * error or do nothing depending upon the state of `enableProdMode()`.
7157 * #### Life-cycle hook examples
7159 * This example shows how you can check for mutations to a Date object even though the identity of the object
7162 * <example name="doCheckDateExample" module="do-check-module">
7163 * <file name="app.js">
7164 * angular.module('do-check-module', [])
7165 * .component('app', {
7167 * 'Month: <input ng-model="$ctrl.month" ng-change="$ctrl.updateDate()">' +
7168 * 'Date: {{ $ctrl.date }}' +
7169 * '<test date="$ctrl.date"></test>',
7170 * controller: function() {
7171 * this.date = new Date();
7172 * this.month = this.date.getMonth();
7173 * this.updateDate = function() {
7174 * this.date.setMonth(this.month);
7178 * .component('test', {
7179 * bindings: { date: '<' },
7181 * '<pre>{{ $ctrl.log | json }}</pre>',
7182 * controller: function() {
7183 * var previousValue;
7185 * this.$doCheck = function() {
7186 * var currentValue = this.date && this.date.valueOf();
7187 * if (previousValue !== currentValue) {
7188 * this.log.push('doCheck: date mutated: ' + this.date);
7189 * previousValue = currentValue;
7195 * <file name="index.html">
7200 * This example show how you might use `$doCheck` to trigger changes in your component's inputs even if the
7201 * actual identity of the component doesn't change. (Be aware that cloning and deep equality checks on large
7202 * arrays or objects can have a negative impact on your application performance)
7204 * <example name="doCheckArrayExample" module="do-check-module">
7205 * <file name="index.html">
7206 * <div ng-init="items = []">
7207 * <button ng-click="items.push(items.length)">Add Item</button>
7208 * <button ng-click="items = []">Reset Items</button>
7209 * <pre>{{ items }}</pre>
7210 * <test items="items"></test>
7213 * <file name="app.js">
7214 * angular.module('do-check-module', [])
7215 * .component('test', {
7216 * bindings: { items: '<' },
7218 * '<pre>{{ $ctrl.log | json }}</pre>',
7219 * controller: function() {
7222 * this.$doCheck = function() {
7223 * if (this.items_ref !== this.items) {
7224 * this.log.push('doCheck: items changed');
7225 * this.items_ref = this.items;
7227 * if (!angular.equals(this.items_clone, this.items)) {
7228 * this.log.push('doCheck: items mutated');
7229 * this.items_clone = angular.copy(this.items);
7238 * ### Directive Definition Object
7240 * The directive definition object provides instructions to the {@link ng.$compile
7241 * compiler}. The attributes are:
7243 * #### `multiElement`
7244 * When this property is set to true (default is `false`), the HTML compiler will collect DOM nodes between
7245 * nodes with the attributes `directive-name-start` and `directive-name-end`, and group them
7246 * together as the directive elements. It is recommended that this feature be used on directives
7247 * which are not strictly behavioral (such as {@link ngClick}), and which
7248 * do not manipulate or replace child nodes (such as {@link ngInclude}).
7251 * When there are multiple directives defined on a single DOM element, sometimes it
7252 * is necessary to specify the order in which the directives are applied. The `priority` is used
7253 * to sort the directives before their `compile` functions get called. Priority is defined as a
7254 * number. Directives with greater numerical `priority` are compiled first. Pre-link functions
7255 * are also run in priority order, but post-link functions are run in reverse order. The order
7256 * of directives with the same priority is undefined. The default priority is `0`.
7259 * If set to true then the current `priority` will be the last set of directives
7260 * which will execute (any directives at the current priority will still execute
7261 * as the order of execution on same `priority` is undefined). Note that expressions
7262 * and other directives used in the directive's template will also be excluded from execution.
7265 * The scope property can be `false`, `true`, or an object:
7267 * * **`false` (default):** No scope will be created for the directive. The directive will use its
7270 * * **`true`:** A new child scope that prototypically inherits from its parent will be created for
7271 * the directive's element. If multiple directives on the same element request a new scope,
7272 * only one new scope is created.
7274 * * **`{...}` (an object hash):** A new "isolate" scope is created for the directive's template.
7275 * The 'isolate' scope differs from normal scope in that it does not prototypically
7276 * inherit from its parent scope. This is useful when creating reusable components, which should not
7277 * accidentally read or modify data in the parent scope. Note that an isolate scope
7278 * directive without a `template` or `templateUrl` will not apply the isolate scope
7279 * to its children elements.
7281 * The 'isolate' scope object hash defines a set of local scope properties derived from attributes on the
7282 * directive's element. These local properties are useful for aliasing values for templates. The keys in
7283 * the object hash map to the name of the property on the isolate scope; the values define how the property
7284 * is bound to the parent scope, via matching attributes on the directive's element:
7286 * * `@` or `@attr` - bind a local scope property to the value of DOM attribute. The result is
7287 * always a string since DOM attributes are strings. If no `attr` name is specified then the
7288 * attribute name is assumed to be the same as the local name. Given `<my-component
7289 * my-attr="hello {{name}}">` and the isolate scope definition `scope: { localName:'@myAttr' }`,
7290 * the directive's scope property `localName` will reflect the interpolated value of `hello
7291 * {{name}}`. As the `name` attribute changes so will the `localName` property on the directive's
7292 * scope. The `name` is read from the parent scope (not the directive's scope).
7294 * * `=` or `=attr` - set up a bidirectional binding between a local scope property and an expression
7295 * passed via the attribute `attr`. The expression is evaluated in the context of the parent scope.
7296 * If no `attr` name is specified then the attribute name is assumed to be the same as the local
7297 * name. Given `<my-component my-attr="parentModel">` and the isolate scope definition `scope: {
7298 * localModel: '=myAttr' }`, the property `localModel` on the directive's scope will reflect the
7299 * value of `parentModel` on the parent scope. Changes to `parentModel` will be reflected in
7300 * `localModel` and vice versa. Optional attributes should be marked as such with a question mark:
7301 * `=?` or `=?attr`. If the binding expression is non-assignable, or if the attribute isn't
7302 * optional and doesn't exist, an exception ({@link error/$compile/nonassign `$compile:nonassign`})
7303 * will be thrown upon discovering changes to the local value, since it will be impossible to sync
7304 * them back to the parent scope. By default, the {@link ng.$rootScope.Scope#$watch `$watch`}
7305 * method is used for tracking changes, and the equality check is based on object identity.
7306 * However, if an object literal or an array literal is passed as the binding expression, the
7307 * equality check is done by value (using the {@link angular.equals} function). It's also possible
7308 * to watch the evaluated value shallowly with {@link ng.$rootScope.Scope#$watchCollection
7309 * `$watchCollection`}: use `=*` or `=*attr` (`=*?` or `=*?attr` if the attribute is optional).
7311 * * `<` or `<attr` - set up a one-way (one-directional) binding between a local scope property and an
7312 * expression passed via the attribute `attr`. The expression is evaluated in the context of the
7313 * parent scope. If no `attr` name is specified then the attribute name is assumed to be the same as the
7314 * local name. You can also make the binding optional by adding `?`: `<?` or `<?attr`.
7316 * For example, given `<my-component my-attr="parentModel">` and directive definition of
7317 * `scope: { localModel:'<myAttr' }`, then the isolated scope property `localModel` will reflect the
7318 * value of `parentModel` on the parent scope. Any changes to `parentModel` will be reflected
7319 * in `localModel`, but changes in `localModel` will not reflect in `parentModel`. There are however
7321 * 1. one-way binding does not copy the value from the parent to the isolate scope, it simply
7322 * sets the same value. That means if your bound value is an object, changes to its properties
7323 * in the isolated scope will be reflected in the parent scope (because both reference the same object).
7324 * 2. one-way binding watches changes to the **identity** of the parent value. That means the
7325 * {@link ng.$rootScope.Scope#$watch `$watch`} on the parent value only fires if the reference
7326 * to the value has changed. In most cases, this should not be of concern, but can be important
7327 * to know if you one-way bind to an object, and then replace that object in the isolated scope.
7328 * If you now change a property of the object in your parent scope, the change will not be
7329 * propagated to the isolated scope, because the identity of the object on the parent scope
7330 * has not changed. Instead you must assign a new object.
7332 * One-way binding is useful if you do not plan to propagate changes to your isolated scope bindings
7333 * back to the parent. However, it does not make this completely impossible.
7335 * * `&` or `&attr` - provides a way to execute an expression in the context of the parent scope. If
7336 * no `attr` name is specified then the attribute name is assumed to be the same as the local name.
7337 * Given `<my-component my-attr="count = count + value">` and the isolate scope definition `scope: {
7338 * localFn:'&myAttr' }`, the isolate scope property `localFn` will point to a function wrapper for
7339 * the `count = count + value` expression. Often it's desirable to pass data from the isolated scope
7340 * via an expression to the parent scope. This can be done by passing a map of local variable names
7341 * and values into the expression wrapper fn. For example, if the expression is `increment(amount)`
7342 * then we can specify the amount value by calling the `localFn` as `localFn({amount: 22})`.
7344 * In general it's possible to apply more than one directive to one element, but there might be limitations
7345 * depending on the type of scope required by the directives. The following points will help explain these limitations.
7346 * For simplicity only two directives are taken into account, but it is also applicable for several directives:
7348 * * **no scope** + **no scope** => Two directives which don't require their own scope will use their parent's scope
7349 * * **child scope** + **no scope** => Both directives will share one single child scope
7350 * * **child scope** + **child scope** => Both directives will share one single child scope
7351 * * **isolated scope** + **no scope** => The isolated directive will use it's own created isolated scope. The other directive will use
7352 * its parent's scope
7353 * * **isolated scope** + **child scope** => **Won't work!** Only one scope can be related to one element. Therefore these directives cannot
7354 * be applied to the same element.
7355 * * **isolated scope** + **isolated scope** => **Won't work!** Only one scope can be related to one element. Therefore these directives
7356 * cannot be applied to the same element.
7359 * #### `bindToController`
7360 * This property is used to bind scope properties directly to the controller. It can be either
7361 * `true` or an object hash with the same format as the `scope` property.
7363 * When an isolate scope is used for a directive (see above), `bindToController: true` will
7364 * allow a component to have its properties bound to the controller, rather than to scope.
7366 * After the controller is instantiated, the initial values of the isolate scope bindings will be bound to the controller
7367 * properties. You can access these bindings once they have been initialized by providing a controller method called
7368 * `$onInit`, which is called after all the controllers on an element have been constructed and had their bindings
7371 * <div class="alert alert-warning">
7372 * **Deprecation warning:** although bindings for non-ES6 class controllers are currently
7373 * bound to `this` before the controller constructor is called, this use is now deprecated. Please place initialization
7374 * code that relies upon bindings inside a `$onInit` method on the controller, instead.
7377 * It is also possible to set `bindToController` to an object hash with the same format as the `scope` property.
7378 * This will set up the scope bindings to the controller directly. Note that `scope` can still be used
7379 * to define which kind of scope is created. By default, no scope is created. Use `scope: {}` to create an isolate
7380 * scope (useful for component directives).
7382 * If both `bindToController` and `scope` are defined and have object hashes, `bindToController` overrides `scope`.
7386 * Controller constructor function. The controller is instantiated before the
7387 * pre-linking phase and can be accessed by other directives (see
7388 * `require` attribute). This allows the directives to communicate with each other and augment
7389 * each other's behavior. The controller is injectable (and supports bracket notation) with the following locals:
7391 * * `$scope` - Current scope associated with the element
7392 * * `$element` - Current element
7393 * * `$attrs` - Current attributes object for the element
7394 * * `$transclude` - A transclude linking function pre-bound to the correct transclusion scope:
7395 * `function([scope], cloneLinkingFn, futureParentElement, slotName)`:
7396 * * `scope`: (optional) override the scope.
7397 * * `cloneLinkingFn`: (optional) argument to create clones of the original transcluded content.
7398 * * `futureParentElement` (optional):
7399 * * defines the parent to which the `cloneLinkingFn` will add the cloned elements.
7400 * * default: `$element.parent()` resp. `$element` for `transclude:'element'` resp. `transclude:true`.
7401 * * only needed for transcludes that are allowed to contain non html elements (e.g. SVG elements)
7402 * and when the `cloneLinkingFn` is passed,
7403 * as those elements need to created and cloned in a special way when they are defined outside their
7404 * usual containers (e.g. like `<svg>`).
7405 * * See also the `directive.templateNamespace` property.
7406 * * `slotName`: (optional) the name of the slot to transclude. If falsy (e.g. `null`, `undefined` or `''`)
7407 * then the default transclusion is provided.
7408 * The `$transclude` function also has a method on it, `$transclude.isSlotFilled(slotName)`, which returns
7409 * `true` if the specified slot contains content (i.e. one or more DOM nodes).
7412 * Require another directive and inject its controller as the fourth argument to the linking function. The
7413 * `require` property can be a string, an array or an object:
7414 * * a **string** containing the name of the directive to pass to the linking function
7415 * * an **array** containing the names of directives to pass to the linking function. The argument passed to the
7416 * linking function will be an array of controllers in the same order as the names in the `require` property
7417 * * an **object** whose property values are the names of the directives to pass to the linking function. The argument
7418 * passed to the linking function will also be an object with matching keys, whose values will hold the corresponding
7421 * If the `require` property is an object and `bindToController` is truthy, then the required controllers are
7422 * bound to the controller using the keys of the `require` property. This binding occurs after all the controllers
7423 * have been constructed but before `$onInit` is called.
7424 * If the name of the required controller is the same as the local name (the key), the name can be
7425 * omitted. For example, `{parentDir: '^^'}` is equivalent to `{parentDir: '^^parentDir'}`.
7426 * See the {@link $compileProvider#component} helper for an example of how this can be used.
7427 * If no such required directive(s) can be found, or if the directive does not have a controller, then an error is
7428 * raised (unless no link function is specified and the required controllers are not being bound to the directive
7429 * controller, in which case error checking is skipped). The name can be prefixed with:
7431 * * (no prefix) - Locate the required controller on the current element. Throw an error if not found.
7432 * * `?` - Attempt to locate the required controller or pass `null` to the `link` fn if not found.
7433 * * `^` - Locate the required controller by searching the element and its parents. Throw an error if not found.
7434 * * `^^` - Locate the required controller by searching the element's parents. Throw an error if not found.
7435 * * `?^` - Attempt to locate the required controller by searching the element and its parents or pass
7436 * `null` to the `link` fn if not found.
7437 * * `?^^` - Attempt to locate the required controller by searching the element's parents, or pass
7438 * `null` to the `link` fn if not found.
7441 * #### `controllerAs`
7442 * Identifier name for a reference to the controller in the directive's scope.
7443 * This allows the controller to be referenced from the directive template. This is especially
7444 * useful when a directive is used as component, i.e. with an `isolate` scope. It's also possible
7445 * to use it in a directive without an `isolate` / `new` scope, but you need to be aware that the
7446 * `controllerAs` reference might overwrite a property that already exists on the parent scope.
7450 * String of subset of `EACM` which restricts the directive to a specific directive
7451 * declaration style. If omitted, the defaults (elements and attributes) are used.
7453 * * `E` - Element name (default): `<my-directive></my-directive>`
7454 * * `A` - Attribute (default): `<div my-directive="exp"></div>`
7455 * * `C` - Class: `<div class="my-directive: exp;"></div>`
7456 * * `M` - Comment: `<!-- directive: my-directive exp -->`
7459 * #### `templateNamespace`
7460 * String representing the document type used by the markup in the template.
7461 * AngularJS needs this information as those elements need to be created and cloned
7462 * in a special way when they are defined outside their usual containers like `<svg>` and `<math>`.
7464 * * `html` - All root nodes in the template are HTML. Root nodes may also be
7465 * top-level elements such as `<svg>` or `<math>`.
7466 * * `svg` - The root nodes in the template are SVG elements (excluding `<math>`).
7467 * * `math` - The root nodes in the template are MathML elements (excluding `<svg>`).
7469 * If no `templateNamespace` is specified, then the namespace is considered to be `html`.
7472 * HTML markup that may:
7473 * * Replace the contents of the directive's element (default).
7474 * * Replace the directive's element itself (if `replace` is true - DEPRECATED).
7475 * * Wrap the contents of the directive's element (if `transclude` is true).
7479 * * A string. For example `<div red-on-hover>{{delete_str}}</div>`.
7480 * * A function which takes two arguments `tElement` and `tAttrs` (described in the `compile`
7481 * function api below) and returns a string value.
7484 * #### `templateUrl`
7485 * This is similar to `template` but the template is loaded from the specified URL, asynchronously.
7487 * Because template loading is asynchronous the compiler will suspend compilation of directives on that element
7488 * for later when the template has been resolved. In the meantime it will continue to compile and link
7489 * sibling and parent elements as though this element had not contained any directives.
7491 * The compiler does not suspend the entire compilation to wait for templates to be loaded because this
7492 * would result in the whole app "stalling" until all templates are loaded asynchronously - even in the
7493 * case when only one deeply nested directive has `templateUrl`.
7495 * Template loading is asynchronous even if the template has been preloaded into the {@link $templateCache}
7497 * You can specify `templateUrl` as a string representing the URL or as a function which takes two
7498 * arguments `tElement` and `tAttrs` (described in the `compile` function api below) and returns
7499 * a string value representing the url. In either case, the template URL is passed through {@link
7500 * $sce#getTrustedResourceUrl $sce.getTrustedResourceUrl}.
7503 * #### `replace` ([*DEPRECATED*!], will be removed in next major release - i.e. v2.0)
7504 * specify what the template should replace. Defaults to `false`.
7506 * * `true` - the template will replace the directive's element.
7507 * * `false` - the template will replace the contents of the directive's element.
7509 * The replacement process migrates all of the attributes / classes from the old element to the new
7510 * one. See the {@link guide/directive#template-expanding-directive
7511 * Directives Guide} for an example.
7513 * There are very few scenarios where element replacement is required for the application function,
7514 * the main one being reusable custom components that are used within SVG contexts
7515 * (because SVG doesn't work with custom elements in the DOM tree).
7518 * Extract the contents of the element where the directive appears and make it available to the directive.
7519 * The contents are compiled and provided to the directive as a **transclusion function**. See the
7520 * {@link $compile#transclusion Transclusion} section below.
7526 * function compile(tElement, tAttrs, transclude) { ... }
7529 * The compile function deals with transforming the template DOM. Since most directives do not do
7530 * template transformation, it is not used often. The compile function takes the following arguments:
7532 * * `tElement` - template element - The element where the directive has been declared. It is
7533 * safe to do template transformation on the element and child elements only.
7535 * * `tAttrs` - template attributes - Normalized list of attributes declared on this element shared
7536 * between all directive compile functions.
7538 * * `transclude` - [*DEPRECATED*!] A transclude linking function: `function(scope, cloneLinkingFn)`
7540 * <div class="alert alert-warning">
7541 * **Note:** The template instance and the link instance may be different objects if the template has
7542 * been cloned. For this reason it is **not** safe to do anything other than DOM transformations that
7543 * apply to all cloned DOM nodes within the compile function. Specifically, DOM listener registration
7544 * should be done in a linking function rather than in a compile function.
7547 * <div class="alert alert-warning">
7548 * **Note:** The compile function cannot handle directives that recursively use themselves in their
7549 * own templates or compile functions. Compiling these directives results in an infinite loop and
7550 * stack overflow errors.
7552 * This can be avoided by manually using $compile in the postLink function to imperatively compile
7553 * a directive's template instead of relying on automatic template compilation via `template` or
7554 * `templateUrl` declaration or manual compilation inside the compile function.
7557 * <div class="alert alert-danger">
7558 * **Note:** The `transclude` function that is passed to the compile function is deprecated, as it
7559 * e.g. does not know about the right outer scope. Please use the transclude function that is passed
7560 * to the link function instead.
7563 * A compile function can have a return value which can be either a function or an object.
7565 * * returning a (post-link) function - is equivalent to registering the linking function via the
7566 * `link` property of the config object when the compile function is empty.
7568 * * returning an object with function(s) registered via `pre` and `post` properties - allows you to
7569 * control when a linking function should be called during the linking phase. See info about
7570 * pre-linking and post-linking functions below.
7574 * This property is used only if the `compile` property is not defined.
7577 * function link(scope, iElement, iAttrs, controller, transcludeFn) { ... }
7580 * The link function is responsible for registering DOM listeners as well as updating the DOM. It is
7581 * executed after the template has been cloned. This is where most of the directive logic will be
7584 * * `scope` - {@link ng.$rootScope.Scope Scope} - The scope to be used by the
7585 * directive for registering {@link ng.$rootScope.Scope#$watch watches}.
7587 * * `iElement` - instance element - The element where the directive is to be used. It is safe to
7588 * manipulate the children of the element only in `postLink` function since the children have
7589 * already been linked.
7591 * * `iAttrs` - instance attributes - Normalized list of attributes declared on this element shared
7592 * between all directive linking functions.
7594 * * `controller` - the directive's required controller instance(s) - Instances are shared
7595 * among all directives, which allows the directives to use the controllers as a communication
7596 * channel. The exact value depends on the directive's `require` property:
7597 * * no controller(s) required: the directive's own controller, or `undefined` if it doesn't have one
7598 * * `string`: the controller instance
7599 * * `array`: array of controller instances
7601 * If a required controller cannot be found, and it is optional, the instance is `null`,
7602 * otherwise the {@link error:$compile:ctreq Missing Required Controller} error is thrown.
7604 * Note that you can also require the directive's own controller - it will be made available like
7605 * any other controller.
7607 * * `transcludeFn` - A transclude linking function pre-bound to the correct transclusion scope.
7608 * This is the same as the `$transclude` parameter of directive controllers,
7609 * see {@link ng.$compile#-controller- the controller section for details}.
7610 * `function([scope], cloneLinkingFn, futureParentElement)`.
7612 * #### Pre-linking function
7614 * Executed before the child elements are linked. Not safe to do DOM transformation since the
7615 * compiler linking function will fail to locate the correct elements for linking.
7617 * #### Post-linking function
7619 * Executed after the child elements are linked.
7621 * Note that child elements that contain `templateUrl` directives will not have been compiled
7622 * and linked since they are waiting for their template to load asynchronously and their own
7623 * compilation and linking has been suspended until that occurs.
7625 * It is safe to do DOM transformation in the post-linking function on elements that are not waiting
7626 * for their async templates to be resolved.
7631 * Transclusion is the process of extracting a collection of DOM elements from one part of the DOM and
7632 * copying them to another part of the DOM, while maintaining their connection to the original AngularJS
7633 * scope from where they were taken.
7635 * Transclusion is used (often with {@link ngTransclude}) to insert the
7636 * original contents of a directive's element into a specified place in the template of the directive.
7637 * The benefit of transclusion, over simply moving the DOM elements manually, is that the transcluded
7638 * content has access to the properties on the scope from which it was taken, even if the directive
7639 * has isolated scope.
7640 * See the {@link guide/directive#creating-a-directive-that-wraps-other-elements Directives Guide}.
7642 * This makes it possible for the widget to have private state for its template, while the transcluded
7643 * content has access to its originating scope.
7645 * <div class="alert alert-warning">
7646 * **Note:** When testing an element transclude directive you must not place the directive at the root of the
7647 * DOM fragment that is being compiled. See {@link guide/unit-testing#testing-transclusion-directives
7648 * Testing Transclusion Directives}.
7651 * There are three kinds of transclusion depending upon whether you want to transclude just the contents of the
7652 * directive's element, the entire element or multiple parts of the element contents:
7654 * * `true` - transclude the content (i.e. the child nodes) of the directive's element.
7655 * * `'element'` - transclude the whole of the directive's element including any directives on this
7656 * element that defined at a lower priority than this directive. When used, the `template`
7657 * property is ignored.
7658 * * **`{...}` (an object hash):** - map elements of the content onto transclusion "slots" in the template.
7660 * **Mult-slot transclusion** is declared by providing an object for the `transclude` property.
7662 * This object is a map where the keys are the name of the slot to fill and the value is an element selector
7663 * used to match the HTML to the slot. The element selector should be in normalized form (e.g. `myElement`)
7664 * and will match the standard element variants (e.g. `my-element`, `my:element`, `data-my-element`, etc).
7666 * For further information check out the guide on {@link guide/directive#matching-directives Matching Directives}
7668 * If the element selector is prefixed with a `?` then that slot is optional.
7670 * For example, the transclude object `{ slotA: '?myCustomElement' }` maps `<my-custom-element>` elements to
7671 * the `slotA` slot, which can be accessed via the `$transclude` function or via the {@link ngTransclude} directive.
7673 * Slots that are not marked as optional (`?`) will trigger a compile time error if there are no matching elements
7674 * in the transclude content. If you wish to know if an optional slot was filled with content, then you can call
7675 * `$transclude.isSlotFilled(slotName)` on the transclude function passed to the directive's link function and
7676 * injectable into the directive's controller.
7679 * #### Transclusion Functions
7681 * When a directive requests transclusion, the compiler extracts its contents and provides a **transclusion
7682 * function** to the directive's `link` function and `controller`. This transclusion function is a special
7683 * **linking function** that will return the compiled contents linked to a new transclusion scope.
7685 * <div class="alert alert-info">
7686 * If you are just using {@link ngTransclude} then you don't need to worry about this function, since
7687 * ngTransclude will deal with it for us.
7690 * If you want to manually control the insertion and removal of the transcluded content in your directive
7691 * then you must use this transclude function. When you call a transclude function it returns a a jqLite/JQuery
7692 * object that contains the compiled DOM, which is linked to the correct transclusion scope.
7694 * When you call a transclusion function you can pass in a **clone attach function**. This function accepts
7695 * two parameters, `function(clone, scope) { ... }`, where the `clone` is a fresh compiled copy of your transcluded
7696 * content and the `scope` is the newly created transclusion scope, which the clone will be linked to.
7698 * <div class="alert alert-info">
7699 * **Best Practice**: Always provide a `cloneFn` (clone attach function) when you call a transclude function
7700 * since you then get a fresh clone of the original DOM and also have access to the new transclusion scope.
7703 * It is normal practice to attach your transcluded content (`clone`) to the DOM inside your **clone
7704 * attach function**:
7707 * var transcludedContent, transclusionScope;
7709 * $transclude(function(clone, scope) {
7710 * element.append(clone);
7711 * transcludedContent = clone;
7712 * transclusionScope = scope;
7716 * Later, if you want to remove the transcluded content from your DOM then you should also destroy the
7717 * associated transclusion scope:
7720 * transcludedContent.remove();
7721 * transclusionScope.$destroy();
7724 * <div class="alert alert-info">
7725 * **Best Practice**: if you intend to add and remove transcluded content manually in your directive
7726 * (by calling the transclude function to get the DOM and calling `element.remove()` to remove it),
7727 * then you are also responsible for calling `$destroy` on the transclusion scope.
7730 * The built-in DOM manipulation directives, such as {@link ngIf}, {@link ngSwitch} and {@link ngRepeat}
7731 * automatically destroy their transcluded clones as necessary so you do not need to worry about this if
7732 * you are simply using {@link ngTransclude} to inject the transclusion into your directive.
7735 * #### Transclusion Scopes
7737 * When you call a transclude function it returns a DOM fragment that is pre-bound to a **transclusion
7738 * scope**. This scope is special, in that it is a child of the directive's scope (and so gets destroyed
7739 * when the directive's scope gets destroyed) but it inherits the properties of the scope from which it
7742 * For example consider a directive that uses transclusion and isolated scope. The DOM hierarchy might look
7748 * <div transclusion>
7754 * The `$parent` scope hierarchy will look like this:
7762 * but the scopes will inherit prototypically from different scopes to their `$parent`.
7773 * The {@link ng.$compile.directive.Attributes Attributes} object - passed as a parameter in the
7774 * `link()` or `compile()` functions. It has a variety of uses.
7776 * * *Accessing normalized attribute names:* Directives like 'ngBind' can be expressed in many ways:
7777 * 'ng:bind', `data-ng-bind`, or 'x-ng-bind'. The attributes object allows for normalized access
7778 * to the attributes.
7780 * * *Directive inter-communication:* All directives share the same instance of the attributes
7781 * object which allows the directives to use the attributes object as inter directive
7784 * * *Supports interpolation:* Interpolation attributes are assigned to the attribute object
7785 * allowing other directives to read the interpolated value.
7787 * * *Observing interpolated attributes:* Use `$observe` to observe the value changes of attributes
7788 * that contain interpolation (e.g. `src="{{bar}}"`). Not only is this very efficient but it's also
7789 * the only way to easily get the actual value because during the linking phase the interpolation
7790 * hasn't been evaluated yet and so the value is at this time set to `undefined`.
7793 * function linkingFn(scope, elm, attrs, ctrl) {
7794 * // get the attribute value
7795 * console.log(attrs.ngModel);
7797 * // change the attribute
7798 * attrs.$set('ngModel', 'new value');
7800 * // observe changes to interpolated attribute
7801 * attrs.$observe('ngModel', function(value) {
7802 * console.log('ngModel has changed value to ' + value);
7809 * <div class="alert alert-warning">
7810 * **Note**: Typically directives are registered with `module.directive`. The example below is
7811 * to illustrate how `$compile` works.
7814 <example module="compileExample" name="compile">
7815 <file name="index.html">
7817 angular.module('compileExample', [], function($compileProvider) {
7818 // configure new 'compile' directive by passing a directive
7819 // factory function. The factory function injects the '$compile'
7820 $compileProvider.directive('compile', function($compile) {
7821 // directive factory creates a link function
7822 return function(scope, element, attrs) {
7825 // watch the 'compile' expression for changes
7826 return scope.$eval(attrs.compile);
7829 // when the 'compile' expression changes
7830 // assign it into the current DOM
7831 element.html(value);
7833 // compile the new DOM and link it to the current
7835 // NOTE: we only compile .childNodes so that
7836 // we don't get into infinite loop compiling ourselves
7837 $compile(element.contents())(scope);
7843 .controller('GreeterController', ['$scope', function($scope) {
7844 $scope.name = 'Angular';
7845 $scope.html = 'Hello {{name}}';
7848 <div ng-controller="GreeterController">
7849 <input ng-model="name"> <br/>
7850 <textarea ng-model="html"></textarea> <br/>
7851 <div compile="html"></div>
7854 <file name="protractor.js" type="protractor">
7855 it('should auto compile', function() {
7856 var textarea = $('textarea');
7857 var output = $('div[compile]');
7858 // The initial state reads 'Hello Angular'.
7859 expect(output.getText()).toBe('Hello Angular');
7861 textarea.sendKeys('{{name}}!');
7862 expect(output.getText()).toBe('Angular!');
7869 * @param {string|DOMElement} element Element or HTML string to compile into a template function.
7870 * @param {function(angular.Scope, cloneAttachFn=)} transclude function available to directives - DEPRECATED.
7872 * <div class="alert alert-danger">
7873 * **Note:** Passing a `transclude` function to the $compile function is deprecated, as it
7874 * e.g. will not use the right outer scope. Please pass the transclude function as a
7875 * `parentBoundTranscludeFn` to the link function instead.
7878 * @param {number} maxPriority only apply directives lower than given priority (Only effects the
7879 * root element(s), not their children)
7880 * @returns {function(scope, cloneAttachFn=, options=)} a link function which is used to bind template
7881 * (a DOM element/tree) to a scope. Where:
7883 * * `scope` - A {@link ng.$rootScope.Scope Scope} to bind to.
7884 * * `cloneAttachFn` - If `cloneAttachFn` is provided, then the link function will clone the
7885 * `template` and call the `cloneAttachFn` function allowing the caller to attach the
7886 * cloned elements to the DOM document at the appropriate place. The `cloneAttachFn` is
7887 * called as: <br/> `cloneAttachFn(clonedElement, scope)` where:
7889 * * `clonedElement` - is a clone of the original `element` passed into the compiler.
7890 * * `scope` - is the current scope with which the linking function is working with.
7892 * * `options` - An optional object hash with linking options. If `options` is provided, then the following
7893 * keys may be used to control linking behavior:
7895 * * `parentBoundTranscludeFn` - the transclude function made available to
7896 * directives; if given, it will be passed through to the link functions of
7897 * directives found in `element` during compilation.
7898 * * `transcludeControllers` - an object hash with keys that map controller names
7899 * to a hash with the key `instance`, which maps to the controller instance;
7900 * if given, it will make the controllers available to directives on the compileNode:
7904 * instance: parentControllerInstance
7908 * * `futureParentElement` - defines the parent to which the `cloneAttachFn` will add
7909 * the cloned elements; only needed for transcludes that are allowed to contain non html
7910 * elements (e.g. SVG elements). See also the directive.controller property.
7912 * Calling the linking function returns the element of the template. It is either the original
7913 * element passed in, or the clone of the element if the `cloneAttachFn` is provided.
7915 * After linking the view is not updated until after a call to $digest which typically is done by
7916 * Angular automatically.
7918 * If you need access to the bound view, there are two ways to do it:
7920 * - If you are not asking the linking function to clone the template, create the DOM element(s)
7921 * before you send them to the compiler and keep this reference around.
7923 * var element = $compile('<p>{{total}}</p>')(scope);
7926 * - if on the other hand, you need the element to be cloned, the view reference from the original
7927 * example would not point to the clone, but rather to the original template that was cloned. In
7928 * this case, you can access the clone via the cloneAttachFn:
7930 * var templateElement = angular.element('<p>{{total}}</p>'),
7933 * var clonedElement = $compile(templateElement)(scope, function(clonedElement, scope) {
7934 * //attach the clone to DOM document at the right place
7937 * //now we have reference to the cloned DOM via `clonedElement`
7941 * For information on how the compiler works, see the
7942 * {@link guide/compiler Angular HTML Compiler} section of the Developer Guide.
7946 * ### Double Compilation
7948 Double compilation occurs when an already compiled part of the DOM gets
7949 compiled again. This is an undesired effect and can lead to misbehaving directives, performance issues,
7950 and memory leaks. Refer to the Compiler Guide {@link guide/compiler#double-compilation-and-how-to-avoid-it
7951 section on double compilation} for an in-depth explanation and ways to avoid it.
7955 var $compileMinErr = minErr('$compile');
7957 function UNINITIALIZED_VALUE() {}
7958 var _UNINITIALIZED_VALUE = new UNINITIALIZED_VALUE();
7962 * @name $compileProvider
7966 $CompileProvider.$inject = ['$provide', '$$sanitizeUriProvider'];
7968 function $CompileProvider($provide, $$sanitizeUriProvider) {
7969 var hasDirectives = {},
7970 Suffix = 'Directive',
7971 COMMENT_DIRECTIVE_REGEXP = /^\s*directive:\s*([\w-]+)\s+(.*)$/,
7972 CLASS_DIRECTIVE_REGEXP = /(([\w-]+)(?::([^;]+))?;?)/,
7973 ALL_OR_NOTHING_ATTRS = makeMap('ngSrc,ngSrcset,src,srcset'),
7974 REQUIRE_PREFIX_REGEXP = /^(?:(\^\^?)?(\?)?(\^\^?)?)?/;
7976 // Ref: http://developers.whatwg.org/webappapis.html#event-handler-idl-attributes
7977 // The assumption is that future DOM event attribute names will begin with
7978 // 'on' and be composed of only English letters.
7979 var EVENT_HANDLER_ATTR_REGEXP = /^(on[a-z]+|formaction)$/;
7980 var bindingCache = createMap();
7982 function parseIsolateBindings(scope, directiveName, isController) {
7983 var LOCAL_REGEXP = /^\s*([@&<]|=(\*?))(\??)\s*([\w$]*)\s*$/;
7985 var bindings = createMap();
7987 forEach(scope, function(definition, scopeName) {
7988 if (definition in bindingCache) {
7989 bindings[scopeName] = bindingCache[definition];
7992 var match = definition.match(LOCAL_REGEXP);
7995 throw $compileMinErr('iscp',
7996 'Invalid {3} for directive \'{0}\'.' +
7997 ' Definition: {... {1}: \'{2}\' ...}',
7998 directiveName, scopeName, definition,
7999 (isController ? 'controller bindings definition' :
8000 'isolate scope definition'));
8003 bindings[scopeName] = {
8005 collection: match[2] === '*',
8006 optional: match[3] === '?',
8007 attrName: match[4] || scopeName
8010 bindingCache[definition] = bindings[scopeName];
8017 function parseDirectiveBindings(directive, directiveName) {
8020 bindToController: null
8022 if (isObject(directive.scope)) {
8023 if (directive.bindToController === true) {
8024 bindings.bindToController = parseIsolateBindings(directive.scope,
8025 directiveName, true);
8026 bindings.isolateScope = {};
8028 bindings.isolateScope = parseIsolateBindings(directive.scope,
8029 directiveName, false);
8032 if (isObject(directive.bindToController)) {
8033 bindings.bindToController =
8034 parseIsolateBindings(directive.bindToController, directiveName, true);
8036 if (bindings.bindToController && !directive.controller) {
8037 // There is no controller
8038 throw $compileMinErr('noctrl',
8039 'Cannot bind to controller without directive \'{0}\'s controller.',
8045 function assertValidDirectiveName(name) {
8046 var letter = name.charAt(0);
8047 if (!letter || letter !== lowercase(letter)) {
8048 throw $compileMinErr('baddir', 'Directive/Component name \'{0}\' is invalid. The first character must be a lowercase letter', name);
8050 if (name !== name.trim()) {
8051 throw $compileMinErr('baddir',
8052 'Directive/Component name \'{0}\' is invalid. The name should not contain leading or trailing whitespaces',
8057 function getDirectiveRequire(directive) {
8058 var require = directive.require || (directive.controller && directive.name);
8060 if (!isArray(require) && isObject(require)) {
8061 forEach(require, function(value, key) {
8062 var match = value.match(REQUIRE_PREFIX_REGEXP);
8063 var name = value.substring(match[0].length);
8064 if (!name) require[key] = match[0] + key;
8071 function getDirectiveRestrict(restrict, name) {
8072 if (restrict && !(isString(restrict) && /[EACM]/.test(restrict))) {
8073 throw $compileMinErr('badrestrict',
8074 'Restrict property \'{0}\' of directive \'{1}\' is invalid',
8079 return restrict || 'EA';
8084 * @name $compileProvider#directive
8088 * Register a new directive with the compiler.
8090 * @param {string|Object} name Name of the directive in camel-case (i.e. <code>ngBind</code> which
8091 * will match as <code>ng-bind</code>), or an object map of directives where the keys are the
8092 * names and the values are the factories.
8093 * @param {Function|Array} directiveFactory An injectable directive factory function. See the
8094 * {@link guide/directive directive guide} and the {@link $compile compile API} for more info.
8095 * @returns {ng.$compileProvider} Self for chaining.
8097 this.directive = function registerDirective(name, directiveFactory) {
8098 assertArg(name, 'name');
8099 assertNotHasOwnProperty(name, 'directive');
8100 if (isString(name)) {
8101 assertValidDirectiveName(name);
8102 assertArg(directiveFactory, 'directiveFactory');
8103 if (!hasDirectives.hasOwnProperty(name)) {
8104 hasDirectives[name] = [];
8105 $provide.factory(name + Suffix, ['$injector', '$exceptionHandler',
8106 function($injector, $exceptionHandler) {
8107 var directives = [];
8108 forEach(hasDirectives[name], function(directiveFactory, index) {
8110 var directive = $injector.invoke(directiveFactory);
8111 if (isFunction(directive)) {
8112 directive = { compile: valueFn(directive) };
8113 } else if (!directive.compile && directive.link) {
8114 directive.compile = valueFn(directive.link);
8116 directive.priority = directive.priority || 0;
8117 directive.index = index;
8118 directive.name = directive.name || name;
8119 directive.require = getDirectiveRequire(directive);
8120 directive.restrict = getDirectiveRestrict(directive.restrict, name);
8121 directive.$$moduleName = directiveFactory.$$moduleName;
8122 directives.push(directive);
8124 $exceptionHandler(e);
8130 hasDirectives[name].push(directiveFactory);
8132 forEach(name, reverseParams(registerDirective));
8139 * @name $compileProvider#component
8141 * @param {string} name Name of the component in camelCase (i.e. `myComp` which will match `<my-comp>`)
8142 * @param {Object} options Component definition object (a simplified
8143 * {@link ng.$compile#directive-definition-object directive definition object}),
8144 * with the following properties (all optional):
8146 * - `controller` – `{(string|function()=}` – controller constructor function that should be
8147 * associated with newly created scope or the name of a {@link ng.$compile#-controller-
8148 * registered controller} if passed as a string. An empty `noop` function by default.
8149 * - `controllerAs` – `{string=}` – identifier name for to reference the controller in the component's scope.
8150 * If present, the controller will be published to scope under the `controllerAs` name.
8151 * If not present, this will default to be `$ctrl`.
8152 * - `template` – `{string=|function()=}` – html template as a string or a function that
8153 * returns an html template as a string which should be used as the contents of this component.
8154 * Empty string by default.
8156 * If `template` is a function, then it is {@link auto.$injector#invoke injected} with
8157 * the following locals:
8159 * - `$element` - Current element
8160 * - `$attrs` - Current attributes object for the element
8162 * - `templateUrl` – `{string=|function()=}` – path or function that returns a path to an html
8163 * template that should be used as the contents of this component.
8165 * If `templateUrl` is a function, then it is {@link auto.$injector#invoke injected} with
8166 * the following locals:
8168 * - `$element` - Current element
8169 * - `$attrs` - Current attributes object for the element
8171 * - `bindings` – `{object=}` – defines bindings between DOM attributes and component properties.
8172 * Component properties are always bound to the component controller and not to the scope.
8173 * See {@link ng.$compile#-bindtocontroller- `bindToController`}.
8174 * - `transclude` – `{boolean=}` – whether {@link $compile#transclusion content transclusion} is enabled.
8175 * Disabled by default.
8176 * - `require` - `{Object<string, string>=}` - requires the controllers of other directives and binds them to
8177 * this component's controller. The object keys specify the property names under which the required
8178 * controllers (object values) will be bound. See {@link ng.$compile#-require- `require`}.
8179 * - `$...` – additional properties to attach to the directive factory function and the controller
8180 * constructor function. (This is used by the component router to annotate)
8182 * @returns {ng.$compileProvider} the compile provider itself, for chaining of function calls.
8184 * Register a **component definition** with the compiler. This is a shorthand for registering a special
8185 * type of directive, which represents a self-contained UI component in your application. Such components
8186 * are always isolated (i.e. `scope: {}`) and are always restricted to elements (i.e. `restrict: 'E'`).
8188 * Component definitions are very simple and do not require as much configuration as defining general
8189 * directives. Component definitions usually consist only of a template and a controller backing it.
8191 * In order to make the definition easier, components enforce best practices like use of `controllerAs`,
8192 * `bindToController`. They always have **isolate scope** and are restricted to elements.
8194 * Here are a few examples of how you would usually define components:
8197 * var myMod = angular.module(...);
8198 * myMod.component('myComp', {
8199 * template: '<div>My name is {{$ctrl.name}}</div>',
8200 * controller: function() {
8201 * this.name = 'shahar';
8205 * myMod.component('myComp', {
8206 * template: '<div>My name is {{$ctrl.name}}</div>',
8207 * bindings: {name: '@'}
8210 * myMod.component('myComp', {
8211 * templateUrl: 'views/my-comp.html',
8212 * controller: 'MyCtrl',
8213 * controllerAs: 'ctrl',
8214 * bindings: {name: '@'}
8218 * For more examples, and an in-depth guide, see the {@link guide/component component guide}.
8221 * See also {@link ng.$compileProvider#directive $compileProvider.directive()}.
8223 this.component = function registerComponent(name, options) {
8224 var controller = options.controller || function() {};
8226 function factory($injector) {
8227 function makeInjectable(fn) {
8228 if (isFunction(fn) || isArray(fn)) {
8229 return /** @this */ function(tElement, tAttrs) {
8230 return $injector.invoke(fn, this, {$element: tElement, $attrs: tAttrs});
8237 var template = (!options.template && !options.templateUrl ? '' : options.template);
8239 controller: controller,
8240 controllerAs: identifierForController(options.controller) || options.controllerAs || '$ctrl',
8241 template: makeInjectable(template),
8242 templateUrl: makeInjectable(options.templateUrl),
8243 transclude: options.transclude,
8245 bindToController: options.bindings || {},
8247 require: options.require
8250 // Copy annotations (starting with $) over to the DDO
8251 forEach(options, function(val, key) {
8252 if (key.charAt(0) === '$') ddo[key] = val;
8258 // TODO(pete) remove the following `forEach` before we release 1.6.0
8259 // The component-router@0.2.0 looks for the annotations on the controller constructor
8260 // Nothing in Angular looks for annotations on the factory function but we can't remove
8261 // it from 1.5.x yet.
8263 // Copy any annotation properties (starting with $) over to the factory and controller constructor functions
8264 // These could be used by libraries such as the new component router
8265 forEach(options, function(val, key) {
8266 if (key.charAt(0) === '$') {
8268 // Don't try to copy over annotations to named controller
8269 if (isFunction(controller)) controller[key] = val;
8273 factory.$inject = ['$injector'];
8275 return this.directive(name, factory);
8281 * @name $compileProvider#aHrefSanitizationWhitelist
8285 * Retrieves or overrides the default regular expression that is used for whitelisting of safe
8286 * urls during a[href] sanitization.
8288 * The sanitization is a security measure aimed at preventing XSS attacks via html links.
8290 * Any url about to be assigned to a[href] via data-binding is first normalized and turned into
8291 * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist`
8292 * regular expression. If a match is found, the original url is written into the dom. Otherwise,
8293 * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
8295 * @param {RegExp=} regexp New regexp to whitelist urls with.
8296 * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
8297 * chaining otherwise.
8299 this.aHrefSanitizationWhitelist = function(regexp) {
8300 if (isDefined(regexp)) {
8301 $$sanitizeUriProvider.aHrefSanitizationWhitelist(regexp);
8304 return $$sanitizeUriProvider.aHrefSanitizationWhitelist();
8311 * @name $compileProvider#imgSrcSanitizationWhitelist
8315 * Retrieves or overrides the default regular expression that is used for whitelisting of safe
8316 * urls during img[src] sanitization.
8318 * The sanitization is a security measure aimed at prevent XSS attacks via html links.
8320 * Any url about to be assigned to img[src] via data-binding is first normalized and turned into
8321 * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist`
8322 * regular expression. If a match is found, the original url is written into the dom. Otherwise,
8323 * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
8325 * @param {RegExp=} regexp New regexp to whitelist urls with.
8326 * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
8327 * chaining otherwise.
8329 this.imgSrcSanitizationWhitelist = function(regexp) {
8330 if (isDefined(regexp)) {
8331 $$sanitizeUriProvider.imgSrcSanitizationWhitelist(regexp);
8334 return $$sanitizeUriProvider.imgSrcSanitizationWhitelist();
8340 * @name $compileProvider#debugInfoEnabled
8342 * @param {boolean=} enabled update the debugInfoEnabled state if provided, otherwise just return the
8343 * current debugInfoEnabled state
8344 * @returns {*} current value if used as getter or itself (chaining) if used as setter
8349 * Call this method to enable/disable various debug runtime information in the compiler such as adding
8350 * binding information and a reference to the current scope on to DOM elements.
8351 * If enabled, the compiler will add the following to DOM elements that have been bound to the scope
8352 * * `ng-binding` CSS class
8353 * * `$binding` data property containing an array of the binding expressions
8355 * You may want to disable this in production for a significant performance boost. See
8356 * {@link guide/production#disabling-debug-data Disabling Debug Data} for more.
8358 * The default value is true.
8360 var debugInfoEnabled = true;
8361 this.debugInfoEnabled = function(enabled) {
8362 if (isDefined(enabled)) {
8363 debugInfoEnabled = enabled;
8366 return debugInfoEnabled;
8371 * @name $compileProvider#preAssignBindingsEnabled
8373 * @param {boolean=} enabled update the preAssignBindingsEnabled state if provided, otherwise just return the
8374 * current preAssignBindingsEnabled state
8375 * @returns {*} current value if used as getter or itself (chaining) if used as setter
8380 * Call this method to enable/disable whether directive controllers are assigned bindings before
8381 * calling the controller's constructor.
8382 * If enabled (true), the compiler assigns the value of each of the bindings to the
8383 * properties of the controller object before the constructor of this object is called.
8385 * If disabled (false), the compiler calls the constructor first before assigning bindings.
8387 * The default value is true in Angular 1.5.x but will switch to false in Angular 1.6.x.
8389 var preAssignBindingsEnabled = false;
8390 this.preAssignBindingsEnabled = function(enabled) {
8391 if (isDefined(enabled)) {
8392 preAssignBindingsEnabled = enabled;
8395 return preAssignBindingsEnabled;
8402 * @name $compileProvider#onChangesTtl
8405 * Sets the number of times `$onChanges` hooks can trigger new changes before giving up and
8406 * assuming that the model is unstable.
8408 * The current default is 10 iterations.
8410 * In complex applications it's possible that dependencies between `$onChanges` hooks and bindings will result
8411 * in several iterations of calls to these hooks. However if an application needs more than the default 10
8412 * iterations to stabilize then you should investigate what is causing the model to continuously change during
8413 * the `$onChanges` hook execution.
8415 * Increasing the TTL could have performance implications, so you should not change it without proper justification.
8417 * @param {number} limit The number of `$onChanges` hook iterations.
8418 * @returns {number|object} the current limit (or `this` if called as a setter for chaining)
8420 this.onChangesTtl = function(value) {
8421 if (arguments.length) {
8428 var commentDirectivesEnabledConfig = true;
8431 * @name $compileProvider#commentDirectivesEnabled
8434 * It indicates to the compiler
8435 * whether or not directives on comments should be compiled.
8436 * Defaults to `true`.
8438 * Calling this function with false disables the compilation of directives
8439 * on comments for the whole application.
8440 * This results in a compilation performance gain,
8441 * as the compiler doesn't have to check comments when looking for directives.
8442 * This should however only be used if you are sure that no comment directives are used in
8443 * the application (including any 3rd party directives).
8445 * @param {boolean} enabled `false` if the compiler may ignore directives on comments
8446 * @returns {boolean|object} the current value (or `this` if called as a setter for chaining)
8448 this.commentDirectivesEnabled = function(value) {
8449 if (arguments.length) {
8450 commentDirectivesEnabledConfig = value;
8453 return commentDirectivesEnabledConfig;
8457 var cssClassDirectivesEnabledConfig = true;
8460 * @name $compileProvider#cssClassDirectivesEnabled
8463 * It indicates to the compiler
8464 * whether or not directives on element classes should be compiled.
8465 * Defaults to `true`.
8467 * Calling this function with false disables the compilation of directives
8468 * on element classes for the whole application.
8469 * This results in a compilation performance gain,
8470 * as the compiler doesn't have to check element classes when looking for directives.
8471 * This should however only be used if you are sure that no class directives are used in
8472 * the application (including any 3rd party directives).
8474 * @param {boolean} enabled `false` if the compiler may ignore directives on element classes
8475 * @returns {boolean|object} the current value (or `this` if called as a setter for chaining)
8477 this.cssClassDirectivesEnabled = function(value) {
8478 if (arguments.length) {
8479 cssClassDirectivesEnabledConfig = value;
8482 return cssClassDirectivesEnabledConfig;
8486 '$injector', '$interpolate', '$exceptionHandler', '$templateRequest', '$parse',
8487 '$controller', '$rootScope', '$sce', '$animate', '$$sanitizeUri',
8488 function($injector, $interpolate, $exceptionHandler, $templateRequest, $parse,
8489 $controller, $rootScope, $sce, $animate, $$sanitizeUri) {
8491 var SIMPLE_ATTR_NAME = /^\w/;
8492 var specialAttrHolder = window.document.createElement('div');
8495 var commentDirectivesEnabled = commentDirectivesEnabledConfig;
8496 var cssClassDirectivesEnabled = cssClassDirectivesEnabledConfig;
8499 var onChangesTtl = TTL;
8500 // The onChanges hooks should all be run together in a single digest
8501 // When changes occur, the call to trigger their hooks will be added to this queue
8504 // This function is called in a $$postDigest to trigger all the onChanges hooks in a single digest
8505 function flushOnChangesQueue() {
8507 if (!(--onChangesTtl)) {
8508 // We have hit the TTL limit so reset everything
8509 onChangesQueue = undefined;
8510 throw $compileMinErr('infchng', '{0} $onChanges() iterations reached. Aborting!\n', TTL);
8512 // We must run this hook in an apply since the $$postDigest runs outside apply
8513 $rootScope.$apply(function() {
8515 for (var i = 0, ii = onChangesQueue.length; i < ii; ++i) {
8517 onChangesQueue[i]();
8522 // Reset the queue to trigger a new schedule next time there is a change
8523 onChangesQueue = undefined;
8524 if (errors.length) {
8534 function Attributes(element, attributesToCopy) {
8535 if (attributesToCopy) {
8536 var keys = Object.keys(attributesToCopy);
8539 for (i = 0, l = keys.length; i < l; i++) {
8541 this[key] = attributesToCopy[key];
8547 this.$$element = element;
8550 Attributes.prototype = {
8553 * @name $compile.directive.Attributes#$normalize
8557 * Converts an attribute name (e.g. dash/colon/underscore-delimited string, optionally prefixed with `x-` or
8558 * `data-`) to its normalized, camelCase form.
8560 * Also there is special case for Moz prefix starting with upper case letter.
8562 * For further information check out the guide on {@link guide/directive#matching-directives Matching Directives}
8564 * @param {string} name Name to normalize
8566 $normalize: directiveNormalize,
8571 * @name $compile.directive.Attributes#$addClass
8575 * Adds the CSS class value specified by the classVal parameter to the element. If animations
8576 * are enabled then an animation will be triggered for the class addition.
8578 * @param {string} classVal The className value that will be added to the element
8580 $addClass: function(classVal) {
8581 if (classVal && classVal.length > 0) {
8582 $animate.addClass(this.$$element, classVal);
8588 * @name $compile.directive.Attributes#$removeClass
8592 * Removes the CSS class value specified by the classVal parameter from the element. If
8593 * animations are enabled then an animation will be triggered for the class removal.
8595 * @param {string} classVal The className value that will be removed from the element
8597 $removeClass: function(classVal) {
8598 if (classVal && classVal.length > 0) {
8599 $animate.removeClass(this.$$element, classVal);
8605 * @name $compile.directive.Attributes#$updateClass
8609 * Adds and removes the appropriate CSS class values to the element based on the difference
8610 * between the new and old CSS class values (specified as newClasses and oldClasses).
8612 * @param {string} newClasses The current CSS className value
8613 * @param {string} oldClasses The former CSS className value
8615 $updateClass: function(newClasses, oldClasses) {
8616 var toAdd = tokenDifference(newClasses, oldClasses);
8617 if (toAdd && toAdd.length) {
8618 $animate.addClass(this.$$element, toAdd);
8621 var toRemove = tokenDifference(oldClasses, newClasses);
8622 if (toRemove && toRemove.length) {
8623 $animate.removeClass(this.$$element, toRemove);
8628 * Set a normalized attribute on the element in a way such that all directives
8629 * can share the attribute. This function properly handles boolean attributes.
8630 * @param {string} key Normalized key. (ie ngAttribute)
8631 * @param {string|boolean} value The value to set. If `null` attribute will be deleted.
8632 * @param {boolean=} writeAttr If false, does not write the value to DOM element attribute.
8634 * @param {string=} attrName Optional none normalized name. Defaults to key.
8636 $set: function(key, value, writeAttr, attrName) {
8637 // TODO: decide whether or not to throw an error if "class"
8638 //is set through this function since it may cause $updateClass to
8641 var node = this.$$element[0],
8642 booleanKey = getBooleanAttrName(node, key),
8643 aliasedKey = getAliasedAttrName(key),
8648 this.$$element.prop(key, value);
8649 attrName = booleanKey;
8650 } else if (aliasedKey) {
8651 this[aliasedKey] = value;
8652 observer = aliasedKey;
8657 // translate normalized key to actual key
8659 this.$attr[key] = attrName;
8661 attrName = this.$attr[key];
8663 this.$attr[key] = attrName = snake_case(key, '-');
8667 nodeName = nodeName_(this.$$element);
8669 if ((nodeName === 'a' && (key === 'href' || key === 'xlinkHref')) ||
8670 (nodeName === 'img' && key === 'src')) {
8671 // sanitize a[href] and img[src] values
8672 this[key] = value = $$sanitizeUri(value, key === 'src');
8673 } else if (nodeName === 'img' && key === 'srcset' && isDefined(value)) {
8674 // sanitize img[srcset] values
8677 // first check if there are spaces because it's not the same pattern
8678 var trimmedSrcset = trim(value);
8679 // ( 999x ,| 999w ,| ,|, )
8680 var srcPattern = /(\s+\d+x\s*,|\s+\d+w\s*,|\s+,|,\s+)/;
8681 var pattern = /\s/.test(trimmedSrcset) ? srcPattern : /(,)/;
8683 // split srcset into tuple of uri and descriptor except for the last item
8684 var rawUris = trimmedSrcset.split(pattern);
8687 var nbrUrisWith2parts = Math.floor(rawUris.length / 2);
8688 for (var i = 0; i < nbrUrisWith2parts; i++) {
8689 var innerIdx = i * 2;
8691 result += $$sanitizeUri(trim(rawUris[innerIdx]), true);
8692 // add the descriptor
8693 result += (' ' + trim(rawUris[innerIdx + 1]));
8696 // split the last item into uri and descriptor
8697 var lastTuple = trim(rawUris[i * 2]).split(/\s/);
8699 // sanitize the last uri
8700 result += $$sanitizeUri(trim(lastTuple[0]), true);
8702 // and add the last descriptor if any
8703 if (lastTuple.length === 2) {
8704 result += (' ' + trim(lastTuple[1]));
8706 this[key] = value = result;
8709 if (writeAttr !== false) {
8710 if (value === null || isUndefined(value)) {
8711 this.$$element.removeAttr(attrName);
8713 if (SIMPLE_ATTR_NAME.test(attrName)) {
8714 this.$$element.attr(attrName, value);
8716 setSpecialAttr(this.$$element[0], attrName, value);
8722 var $$observers = this.$$observers;
8724 forEach($$observers[observer], function(fn) {
8728 $exceptionHandler(e);
8737 * @name $compile.directive.Attributes#$observe
8741 * Observes an interpolated attribute.
8743 * The observer function will be invoked once during the next `$digest` following
8744 * compilation. The observer is then invoked whenever the interpolated value
8747 * @param {string} key Normalized key. (ie ngAttribute) .
8748 * @param {function(interpolatedValue)} fn Function that will be called whenever
8749 the interpolated value of the attribute changes.
8750 * See the {@link guide/interpolation#how-text-and-attribute-bindings-work Interpolation
8751 * guide} for more info.
8752 * @returns {function()} Returns a deregistration function for this observer.
8754 $observe: function(key, fn) {
8756 $$observers = (attrs.$$observers || (attrs.$$observers = createMap())),
8757 listeners = ($$observers[key] || ($$observers[key] = []));
8760 $rootScope.$evalAsync(function() {
8761 if (!listeners.$$inter && attrs.hasOwnProperty(key) && !isUndefined(attrs[key])) {
8762 // no one registered attribute interpolation function, so lets call it manually
8768 arrayRemove(listeners, fn);
8773 function setSpecialAttr(element, attrName, value) {
8774 // Attributes names that do not start with letters (such as `(click)`) cannot be set using `setAttribute`
8775 // so we have to jump through some hoops to get such an attribute
8776 // https://github.com/angular/angular.js/pull/13318
8777 specialAttrHolder.innerHTML = '<span ' + attrName + '>';
8778 var attributes = specialAttrHolder.firstChild.attributes;
8779 var attribute = attributes[0];
8780 // We have to remove the attribute from its container element before we can add it to the destination element
8781 attributes.removeNamedItem(attribute.name);
8782 attribute.value = value;
8783 element.attributes.setNamedItem(attribute);
8786 function safeAddClass($element, className) {
8788 $element.addClass(className);
8790 // ignore, since it means that we are trying to set class on
8791 // SVG element, where class name is read-only.
8796 var startSymbol = $interpolate.startSymbol(),
8797 endSymbol = $interpolate.endSymbol(),
8798 denormalizeTemplate = (startSymbol === '{{' && endSymbol === '}}')
8800 : function denormalizeTemplate(template) {
8801 return template.replace(/\{\{/g, startSymbol).replace(/}}/g, endSymbol);
8803 NG_ATTR_BINDING = /^ngAttr[A-Z]/;
8804 var MULTI_ELEMENT_DIR_RE = /^(.+)Start$/;
8806 compile.$$addBindingInfo = debugInfoEnabled ? function $$addBindingInfo($element, binding) {
8807 var bindings = $element.data('$binding') || [];
8809 if (isArray(binding)) {
8810 bindings = bindings.concat(binding);
8812 bindings.push(binding);
8815 $element.data('$binding', bindings);
8818 compile.$$addBindingClass = debugInfoEnabled ? function $$addBindingClass($element) {
8819 safeAddClass($element, 'ng-binding');
8822 compile.$$addScopeInfo = debugInfoEnabled ? function $$addScopeInfo($element, scope, isolated, noTemplate) {
8823 var dataName = isolated ? (noTemplate ? '$isolateScopeNoTemplate' : '$isolateScope') : '$scope';
8824 $element.data(dataName, scope);
8827 compile.$$addScopeClass = debugInfoEnabled ? function $$addScopeClass($element, isolated) {
8828 safeAddClass($element, isolated ? 'ng-isolate-scope' : 'ng-scope');
8831 compile.$$createComment = function(directiveName, comment) {
8833 if (debugInfoEnabled) {
8834 content = ' ' + (directiveName || '') + ': ';
8835 if (comment) content += comment + ' ';
8837 return window.document.createComment(content);
8842 //================================
8844 function compile($compileNodes, transcludeFn, maxPriority, ignoreDirective,
8845 previousCompileContext) {
8846 if (!($compileNodes instanceof jqLite)) {
8847 // jquery always rewraps, whereas we need to preserve the original selector so that we can
8849 $compileNodes = jqLite($compileNodes);
8851 var compositeLinkFn =
8852 compileNodes($compileNodes, transcludeFn, $compileNodes,
8853 maxPriority, ignoreDirective, previousCompileContext);
8854 compile.$$addScopeClass($compileNodes);
8855 var namespace = null;
8856 return function publicLinkFn(scope, cloneConnectFn, options) {
8857 if (!$compileNodes) {
8858 throw $compileMinErr('multilink', 'This element has already been linked.');
8860 assertArg(scope, 'scope');
8862 if (previousCompileContext && previousCompileContext.needsNewScope) {
8863 // A parent directive did a replace and a directive on this element asked
8864 // for transclusion, which caused us to lose a layer of element on which
8865 // we could hold the new transclusion scope, so we will create it manually
8867 scope = scope.$parent.$new();
8870 options = options || {};
8871 var parentBoundTranscludeFn = options.parentBoundTranscludeFn,
8872 transcludeControllers = options.transcludeControllers,
8873 futureParentElement = options.futureParentElement;
8875 // When `parentBoundTranscludeFn` is passed, it is a
8876 // `controllersBoundTransclude` function (it was previously passed
8877 // as `transclude` to directive.link) so we must unwrap it to get
8878 // its `boundTranscludeFn`
8879 if (parentBoundTranscludeFn && parentBoundTranscludeFn.$$boundTransclude) {
8880 parentBoundTranscludeFn = parentBoundTranscludeFn.$$boundTransclude;
8884 namespace = detectNamespaceForChildElements(futureParentElement);
8887 if (namespace !== 'html') {
8888 // When using a directive with replace:true and templateUrl the $compileNodes
8889 // (or a child element inside of them)
8890 // might change, so we need to recreate the namespace adapted compileNodes
8891 // for call to the link function.
8892 // Note: This will already clone the nodes...
8894 wrapTemplate(namespace, jqLite('<div>').append($compileNodes).html())
8896 } else if (cloneConnectFn) {
8897 // important!!: we must call our jqLite.clone() since the jQuery one is trying to be smart
8898 // and sometimes changes the structure of the DOM.
8899 $linkNode = JQLitePrototype.clone.call($compileNodes);
8901 $linkNode = $compileNodes;
8904 if (transcludeControllers) {
8905 for (var controllerName in transcludeControllers) {
8906 $linkNode.data('$' + controllerName + 'Controller', transcludeControllers[controllerName].instance);
8910 compile.$$addScopeInfo($linkNode, scope);
8912 if (cloneConnectFn) cloneConnectFn($linkNode, scope);
8913 if (compositeLinkFn) compositeLinkFn(scope, $linkNode, $linkNode, parentBoundTranscludeFn);
8915 if (!cloneConnectFn) {
8916 $compileNodes = compositeLinkFn = null;
8922 function detectNamespaceForChildElements(parentElement) {
8923 // TODO: Make this detect MathML as well...
8924 var node = parentElement && parentElement[0];
8928 return nodeName_(node) !== 'foreignobject' && toString.call(node).match(/SVG/) ? 'svg' : 'html';
8933 * Compile function matches each node in nodeList against the directives. Once all directives
8934 * for a particular node are collected their compile functions are executed. The compile
8935 * functions return values - the linking functions - are combined into a composite linking
8936 * function, which is the a linking function for the node.
8938 * @param {NodeList} nodeList an array of nodes or NodeList to compile
8939 * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the
8940 * scope argument is auto-generated to the new child of the transcluded parent scope.
8941 * @param {DOMElement=} $rootElement If the nodeList is the root of the compilation tree then
8942 * the rootElement must be set the jqLite collection of the compile root. This is
8943 * needed so that the jqLite collection items can be replaced with widgets.
8944 * @param {number=} maxPriority Max directive priority.
8945 * @returns {Function} A composite linking function of all of the matched directives or null.
8947 function compileNodes(nodeList, transcludeFn, $rootElement, maxPriority, ignoreDirective,
8948 previousCompileContext) {
8950 // `nodeList` can be either an element's `.childNodes` (live NodeList)
8951 // or a jqLite/jQuery collection or an array
8952 notLiveList = isArray(nodeList) || (nodeList instanceof jqLite),
8953 attrs, directives, nodeLinkFn, childNodes, childLinkFn, linkFnFound, nodeLinkFnFound;
8956 for (var i = 0; i < nodeList.length; i++) {
8957 attrs = new Attributes();
8959 // Support: IE 11 only
8960 // Workaround for #11781 and #14924
8962 mergeConsecutiveTextNodes(nodeList, i, notLiveList);
8965 // We must always refer to `nodeList[i]` hereafter,
8966 // since the nodes can be replaced underneath us.
8967 directives = collectDirectives(nodeList[i], [], attrs, i === 0 ? maxPriority : undefined,
8970 nodeLinkFn = (directives.length)
8971 ? applyDirectivesToNode(directives, nodeList[i], attrs, transcludeFn, $rootElement,
8972 null, [], [], previousCompileContext)
8975 if (nodeLinkFn && nodeLinkFn.scope) {
8976 compile.$$addScopeClass(attrs.$$element);
8979 childLinkFn = (nodeLinkFn && nodeLinkFn.terminal ||
8980 !(childNodes = nodeList[i].childNodes) ||
8983 : compileNodes(childNodes,
8985 (nodeLinkFn.transcludeOnThisElement || !nodeLinkFn.templateOnThisElement)
8986 && nodeLinkFn.transclude) : transcludeFn);
8988 if (nodeLinkFn || childLinkFn) {
8989 linkFns.push(i, nodeLinkFn, childLinkFn);
8991 nodeLinkFnFound = nodeLinkFnFound || nodeLinkFn;
8994 //use the previous context only for the first element in the virtual group
8995 previousCompileContext = null;
8998 // return a linking function if we have found anything, null otherwise
8999 return linkFnFound ? compositeLinkFn : null;
9001 function compositeLinkFn(scope, nodeList, $rootElement, parentBoundTranscludeFn) {
9002 var nodeLinkFn, childLinkFn, node, childScope, i, ii, idx, childBoundTranscludeFn;
9006 if (nodeLinkFnFound) {
9007 // copy nodeList so that if a nodeLinkFn removes or adds an element at this DOM level our
9008 // offsets don't get screwed up
9009 var nodeListLength = nodeList.length;
9010 stableNodeList = new Array(nodeListLength);
9012 // create a sparse array by only copying the elements which have a linkFn
9013 for (i = 0; i < linkFns.length; i += 3) {
9015 stableNodeList[idx] = nodeList[idx];
9018 stableNodeList = nodeList;
9021 for (i = 0, ii = linkFns.length; i < ii;) {
9022 node = stableNodeList[linkFns[i++]];
9023 nodeLinkFn = linkFns[i++];
9024 childLinkFn = linkFns[i++];
9027 if (nodeLinkFn.scope) {
9028 childScope = scope.$new();
9029 compile.$$addScopeInfo(jqLite(node), childScope);
9034 if (nodeLinkFn.transcludeOnThisElement) {
9035 childBoundTranscludeFn = createBoundTranscludeFn(
9036 scope, nodeLinkFn.transclude, parentBoundTranscludeFn);
9038 } else if (!nodeLinkFn.templateOnThisElement && parentBoundTranscludeFn) {
9039 childBoundTranscludeFn = parentBoundTranscludeFn;
9041 } else if (!parentBoundTranscludeFn && transcludeFn) {
9042 childBoundTranscludeFn = createBoundTranscludeFn(scope, transcludeFn);
9045 childBoundTranscludeFn = null;
9048 nodeLinkFn(childLinkFn, childScope, node, $rootElement, childBoundTranscludeFn);
9050 } else if (childLinkFn) {
9051 childLinkFn(scope, node.childNodes, undefined, parentBoundTranscludeFn);
9057 function mergeConsecutiveTextNodes(nodeList, idx, notLiveList) {
9058 var node = nodeList[idx];
9059 var parent = node.parentNode;
9062 if (node.nodeType !== NODE_TYPE_TEXT) {
9067 sibling = parent ? node.nextSibling : nodeList[idx + 1];
9068 if (!sibling || sibling.nodeType !== NODE_TYPE_TEXT) {
9072 node.nodeValue = node.nodeValue + sibling.nodeValue;
9074 if (sibling.parentNode) {
9075 sibling.parentNode.removeChild(sibling);
9077 if (notLiveList && sibling === nodeList[idx + 1]) {
9078 nodeList.splice(idx + 1, 1);
9083 function createBoundTranscludeFn(scope, transcludeFn, previousBoundTranscludeFn) {
9084 function boundTranscludeFn(transcludedScope, cloneFn, controllers, futureParentElement, containingScope) {
9086 if (!transcludedScope) {
9087 transcludedScope = scope.$new(false, containingScope);
9088 transcludedScope.$$transcluded = true;
9091 return transcludeFn(transcludedScope, cloneFn, {
9092 parentBoundTranscludeFn: previousBoundTranscludeFn,
9093 transcludeControllers: controllers,
9094 futureParentElement: futureParentElement
9098 // We need to attach the transclusion slots onto the `boundTranscludeFn`
9099 // so that they are available inside the `controllersBoundTransclude` function
9100 var boundSlots = boundTranscludeFn.$$slots = createMap();
9101 for (var slotName in transcludeFn.$$slots) {
9102 if (transcludeFn.$$slots[slotName]) {
9103 boundSlots[slotName] = createBoundTranscludeFn(scope, transcludeFn.$$slots[slotName], previousBoundTranscludeFn);
9105 boundSlots[slotName] = null;
9109 return boundTranscludeFn;
9113 * Looks for directives on the given node and adds them to the directive collection which is
9116 * @param node Node to search.
9117 * @param directives An array to which the directives are added to. This array is sorted before
9118 * the function returns.
9119 * @param attrs The shared attrs object which is used to populate the normalized attributes.
9120 * @param {number=} maxPriority Max directive priority.
9122 function collectDirectives(node, directives, attrs, maxPriority, ignoreDirective) {
9123 var nodeType = node.nodeType,
9124 attrsMap = attrs.$attr,
9130 case NODE_TYPE_ELEMENT: /* Element */
9132 nodeName = nodeName_(node);
9134 // use the node name: <directive>
9135 addDirective(directives,
9136 directiveNormalize(nodeName), 'E', maxPriority, ignoreDirective);
9138 // iterate over the attributes
9139 for (var attr, name, nName, ngAttrName, value, isNgAttr, nAttrs = node.attributes,
9140 j = 0, jj = nAttrs && nAttrs.length; j < jj; j++) {
9141 var attrStartName = false;
9142 var attrEndName = false;
9148 // support ngAttr attribute binding
9149 ngAttrName = directiveNormalize(name);
9150 isNgAttr = NG_ATTR_BINDING.test(ngAttrName);
9152 name = name.replace(PREFIX_REGEXP, '')
9153 .substr(8).replace(/_(.)/g, function(match, letter) {
9154 return letter.toUpperCase();
9158 var multiElementMatch = ngAttrName.match(MULTI_ELEMENT_DIR_RE);
9159 if (multiElementMatch && directiveIsMultiElement(multiElementMatch[1])) {
9160 attrStartName = name;
9161 attrEndName = name.substr(0, name.length - 5) + 'end';
9162 name = name.substr(0, name.length - 6);
9165 nName = directiveNormalize(name.toLowerCase());
9166 attrsMap[nName] = name;
9167 if (isNgAttr || !attrs.hasOwnProperty(nName)) {
9168 attrs[nName] = value;
9169 if (getBooleanAttrName(node, nName)) {
9170 attrs[nName] = true; // presence means true
9173 addAttrInterpolateDirective(node, directives, value, nName, isNgAttr);
9174 addDirective(directives, nName, 'A', maxPriority, ignoreDirective, attrStartName,
9178 if (nodeName === 'input' && node.getAttribute('type') === 'hidden') {
9179 // Hidden input elements can have strange behaviour when navigating back to the page
9180 // This tells the browser not to try to cache and reinstate previous values
9181 node.setAttribute('autocomplete', 'off');
9184 // use class as directive
9185 if (!cssClassDirectivesEnabled) break;
9186 className = node.className;
9187 if (isObject(className)) {
9188 // Maybe SVGAnimatedString
9189 className = className.animVal;
9191 if (isString(className) && className !== '') {
9192 while ((match = CLASS_DIRECTIVE_REGEXP.exec(className))) {
9193 nName = directiveNormalize(match[2]);
9194 if (addDirective(directives, nName, 'C', maxPriority, ignoreDirective)) {
9195 attrs[nName] = trim(match[3]);
9197 className = className.substr(match.index + match[0].length);
9201 case NODE_TYPE_TEXT: /* Text Node */
9202 addTextInterpolateDirective(directives, node.nodeValue);
9204 case NODE_TYPE_COMMENT: /* Comment */
9205 if (!commentDirectivesEnabled) break;
9206 collectCommentDirectives(node, directives, attrs, maxPriority, ignoreDirective);
9210 directives.sort(byPriority);
9214 function collectCommentDirectives(node, directives, attrs, maxPriority, ignoreDirective) {
9215 // function created because of performance, try/catch disables
9216 // the optimization of the whole function #14848
9218 var match = COMMENT_DIRECTIVE_REGEXP.exec(node.nodeValue);
9220 var nName = directiveNormalize(match[1]);
9221 if (addDirective(directives, nName, 'M', maxPriority, ignoreDirective)) {
9222 attrs[nName] = trim(match[2]);
9226 // turns out that under some circumstances IE9 throws errors when one attempts to read
9227 // comment's node value.
9228 // Just ignore it and continue. (Can't seem to reproduce in test case.)
9233 * Given a node with a directive-start it collects all of the siblings until it finds
9240 function groupScan(node, attrStart, attrEnd) {
9243 if (attrStart && node.hasAttribute && node.hasAttribute(attrStart)) {
9246 throw $compileMinErr('uterdir',
9247 'Unterminated attribute, found \'{0}\' but no matching \'{1}\' found.',
9248 attrStart, attrEnd);
9250 if (node.nodeType === NODE_TYPE_ELEMENT) {
9251 if (node.hasAttribute(attrStart)) depth++;
9252 if (node.hasAttribute(attrEnd)) depth--;
9255 node = node.nextSibling;
9256 } while (depth > 0);
9261 return jqLite(nodes);
9265 * Wrapper for linking function which converts normal linking function into a grouped
9270 * @returns {Function}
9272 function groupElementsLinkFnWrapper(linkFn, attrStart, attrEnd) {
9273 return function groupedElementsLink(scope, element, attrs, controllers, transcludeFn) {
9274 element = groupScan(element[0], attrStart, attrEnd);
9275 return linkFn(scope, element, attrs, controllers, transcludeFn);
9280 * A function generator that is used to support both eager and lazy compilation
9283 * @param $compileNodes
9284 * @param transcludeFn
9285 * @param maxPriority
9286 * @param ignoreDirective
9287 * @param previousCompileContext
9288 * @returns {Function}
9290 function compilationGenerator(eager, $compileNodes, transcludeFn, maxPriority, ignoreDirective, previousCompileContext) {
9294 return compile($compileNodes, transcludeFn, maxPriority, ignoreDirective, previousCompileContext);
9296 return /** @this */ function lazyCompilation() {
9298 compiled = compile($compileNodes, transcludeFn, maxPriority, ignoreDirective, previousCompileContext);
9300 // Null out all of these references in order to make them eligible for garbage collection
9301 // since this is a potentially long lived closure
9302 $compileNodes = transcludeFn = previousCompileContext = null;
9304 return compiled.apply(this, arguments);
9309 * Once the directives have been collected, their compile functions are executed. This method
9310 * is responsible for inlining directive templates as well as terminating the application
9311 * of the directives if the terminal directive has been reached.
9313 * @param {Array} directives Array of collected directives to execute their compile function.
9314 * this needs to be pre-sorted by priority order.
9315 * @param {Node} compileNode The raw DOM node to apply the compile functions to
9316 * @param {Object} templateAttrs The shared attribute function
9317 * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the
9318 * scope argument is auto-generated to the new
9319 * child of the transcluded parent scope.
9320 * @param {JQLite} jqCollection If we are working on the root of the compile tree then this
9321 * argument has the root jqLite array so that we can replace nodes
9323 * @param {Object=} originalReplaceDirective An optional directive that will be ignored when
9324 * compiling the transclusion.
9325 * @param {Array.<Function>} preLinkFns
9326 * @param {Array.<Function>} postLinkFns
9327 * @param {Object} previousCompileContext Context used for previous compilation of the current
9329 * @returns {Function} linkFn
9331 function applyDirectivesToNode(directives, compileNode, templateAttrs, transcludeFn,
9332 jqCollection, originalReplaceDirective, preLinkFns, postLinkFns,
9333 previousCompileContext) {
9334 previousCompileContext = previousCompileContext || {};
9336 var terminalPriority = -Number.MAX_VALUE,
9337 newScopeDirective = previousCompileContext.newScopeDirective,
9338 controllerDirectives = previousCompileContext.controllerDirectives,
9339 newIsolateScopeDirective = previousCompileContext.newIsolateScopeDirective,
9340 templateDirective = previousCompileContext.templateDirective,
9341 nonTlbTranscludeDirective = previousCompileContext.nonTlbTranscludeDirective,
9342 hasTranscludeDirective = false,
9343 hasTemplate = false,
9344 hasElementTranscludeDirective = previousCompileContext.hasElementTranscludeDirective,
9345 $compileNode = templateAttrs.$$element = jqLite(compileNode),
9349 replaceDirective = originalReplaceDirective,
9350 childTranscludeFn = transcludeFn,
9352 didScanForMultipleTransclusion = false,
9353 mightHaveMultipleTransclusionError = false,
9356 // executes all directives on the current element
9357 for (var i = 0, ii = directives.length; i < ii; i++) {
9358 directive = directives[i];
9359 var attrStart = directive.$$start;
9360 var attrEnd = directive.$$end;
9362 // collect multiblock sections
9364 $compileNode = groupScan(compileNode, attrStart, attrEnd);
9366 $template = undefined;
9368 if (terminalPriority > directive.priority) {
9369 break; // prevent further processing of directives
9372 directiveValue = directive.scope;
9374 if (directiveValue) {
9376 // skip the check for directives with async templates, we'll check the derived sync
9377 // directive when the template arrives
9378 if (!directive.templateUrl) {
9379 if (isObject(directiveValue)) {
9380 // This directive is trying to add an isolated scope.
9381 // Check that there is no scope of any kind already
9382 assertNoDuplicate('new/isolated scope', newIsolateScopeDirective || newScopeDirective,
9383 directive, $compileNode);
9384 newIsolateScopeDirective = directive;
9386 // This directive is trying to add a child scope.
9387 // Check that there is no isolated scope already
9388 assertNoDuplicate('new/isolated scope', newIsolateScopeDirective, directive,
9393 newScopeDirective = newScopeDirective || directive;
9396 directiveName = directive.name;
9398 // If we encounter a condition that can result in transclusion on the directive,
9399 // then scan ahead in the remaining directives for others that may cause a multiple
9400 // transclusion error to be thrown during the compilation process. If a matching directive
9401 // is found, then we know that when we encounter a transcluded directive, we need to eagerly
9402 // compile the `transclude` function rather than doing it lazily in order to throw
9403 // exceptions at the correct time
9404 if (!didScanForMultipleTransclusion && ((directive.replace && (directive.templateUrl || directive.template))
9405 || (directive.transclude && !directive.$$tlb))) {
9406 var candidateDirective;
9408 for (var scanningIndex = i + 1; (candidateDirective = directives[scanningIndex++]);) {
9409 if ((candidateDirective.transclude && !candidateDirective.$$tlb)
9410 || (candidateDirective.replace && (candidateDirective.templateUrl || candidateDirective.template))) {
9411 mightHaveMultipleTransclusionError = true;
9416 didScanForMultipleTransclusion = true;
9419 if (!directive.templateUrl && directive.controller) {
9420 controllerDirectives = controllerDirectives || createMap();
9421 assertNoDuplicate('\'' + directiveName + '\' controller',
9422 controllerDirectives[directiveName], directive, $compileNode);
9423 controllerDirectives[directiveName] = directive;
9426 directiveValue = directive.transclude;
9428 if (directiveValue) {
9429 hasTranscludeDirective = true;
9431 // Special case ngIf and ngRepeat so that we don't complain about duplicate transclusion.
9432 // This option should only be used by directives that know how to safely handle element transclusion,
9433 // where the transcluded nodes are added or replaced after linking.
9434 if (!directive.$$tlb) {
9435 assertNoDuplicate('transclusion', nonTlbTranscludeDirective, directive, $compileNode);
9436 nonTlbTranscludeDirective = directive;
9439 if (directiveValue === 'element') {
9440 hasElementTranscludeDirective = true;
9441 terminalPriority = directive.priority;
9442 $template = $compileNode;
9443 $compileNode = templateAttrs.$$element =
9444 jqLite(compile.$$createComment(directiveName, templateAttrs[directiveName]));
9445 compileNode = $compileNode[0];
9446 replaceWith(jqCollection, sliceArgs($template), compileNode);
9448 // Support: Chrome < 50
9449 // https://github.com/angular/angular.js/issues/14041
9451 // In the versions of V8 prior to Chrome 50, the document fragment that is created
9452 // in the `replaceWith` function is improperly garbage collected despite still
9453 // being referenced by the `parentNode` property of all of the child nodes. By adding
9454 // a reference to the fragment via a different property, we can avoid that incorrect
9456 // TODO: remove this line after Chrome 50 has been released
9457 $template[0].$$parentNode = $template[0].parentNode;
9459 childTranscludeFn = compilationGenerator(mightHaveMultipleTransclusionError, $template, transcludeFn, terminalPriority,
9460 replaceDirective && replaceDirective.name, {
9462 // - controllerDirectives - otherwise we'll create duplicates controllers
9463 // - newIsolateScopeDirective or templateDirective - combining templates with
9464 // element transclusion doesn't make sense.
9466 // We need only nonTlbTranscludeDirective so that we prevent putting transclusion
9467 // on the same element more than once.
9468 nonTlbTranscludeDirective: nonTlbTranscludeDirective
9472 var slots = createMap();
9474 if (!isObject(directiveValue)) {
9475 $template = jqLite(jqLiteClone(compileNode)).contents();
9478 // We have transclusion slots,
9479 // collect them up, compile them and store their transclusion functions
9482 var slotMap = createMap();
9483 var filledSlots = createMap();
9485 // Parse the element selectors
9486 forEach(directiveValue, function(elementSelector, slotName) {
9487 // If an element selector starts with a ? then it is optional
9488 var optional = (elementSelector.charAt(0) === '?');
9489 elementSelector = optional ? elementSelector.substring(1) : elementSelector;
9491 slotMap[elementSelector] = slotName;
9493 // We explicitly assign `null` since this implies that a slot was defined but not filled.
9494 // Later when calling boundTransclusion functions with a slot name we only error if the
9495 // slot is `undefined`
9496 slots[slotName] = null;
9498 // filledSlots contains `true` for all slots that are either optional or have been
9499 // filled. This is used to check that we have not missed any required slots
9500 filledSlots[slotName] = optional;
9503 // Add the matching elements into their slot
9504 forEach($compileNode.contents(), function(node) {
9505 var slotName = slotMap[directiveNormalize(nodeName_(node))];
9507 filledSlots[slotName] = true;
9508 slots[slotName] = slots[slotName] || [];
9509 slots[slotName].push(node);
9511 $template.push(node);
9515 // Check for required slots that were not filled
9516 forEach(filledSlots, function(filled, slotName) {
9518 throw $compileMinErr('reqslot', 'Required transclusion slot `{0}` was not filled.', slotName);
9522 for (var slotName in slots) {
9523 if (slots[slotName]) {
9524 // Only define a transclusion function if the slot was filled
9525 slots[slotName] = compilationGenerator(mightHaveMultipleTransclusionError, slots[slotName], transcludeFn);
9530 $compileNode.empty(); // clear contents
9531 childTranscludeFn = compilationGenerator(mightHaveMultipleTransclusionError, $template, transcludeFn, undefined,
9532 undefined, { needsNewScope: directive.$$isolateScope || directive.$$newScope});
9533 childTranscludeFn.$$slots = slots;
9537 if (directive.template) {
9539 assertNoDuplicate('template', templateDirective, directive, $compileNode);
9540 templateDirective = directive;
9542 directiveValue = (isFunction(directive.template))
9543 ? directive.template($compileNode, templateAttrs)
9544 : directive.template;
9546 directiveValue = denormalizeTemplate(directiveValue);
9548 if (directive.replace) {
9549 replaceDirective = directive;
9550 if (jqLiteIsTextNode(directiveValue)) {
9553 $template = removeComments(wrapTemplate(directive.templateNamespace, trim(directiveValue)));
9555 compileNode = $template[0];
9557 if ($template.length !== 1 || compileNode.nodeType !== NODE_TYPE_ELEMENT) {
9558 throw $compileMinErr('tplrt',
9559 'Template for directive \'{0}\' must have exactly one root element. {1}',
9563 replaceWith(jqCollection, $compileNode, compileNode);
9565 var newTemplateAttrs = {$attr: {}};
9567 // combine directives from the original node and from the template:
9568 // - take the array of directives for this element
9569 // - split it into two parts, those that already applied (processed) and those that weren't (unprocessed)
9570 // - collect directives from the template and sort them by priority
9571 // - combine directives as: processed + template + unprocessed
9572 var templateDirectives = collectDirectives(compileNode, [], newTemplateAttrs);
9573 var unprocessedDirectives = directives.splice(i + 1, directives.length - (i + 1));
9575 if (newIsolateScopeDirective || newScopeDirective) {
9576 // The original directive caused the current element to be replaced but this element
9577 // also needs to have a new scope, so we need to tell the template directives
9578 // that they would need to get their scope from further up, if they require transclusion
9579 markDirectiveScope(templateDirectives, newIsolateScopeDirective, newScopeDirective);
9581 directives = directives.concat(templateDirectives).concat(unprocessedDirectives);
9582 mergeTemplateAttributes(templateAttrs, newTemplateAttrs);
9584 ii = directives.length;
9586 $compileNode.html(directiveValue);
9590 if (directive.templateUrl) {
9592 assertNoDuplicate('template', templateDirective, directive, $compileNode);
9593 templateDirective = directive;
9595 if (directive.replace) {
9596 replaceDirective = directive;
9599 // eslint-disable-next-line no-func-assign
9600 nodeLinkFn = compileTemplateUrl(directives.splice(i, directives.length - i), $compileNode,
9601 templateAttrs, jqCollection, hasTranscludeDirective && childTranscludeFn, preLinkFns, postLinkFns, {
9602 controllerDirectives: controllerDirectives,
9603 newScopeDirective: (newScopeDirective !== directive) && newScopeDirective,
9604 newIsolateScopeDirective: newIsolateScopeDirective,
9605 templateDirective: templateDirective,
9606 nonTlbTranscludeDirective: nonTlbTranscludeDirective
9608 ii = directives.length;
9609 } else if (directive.compile) {
9611 linkFn = directive.compile($compileNode, templateAttrs, childTranscludeFn);
9612 var context = directive.$$originalDirective || directive;
9613 if (isFunction(linkFn)) {
9614 addLinkFns(null, bind(context, linkFn), attrStart, attrEnd);
9615 } else if (linkFn) {
9616 addLinkFns(bind(context, linkFn.pre), bind(context, linkFn.post), attrStart, attrEnd);
9619 $exceptionHandler(e, startingTag($compileNode));
9623 if (directive.terminal) {
9624 nodeLinkFn.terminal = true;
9625 terminalPriority = Math.max(terminalPriority, directive.priority);
9630 nodeLinkFn.scope = newScopeDirective && newScopeDirective.scope === true;
9631 nodeLinkFn.transcludeOnThisElement = hasTranscludeDirective;
9632 nodeLinkFn.templateOnThisElement = hasTemplate;
9633 nodeLinkFn.transclude = childTranscludeFn;
9635 previousCompileContext.hasElementTranscludeDirective = hasElementTranscludeDirective;
9637 // might be normal or delayed nodeLinkFn depending on if templateUrl is present
9640 ////////////////////
9642 function addLinkFns(pre, post, attrStart, attrEnd) {
9644 if (attrStart) pre = groupElementsLinkFnWrapper(pre, attrStart, attrEnd);
9645 pre.require = directive.require;
9646 pre.directiveName = directiveName;
9647 if (newIsolateScopeDirective === directive || directive.$$isolateScope) {
9648 pre = cloneAndAnnotateFn(pre, {isolateScope: true});
9650 preLinkFns.push(pre);
9653 if (attrStart) post = groupElementsLinkFnWrapper(post, attrStart, attrEnd);
9654 post.require = directive.require;
9655 post.directiveName = directiveName;
9656 if (newIsolateScopeDirective === directive || directive.$$isolateScope) {
9657 post = cloneAndAnnotateFn(post, {isolateScope: true});
9659 postLinkFns.push(post);
9663 function nodeLinkFn(childLinkFn, scope, linkNode, $rootElement, boundTranscludeFn) {
9664 var i, ii, linkFn, isolateScope, controllerScope, elementControllers, transcludeFn, $element,
9665 attrs, scopeBindingInfo;
9667 if (compileNode === linkNode) {
9668 attrs = templateAttrs;
9669 $element = templateAttrs.$$element;
9671 $element = jqLite(linkNode);
9672 attrs = new Attributes($element, templateAttrs);
9675 controllerScope = scope;
9676 if (newIsolateScopeDirective) {
9677 isolateScope = scope.$new(true);
9678 } else if (newScopeDirective) {
9679 controllerScope = scope.$parent;
9682 if (boundTranscludeFn) {
9683 // track `boundTranscludeFn` so it can be unwrapped if `transcludeFn`
9684 // is later passed as `parentBoundTranscludeFn` to `publicLinkFn`
9685 transcludeFn = controllersBoundTransclude;
9686 transcludeFn.$$boundTransclude = boundTranscludeFn;
9687 // expose the slots on the `$transclude` function
9688 transcludeFn.isSlotFilled = function(slotName) {
9689 return !!boundTranscludeFn.$$slots[slotName];
9693 if (controllerDirectives) {
9694 elementControllers = setupControllers($element, attrs, transcludeFn, controllerDirectives, isolateScope, scope, newIsolateScopeDirective);
9697 if (newIsolateScopeDirective) {
9698 // Initialize isolate scope bindings for new isolate scope directive.
9699 compile.$$addScopeInfo($element, isolateScope, true, !(templateDirective && (templateDirective === newIsolateScopeDirective ||
9700 templateDirective === newIsolateScopeDirective.$$originalDirective)));
9701 compile.$$addScopeClass($element, true);
9702 isolateScope.$$isolateBindings =
9703 newIsolateScopeDirective.$$isolateBindings;
9704 scopeBindingInfo = initializeDirectiveBindings(scope, attrs, isolateScope,
9705 isolateScope.$$isolateBindings,
9706 newIsolateScopeDirective);
9707 if (scopeBindingInfo.removeWatches) {
9708 isolateScope.$on('$destroy', scopeBindingInfo.removeWatches);
9712 // Initialize bindToController bindings
9713 for (var name in elementControllers) {
9714 var controllerDirective = controllerDirectives[name];
9715 var controller = elementControllers[name];
9716 var bindings = controllerDirective.$$bindings.bindToController;
9718 if (preAssignBindingsEnabled) {
9720 controller.bindingInfo =
9721 initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective);
9723 controller.bindingInfo = {};
9726 var controllerResult = controller();
9727 if (controllerResult !== controller.instance) {
9728 // If the controller constructor has a return value, overwrite the instance
9729 // from setupControllers
9730 controller.instance = controllerResult;
9731 $element.data('$' + controllerDirective.name + 'Controller', controllerResult);
9732 if (controller.bindingInfo.removeWatches) {
9733 controller.bindingInfo.removeWatches();
9735 controller.bindingInfo =
9736 initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective);
9739 controller.instance = controller();
9740 $element.data('$' + controllerDirective.name + 'Controller', controller.instance);
9741 controller.bindingInfo =
9742 initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective);
9746 // Bind the required controllers to the controller, if `require` is an object and `bindToController` is truthy
9747 forEach(controllerDirectives, function(controllerDirective, name) {
9748 var require = controllerDirective.require;
9749 if (controllerDirective.bindToController && !isArray(require) && isObject(require)) {
9750 extend(elementControllers[name].instance, getControllers(name, require, $element, elementControllers));
9754 // Handle the init and destroy lifecycle hooks on all controllers that have them
9755 forEach(elementControllers, function(controller) {
9756 var controllerInstance = controller.instance;
9757 if (isFunction(controllerInstance.$onChanges)) {
9759 controllerInstance.$onChanges(controller.bindingInfo.initialChanges);
9761 $exceptionHandler(e);
9764 if (isFunction(controllerInstance.$onInit)) {
9766 controllerInstance.$onInit();
9768 $exceptionHandler(e);
9771 if (isFunction(controllerInstance.$doCheck)) {
9772 controllerScope.$watch(function() { controllerInstance.$doCheck(); });
9773 controllerInstance.$doCheck();
9775 if (isFunction(controllerInstance.$onDestroy)) {
9776 controllerScope.$on('$destroy', function callOnDestroyHook() {
9777 controllerInstance.$onDestroy();
9783 for (i = 0, ii = preLinkFns.length; i < ii; i++) {
9784 linkFn = preLinkFns[i];
9785 invokeLinkFn(linkFn,
9786 linkFn.isolateScope ? isolateScope : scope,
9789 linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers),
9795 // We only pass the isolate scope, if the isolate directive has a template,
9796 // otherwise the child elements do not belong to the isolate directive.
9797 var scopeToChild = scope;
9798 if (newIsolateScopeDirective && (newIsolateScopeDirective.template || newIsolateScopeDirective.templateUrl === null)) {
9799 scopeToChild = isolateScope;
9802 childLinkFn(scopeToChild, linkNode.childNodes, undefined, boundTranscludeFn);
9806 for (i = postLinkFns.length - 1; i >= 0; i--) {
9807 linkFn = postLinkFns[i];
9808 invokeLinkFn(linkFn,
9809 linkFn.isolateScope ? isolateScope : scope,
9812 linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers),
9817 // Trigger $postLink lifecycle hooks
9818 forEach(elementControllers, function(controller) {
9819 var controllerInstance = controller.instance;
9820 if (isFunction(controllerInstance.$postLink)) {
9821 controllerInstance.$postLink();
9825 // This is the function that is injected as `$transclude`.
9826 // Note: all arguments are optional!
9827 function controllersBoundTransclude(scope, cloneAttachFn, futureParentElement, slotName) {
9828 var transcludeControllers;
9829 // No scope passed in:
9830 if (!isScope(scope)) {
9831 slotName = futureParentElement;
9832 futureParentElement = cloneAttachFn;
9833 cloneAttachFn = scope;
9837 if (hasElementTranscludeDirective) {
9838 transcludeControllers = elementControllers;
9840 if (!futureParentElement) {
9841 futureParentElement = hasElementTranscludeDirective ? $element.parent() : $element;
9844 // slotTranscludeFn can be one of three things:
9845 // * a transclude function - a filled slot
9846 // * `null` - an optional slot that was not filled
9847 // * `undefined` - a slot that was not declared (i.e. invalid)
9848 var slotTranscludeFn = boundTranscludeFn.$$slots[slotName];
9849 if (slotTranscludeFn) {
9850 return slotTranscludeFn(scope, cloneAttachFn, transcludeControllers, futureParentElement, scopeToChild);
9851 } else if (isUndefined(slotTranscludeFn)) {
9852 throw $compileMinErr('noslot',
9853 'No parent directive that requires a transclusion with slot name "{0}". ' +
9855 slotName, startingTag($element));
9858 return boundTranscludeFn(scope, cloneAttachFn, transcludeControllers, futureParentElement, scopeToChild);
9864 function getControllers(directiveName, require, $element, elementControllers) {
9867 if (isString(require)) {
9868 var match = require.match(REQUIRE_PREFIX_REGEXP);
9869 var name = require.substring(match[0].length);
9870 var inheritType = match[1] || match[3];
9871 var optional = match[2] === '?';
9873 //If only parents then start at the parent element
9874 if (inheritType === '^^') {
9875 $element = $element.parent();
9876 //Otherwise attempt getting the controller from elementControllers in case
9877 //the element is transcluded (and has no data) and to avoid .data if possible
9879 value = elementControllers && elementControllers[name];
9880 value = value && value.instance;
9884 var dataName = '$' + name + 'Controller';
9885 value = inheritType ? $element.inheritedData(dataName) : $element.data(dataName);
9888 if (!value && !optional) {
9889 throw $compileMinErr('ctreq',
9890 'Controller \'{0}\', required by directive \'{1}\', can\'t be found!',
9891 name, directiveName);
9893 } else if (isArray(require)) {
9895 for (var i = 0, ii = require.length; i < ii; i++) {
9896 value[i] = getControllers(directiveName, require[i], $element, elementControllers);
9898 } else if (isObject(require)) {
9900 forEach(require, function(controller, property) {
9901 value[property] = getControllers(directiveName, controller, $element, elementControllers);
9905 return value || null;
9908 function setupControllers($element, attrs, transcludeFn, controllerDirectives, isolateScope, scope, newIsolateScopeDirective) {
9909 var elementControllers = createMap();
9910 for (var controllerKey in controllerDirectives) {
9911 var directive = controllerDirectives[controllerKey];
9913 $scope: directive === newIsolateScopeDirective || directive.$$isolateScope ? isolateScope : scope,
9916 $transclude: transcludeFn
9919 var controller = directive.controller;
9920 if (controller === '@') {
9921 controller = attrs[directive.name];
9924 var controllerInstance = $controller(controller, locals, true, directive.controllerAs);
9926 // For directives with element transclusion the element is a comment.
9927 // In this case .data will not attach any data.
9928 // Instead, we save the controllers for the element in a local hash and attach to .data
9929 // later, once we have the actual element.
9930 elementControllers[directive.name] = controllerInstance;
9931 $element.data('$' + directive.name + 'Controller', controllerInstance.instance);
9933 return elementControllers;
9936 // Depending upon the context in which a directive finds itself it might need to have a new isolated
9937 // or child scope created. For instance:
9938 // * if the directive has been pulled into a template because another directive with a higher priority
9939 // asked for element transclusion
9940 // * if the directive itself asks for transclusion but it is at the root of a template and the original
9941 // element was replaced. See https://github.com/angular/angular.js/issues/12936
9942 function markDirectiveScope(directives, isolateScope, newScope) {
9943 for (var j = 0, jj = directives.length; j < jj; j++) {
9944 directives[j] = inherit(directives[j], {$$isolateScope: isolateScope, $$newScope: newScope});
9949 * looks up the directive and decorates it with exception handling and proper parameters. We
9950 * call this the boundDirective.
9952 * @param {string} name name of the directive to look up.
9953 * @param {string} location The directive must be found in specific format.
9954 * String containing any of theses characters:
9956 * * `E`: element name
9960 * @returns {boolean} true if directive was added.
9962 function addDirective(tDirectives, name, location, maxPriority, ignoreDirective, startAttrName,
9964 if (name === ignoreDirective) return null;
9966 if (hasDirectives.hasOwnProperty(name)) {
9967 for (var directive, directives = $injector.get(name + Suffix),
9968 i = 0, ii = directives.length; i < ii; i++) {
9969 directive = directives[i];
9970 if ((isUndefined(maxPriority) || maxPriority > directive.priority) &&
9971 directive.restrict.indexOf(location) !== -1) {
9972 if (startAttrName) {
9973 directive = inherit(directive, {$$start: startAttrName, $$end: endAttrName});
9975 if (!directive.$$bindings) {
9976 var bindings = directive.$$bindings =
9977 parseDirectiveBindings(directive, directive.name);
9978 if (isObject(bindings.isolateScope)) {
9979 directive.$$isolateBindings = bindings.isolateScope;
9982 tDirectives.push(directive);
9992 * looks up the directive and returns true if it is a multi-element directive,
9993 * and therefore requires DOM nodes between -start and -end markers to be grouped
9996 * @param {string} name name of the directive to look up.
9997 * @returns true if directive was registered as multi-element.
9999 function directiveIsMultiElement(name) {
10000 if (hasDirectives.hasOwnProperty(name)) {
10001 for (var directive, directives = $injector.get(name + Suffix),
10002 i = 0, ii = directives.length; i < ii; i++) {
10003 directive = directives[i];
10004 if (directive.multiElement) {
10013 * When the element is replaced with HTML template then the new attributes
10014 * on the template need to be merged with the existing attributes in the DOM.
10015 * The desired effect is to have both of the attributes present.
10017 * @param {object} dst destination attributes (original DOM)
10018 * @param {object} src source attributes (from the directive template)
10020 function mergeTemplateAttributes(dst, src) {
10021 var srcAttr = src.$attr,
10022 dstAttr = dst.$attr;
10024 // reapply the old attributes to the new element
10025 forEach(dst, function(value, key) {
10026 if (key.charAt(0) !== '$') {
10027 if (src[key] && src[key] !== value) {
10028 if (value.length) {
10029 value += (key === 'style' ? ';' : ' ') + src[key];
10034 dst.$set(key, value, true, srcAttr[key]);
10038 // copy the new attributes on the old attrs object
10039 forEach(src, function(value, key) {
10040 // Check if we already set this attribute in the loop above.
10041 // `dst` will never contain hasOwnProperty as DOM parser won't let it.
10042 // You will get an "InvalidCharacterError: DOM Exception 5" error if you
10043 // have an attribute like "has-own-property" or "data-has-own-property", etc.
10044 if (!dst.hasOwnProperty(key) && key.charAt(0) !== '$') {
10047 if (key !== 'class' && key !== 'style') {
10048 dstAttr[key] = srcAttr[key];
10055 function compileTemplateUrl(directives, $compileNode, tAttrs,
10056 $rootElement, childTranscludeFn, preLinkFns, postLinkFns, previousCompileContext) {
10057 var linkQueue = [],
10058 afterTemplateNodeLinkFn,
10059 afterTemplateChildLinkFn,
10060 beforeTemplateCompileNode = $compileNode[0],
10061 origAsyncDirective = directives.shift(),
10062 derivedSyncDirective = inherit(origAsyncDirective, {
10063 templateUrl: null, transclude: null, replace: null, $$originalDirective: origAsyncDirective
10065 templateUrl = (isFunction(origAsyncDirective.templateUrl))
10066 ? origAsyncDirective.templateUrl($compileNode, tAttrs)
10067 : origAsyncDirective.templateUrl,
10068 templateNamespace = origAsyncDirective.templateNamespace;
10070 $compileNode.empty();
10072 $templateRequest(templateUrl)
10073 .then(function(content) {
10074 var compileNode, tempTemplateAttrs, $template, childBoundTranscludeFn;
10076 content = denormalizeTemplate(content);
10078 if (origAsyncDirective.replace) {
10079 if (jqLiteIsTextNode(content)) {
10082 $template = removeComments(wrapTemplate(templateNamespace, trim(content)));
10084 compileNode = $template[0];
10086 if ($template.length !== 1 || compileNode.nodeType !== NODE_TYPE_ELEMENT) {
10087 throw $compileMinErr('tplrt',
10088 'Template for directive \'{0}\' must have exactly one root element. {1}',
10089 origAsyncDirective.name, templateUrl);
10092 tempTemplateAttrs = {$attr: {}};
10093 replaceWith($rootElement, $compileNode, compileNode);
10094 var templateDirectives = collectDirectives(compileNode, [], tempTemplateAttrs);
10096 if (isObject(origAsyncDirective.scope)) {
10097 // the original directive that caused the template to be loaded async required
10098 // an isolate scope
10099 markDirectiveScope(templateDirectives, true);
10101 directives = templateDirectives.concat(directives);
10102 mergeTemplateAttributes(tAttrs, tempTemplateAttrs);
10104 compileNode = beforeTemplateCompileNode;
10105 $compileNode.html(content);
10108 directives.unshift(derivedSyncDirective);
10110 afterTemplateNodeLinkFn = applyDirectivesToNode(directives, compileNode, tAttrs,
10111 childTranscludeFn, $compileNode, origAsyncDirective, preLinkFns, postLinkFns,
10112 previousCompileContext);
10113 forEach($rootElement, function(node, i) {
10114 if (node === compileNode) {
10115 $rootElement[i] = $compileNode[0];
10118 afterTemplateChildLinkFn = compileNodes($compileNode[0].childNodes, childTranscludeFn);
10120 while (linkQueue.length) {
10121 var scope = linkQueue.shift(),
10122 beforeTemplateLinkNode = linkQueue.shift(),
10123 linkRootElement = linkQueue.shift(),
10124 boundTranscludeFn = linkQueue.shift(),
10125 linkNode = $compileNode[0];
10127 if (scope.$$destroyed) continue;
10129 if (beforeTemplateLinkNode !== beforeTemplateCompileNode) {
10130 var oldClasses = beforeTemplateLinkNode.className;
10132 if (!(previousCompileContext.hasElementTranscludeDirective &&
10133 origAsyncDirective.replace)) {
10134 // it was cloned therefore we have to clone as well.
10135 linkNode = jqLiteClone(compileNode);
10137 replaceWith(linkRootElement, jqLite(beforeTemplateLinkNode), linkNode);
10139 // Copy in CSS classes from original node
10140 safeAddClass(jqLite(linkNode), oldClasses);
10142 if (afterTemplateNodeLinkFn.transcludeOnThisElement) {
10143 childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn);
10145 childBoundTranscludeFn = boundTranscludeFn;
10147 afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, linkNode, $rootElement,
10148 childBoundTranscludeFn);
10151 }).catch(function(error) {
10152 if (error instanceof Error) {
10153 $exceptionHandler(error);
10157 return function delayedNodeLinkFn(ignoreChildLinkFn, scope, node, rootElement, boundTranscludeFn) {
10158 var childBoundTranscludeFn = boundTranscludeFn;
10159 if (scope.$$destroyed) return;
10161 linkQueue.push(scope,
10164 childBoundTranscludeFn);
10166 if (afterTemplateNodeLinkFn.transcludeOnThisElement) {
10167 childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn);
10169 afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, node, rootElement, childBoundTranscludeFn);
10176 * Sorting function for bound directives.
10178 function byPriority(a, b) {
10179 var diff = b.priority - a.priority;
10180 if (diff !== 0) return diff;
10181 if (a.name !== b.name) return (a.name < b.name) ? -1 : 1;
10182 return a.index - b.index;
10185 function assertNoDuplicate(what, previousDirective, directive, element) {
10187 function wrapModuleNameIfDefined(moduleName) {
10188 return moduleName ?
10189 (' (module: ' + moduleName + ')') :
10193 if (previousDirective) {
10194 throw $compileMinErr('multidir', 'Multiple directives [{0}{1}, {2}{3}] asking for {4} on: {5}',
10195 previousDirective.name, wrapModuleNameIfDefined(previousDirective.$$moduleName),
10196 directive.name, wrapModuleNameIfDefined(directive.$$moduleName), what, startingTag(element));
10201 function addTextInterpolateDirective(directives, text) {
10202 var interpolateFn = $interpolate(text, true);
10203 if (interpolateFn) {
10206 compile: function textInterpolateCompileFn(templateNode) {
10207 var templateNodeParent = templateNode.parent(),
10208 hasCompileParent = !!templateNodeParent.length;
10210 // When transcluding a template that has bindings in the root
10211 // we don't have a parent and thus need to add the class during linking fn.
10212 if (hasCompileParent) compile.$$addBindingClass(templateNodeParent);
10214 return function textInterpolateLinkFn(scope, node) {
10215 var parent = node.parent();
10216 if (!hasCompileParent) compile.$$addBindingClass(parent);
10217 compile.$$addBindingInfo(parent, interpolateFn.expressions);
10218 scope.$watch(interpolateFn, function interpolateFnWatchAction(value) {
10219 node[0].nodeValue = value;
10228 function wrapTemplate(type, template) {
10229 type = lowercase(type || 'html');
10233 var wrapper = window.document.createElement('div');
10234 wrapper.innerHTML = '<' + type + '>' + template + '</' + type + '>';
10235 return wrapper.childNodes[0].childNodes;
10242 function getTrustedContext(node, attrNormalizedName) {
10243 if (attrNormalizedName === 'srcdoc') {
10246 var tag = nodeName_(node);
10247 // All tags with src attributes require a RESOURCE_URL value, except for
10248 // img and various html5 media tags.
10249 if (attrNormalizedName === 'src' || attrNormalizedName === 'ngSrc') {
10250 if (['img', 'video', 'audio', 'source', 'track'].indexOf(tag) === -1) {
10251 return $sce.RESOURCE_URL;
10253 // maction[xlink:href] can source SVG. It's not limited to <maction>.
10254 } else if (attrNormalizedName === 'xlinkHref' ||
10255 (tag === 'form' && attrNormalizedName === 'action') ||
10256 // links can be stylesheets or imports, which can run script in the current origin
10257 (tag === 'link' && attrNormalizedName === 'href')
10259 return $sce.RESOURCE_URL;
10264 function addAttrInterpolateDirective(node, directives, value, name, isNgAttr) {
10265 var trustedContext = getTrustedContext(node, name);
10266 var mustHaveExpression = !isNgAttr;
10267 var allOrNothing = ALL_OR_NOTHING_ATTRS[name] || isNgAttr;
10269 var interpolateFn = $interpolate(value, mustHaveExpression, trustedContext, allOrNothing);
10271 // no interpolation found -> ignore
10272 if (!interpolateFn) return;
10274 if (name === 'multiple' && nodeName_(node) === 'select') {
10275 throw $compileMinErr('selmulti',
10276 'Binding to the \'multiple\' attribute is not supported. Element: {0}',
10277 startingTag(node));
10280 if (EVENT_HANDLER_ATTR_REGEXP.test(name)) {
10281 throw $compileMinErr('nodomevents',
10282 'Interpolations for HTML DOM event attributes are disallowed. Please use the ' +
10283 'ng- versions (such as ng-click instead of onclick) instead.');
10288 compile: function() {
10290 pre: function attrInterpolatePreLinkFn(scope, element, attr) {
10291 var $$observers = (attr.$$observers || (attr.$$observers = createMap()));
10293 // If the attribute has changed since last $interpolate()ed
10294 var newValue = attr[name];
10295 if (newValue !== value) {
10296 // we need to interpolate again since the attribute value has been updated
10297 // (e.g. by another directive's compile function)
10298 // ensure unset/empty values make interpolateFn falsy
10299 interpolateFn = newValue && $interpolate(newValue, true, trustedContext, allOrNothing);
10303 // if attribute was updated so that there is no interpolation going on we don't want to
10304 // register any observers
10305 if (!interpolateFn) return;
10307 // initialize attr object so that it's ready in case we need the value for isolate
10308 // scope initialization, otherwise the value would not be available from isolate
10309 // directive's linking fn during linking phase
10310 attr[name] = interpolateFn(scope);
10312 ($$observers[name] || ($$observers[name] = [])).$$inter = true;
10313 (attr.$$observers && attr.$$observers[name].$$scope || scope).
10314 $watch(interpolateFn, function interpolateFnWatchAction(newValue, oldValue) {
10315 //special case for class attribute addition + removal
10316 //so that class changes can tap into the animation
10317 //hooks provided by the $animate service. Be sure to
10318 //skip animations when the first digest occurs (when
10319 //both the new and the old values are the same) since
10320 //the CSS classes are the non-interpolated values
10321 if (name === 'class' && newValue !== oldValue) {
10322 attr.$updateClass(newValue, oldValue);
10324 attr.$set(name, newValue);
10335 * This is a special jqLite.replaceWith, which can replace items which
10336 * have no parents, provided that the containing jqLite collection is provided.
10338 * @param {JqLite=} $rootElement The root of the compile tree. Used so that we can replace nodes
10339 * in the root of the tree.
10340 * @param {JqLite} elementsToRemove The jqLite element which we are going to replace. We keep
10341 * the shell, but replace its DOM node reference.
10342 * @param {Node} newNode The new DOM node.
10344 function replaceWith($rootElement, elementsToRemove, newNode) {
10345 var firstElementToRemove = elementsToRemove[0],
10346 removeCount = elementsToRemove.length,
10347 parent = firstElementToRemove.parentNode,
10350 if ($rootElement) {
10351 for (i = 0, ii = $rootElement.length; i < ii; i++) {
10352 if ($rootElement[i] === firstElementToRemove) {
10353 $rootElement[i++] = newNode;
10354 for (var j = i, j2 = j + removeCount - 1,
10355 jj = $rootElement.length;
10356 j < jj; j++, j2++) {
10358 $rootElement[j] = $rootElement[j2];
10360 delete $rootElement[j];
10363 $rootElement.length -= removeCount - 1;
10365 // If the replaced element is also the jQuery .context then replace it
10366 // .context is a deprecated jQuery api, so we should set it only when jQuery set it
10367 // http://api.jquery.com/context/
10368 if ($rootElement.context === firstElementToRemove) {
10369 $rootElement.context = newNode;
10377 parent.replaceChild(newNode, firstElementToRemove);
10380 // Append all the `elementsToRemove` to a fragment. This will...
10381 // - remove them from the DOM
10382 // - allow them to still be traversed with .nextSibling
10383 // - allow a single fragment.qSA to fetch all elements being removed
10384 var fragment = window.document.createDocumentFragment();
10385 for (i = 0; i < removeCount; i++) {
10386 fragment.appendChild(elementsToRemove[i]);
10389 if (jqLite.hasData(firstElementToRemove)) {
10390 // Copy over user data (that includes Angular's $scope etc.). Don't copy private
10391 // data here because there's no public interface in jQuery to do that and copying over
10392 // event listeners (which is the main use of private data) wouldn't work anyway.
10393 jqLite.data(newNode, jqLite.data(firstElementToRemove));
10395 // Remove $destroy event listeners from `firstElementToRemove`
10396 jqLite(firstElementToRemove).off('$destroy');
10399 // Cleanup any data/listeners on the elements and children.
10400 // This includes invoking the $destroy event on any elements with listeners.
10401 jqLite.cleanData(fragment.querySelectorAll('*'));
10403 // Update the jqLite collection to only contain the `newNode`
10404 for (i = 1; i < removeCount; i++) {
10405 delete elementsToRemove[i];
10407 elementsToRemove[0] = newNode;
10408 elementsToRemove.length = 1;
10412 function cloneAndAnnotateFn(fn, annotation) {
10413 return extend(function() { return fn.apply(null, arguments); }, fn, annotation);
10417 function invokeLinkFn(linkFn, scope, $element, attrs, controllers, transcludeFn) {
10419 linkFn(scope, $element, attrs, controllers, transcludeFn);
10421 $exceptionHandler(e, startingTag($element));
10426 // Set up $watches for isolate scope and controller bindings.
10427 function initializeDirectiveBindings(scope, attrs, destination, bindings, directive) {
10428 var removeWatchCollection = [];
10429 var initialChanges = {};
10431 forEach(bindings, function initializeBinding(definition, scopeName) {
10432 var attrName = definition.attrName,
10433 optional = definition.optional,
10434 mode = definition.mode, // @, =, <, or &
10436 parentGet, parentSet, compare, removeWatch;
10441 if (!optional && !hasOwnProperty.call(attrs, attrName)) {
10442 destination[scopeName] = attrs[attrName] = undefined;
10444 removeWatch = attrs.$observe(attrName, function(value) {
10445 if (isString(value) || isBoolean(value)) {
10446 var oldValue = destination[scopeName];
10447 recordChanges(scopeName, value, oldValue);
10448 destination[scopeName] = value;
10451 attrs.$$observers[attrName].$$scope = scope;
10452 lastValue = attrs[attrName];
10453 if (isString(lastValue)) {
10454 // If the attribute has been provided then we trigger an interpolation to ensure
10455 // the value is there for use in the link fn
10456 destination[scopeName] = $interpolate(lastValue)(scope);
10457 } else if (isBoolean(lastValue)) {
10458 // If the attributes is one of the BOOLEAN_ATTR then Angular will have converted
10459 // the value to boolean rather than a string, so we special case this situation
10460 destination[scopeName] = lastValue;
10462 initialChanges[scopeName] = new SimpleChange(_UNINITIALIZED_VALUE, destination[scopeName]);
10463 removeWatchCollection.push(removeWatch);
10467 if (!hasOwnProperty.call(attrs, attrName)) {
10468 if (optional) break;
10469 attrs[attrName] = undefined;
10471 if (optional && !attrs[attrName]) break;
10473 parentGet = $parse(attrs[attrName]);
10474 if (parentGet.literal) {
10477 // eslint-disable-next-line no-self-compare
10478 compare = function simpleCompare(a, b) { return a === b || (a !== a && b !== b); };
10480 parentSet = parentGet.assign || function() {
10481 // reset the change, or we will throw this exception on every $digest
10482 lastValue = destination[scopeName] = parentGet(scope);
10483 throw $compileMinErr('nonassign',
10484 'Expression \'{0}\' in attribute \'{1}\' used with directive \'{2}\' is non-assignable!',
10485 attrs[attrName], attrName, directive.name);
10487 lastValue = destination[scopeName] = parentGet(scope);
10488 var parentValueWatch = function parentValueWatch(parentValue) {
10489 if (!compare(parentValue, destination[scopeName])) {
10490 // we are out of sync and need to copy
10491 if (!compare(parentValue, lastValue)) {
10492 // parent changed and it has precedence
10493 destination[scopeName] = parentValue;
10495 // if the parent can be assigned then do so
10496 parentSet(scope, parentValue = destination[scopeName]);
10499 lastValue = parentValue;
10502 parentValueWatch.$stateful = true;
10503 if (definition.collection) {
10504 removeWatch = scope.$watchCollection(attrs[attrName], parentValueWatch);
10506 removeWatch = scope.$watch($parse(attrs[attrName], parentValueWatch), null, parentGet.literal);
10508 removeWatchCollection.push(removeWatch);
10512 if (!hasOwnProperty.call(attrs, attrName)) {
10513 if (optional) break;
10514 attrs[attrName] = undefined;
10516 if (optional && !attrs[attrName]) break;
10518 parentGet = $parse(attrs[attrName]);
10519 var deepWatch = parentGet.literal;
10521 var initialValue = destination[scopeName] = parentGet(scope);
10522 initialChanges[scopeName] = new SimpleChange(_UNINITIALIZED_VALUE, destination[scopeName]);
10524 removeWatch = scope.$watch(parentGet, function parentValueWatchAction(newValue, oldValue) {
10525 if (oldValue === newValue) {
10526 if (oldValue === initialValue || (deepWatch && equals(oldValue, initialValue))) {
10529 oldValue = initialValue;
10531 recordChanges(scopeName, newValue, oldValue);
10532 destination[scopeName] = newValue;
10535 removeWatchCollection.push(removeWatch);
10539 // Don't assign Object.prototype method to scope
10540 parentGet = attrs.hasOwnProperty(attrName) ? $parse(attrs[attrName]) : noop;
10542 // Don't assign noop to destination if expression is not valid
10543 if (parentGet === noop && optional) break;
10545 destination[scopeName] = function(locals) {
10546 return parentGet(scope, locals);
10552 function recordChanges(key, currentValue, previousValue) {
10553 if (isFunction(destination.$onChanges) && currentValue !== previousValue &&
10554 // eslint-disable-next-line no-self-compare
10555 (currentValue === currentValue || previousValue === previousValue)) {
10556 // If we have not already scheduled the top level onChangesQueue handler then do so now
10557 if (!onChangesQueue) {
10558 scope.$$postDigest(flushOnChangesQueue);
10559 onChangesQueue = [];
10561 // If we have not already queued a trigger of onChanges for this controller then do so now
10564 onChangesQueue.push(triggerOnChangesHook);
10566 // If the has been a change on this property already then we need to reuse the previous value
10567 if (changes[key]) {
10568 previousValue = changes[key].previousValue;
10570 // Store this change
10571 changes[key] = new SimpleChange(previousValue, currentValue);
10575 function triggerOnChangesHook() {
10576 destination.$onChanges(changes);
10577 // Now clear the changes so that we schedule onChanges when more changes arrive
10578 changes = undefined;
10582 initialChanges: initialChanges,
10583 removeWatches: removeWatchCollection.length && function removeWatches() {
10584 for (var i = 0, ii = removeWatchCollection.length; i < ii; ++i) {
10585 removeWatchCollection[i]();
10593 function SimpleChange(previous, current) {
10594 this.previousValue = previous;
10595 this.currentValue = current;
10597 SimpleChange.prototype.isFirstChange = function() { return this.previousValue === _UNINITIALIZED_VALUE; };
10600 var PREFIX_REGEXP = /^((?:x|data)[:\-_])/i;
10601 var SPECIAL_CHARS_REGEXP = /[:\-_]+(.)/g;
10604 * Converts all accepted directives format into proper directive name.
10605 * @param name Name to normalize
10607 function directiveNormalize(name) {
10609 .replace(PREFIX_REGEXP, '')
10610 .replace(SPECIAL_CHARS_REGEXP, fnCamelCaseReplace);
10615 * @name $compile.directive.Attributes
10618 * A shared object between directive compile / linking functions which contains normalized DOM
10619 * element attributes. The values reflect current binding state `{{ }}`. The normalization is
10620 * needed since all of these are treated as equivalent in Angular:
10623 * <span ng:bind="a" ng-bind="a" data-ng-bind="a" x-ng-bind="a">
10629 * @name $compile.directive.Attributes#$attr
10632 * A map of DOM element attribute names to the normalized name. This is
10633 * needed to do reverse lookup from normalized name back to actual name.
10639 * @name $compile.directive.Attributes#$set
10643 * Set DOM element attribute value.
10646 * @param {string} name Normalized element attribute name of the property to modify. The name is
10647 * reverse-translated using the {@link ng.$compile.directive.Attributes#$attr $attr}
10648 * property to the original name.
10649 * @param {string} value Value to set the attribute to. The value can be an interpolated string.
10655 * Closure compiler type information
10658 function nodesetLinkingFn(
10659 /* angular.Scope */ scope,
10660 /* NodeList */ nodeList,
10661 /* Element */ rootElement,
10662 /* function(Function) */ boundTranscludeFn
10665 function directiveLinkingFn(
10666 /* nodesetLinkingFn */ nodesetLinkingFn,
10667 /* angular.Scope */ scope,
10669 /* Element */ rootElement,
10670 /* function(Function) */ boundTranscludeFn
10673 function tokenDifference(str1, str2) {
10675 tokens1 = str1.split(/\s+/),
10676 tokens2 = str2.split(/\s+/);
10679 for (var i = 0; i < tokens1.length; i++) {
10680 var token = tokens1[i];
10681 for (var j = 0; j < tokens2.length; j++) {
10682 if (token === tokens2[j]) continue outer;
10684 values += (values.length > 0 ? ' ' : '') + token;
10689 function removeComments(jqNodes) {
10690 jqNodes = jqLite(jqNodes);
10691 var i = jqNodes.length;
10698 var node = jqNodes[i];
10699 if (node.nodeType === NODE_TYPE_COMMENT ||
10700 (node.nodeType === NODE_TYPE_TEXT && node.nodeValue.trim() === '')) {
10701 splice.call(jqNodes, i, 1);
10707 var $controllerMinErr = minErr('$controller');
10710 var CNTRL_REG = /^(\S+)(\s+as\s+([\w$]+))?$/;
10711 function identifierForController(controller, ident) {
10712 if (ident && isString(ident)) return ident;
10713 if (isString(controller)) {
10714 var match = CNTRL_REG.exec(controller);
10715 if (match) return match[3];
10722 * @name $controllerProvider
10726 * The {@link ng.$controller $controller service} is used by Angular to create new
10729 * This provider allows controller registration via the
10730 * {@link ng.$controllerProvider#register register} method.
10732 function $ControllerProvider() {
10733 var controllers = {},
10738 * @name $controllerProvider#has
10739 * @param {string} name Controller name to check.
10741 this.has = function(name) {
10742 return controllers.hasOwnProperty(name);
10747 * @name $controllerProvider#register
10748 * @param {string|Object} name Controller name, or an object map of controllers where the keys are
10749 * the names and the values are the constructors.
10750 * @param {Function|Array} constructor Controller constructor fn (optionally decorated with DI
10751 * annotations in the array notation).
10753 this.register = function(name, constructor) {
10754 assertNotHasOwnProperty(name, 'controller');
10755 if (isObject(name)) {
10756 extend(controllers, name);
10758 controllers[name] = constructor;
10764 * @name $controllerProvider#allowGlobals
10765 * @description If called, allows `$controller` to find controller constructors on `window`
10768 * sinceVersion="v1.3.0"
10769 * removeVersion="v1.7.0"
10770 * This method of finding controllers has been deprecated.
10772 this.allowGlobals = function() {
10777 this.$get = ['$injector', '$window', function($injector, $window) {
10781 * @name $controller
10782 * @requires $injector
10784 * @param {Function|string} constructor If called with a function then it's considered to be the
10785 * controller constructor function. Otherwise it's considered to be a string which is used
10786 * to retrieve the controller constructor using the following steps:
10788 * * check if a controller with given name is registered via `$controllerProvider`
10789 * * check if evaluating the string on the current scope returns a constructor
10790 * * if $controllerProvider#allowGlobals, check `window[constructor]` on the global
10791 * `window` object (deprecated, not recommended)
10793 * The string can use the `controller as property` syntax, where the controller instance is published
10794 * as the specified property on the `scope`; the `scope` must be injected into `locals` param for this
10795 * to work correctly.
10797 * @param {Object} locals Injection locals for Controller.
10798 * @return {Object} Instance of given controller.
10801 * `$controller` service is responsible for instantiating controllers.
10803 * It's just a simple call to {@link auto.$injector $injector}, but extracted into
10804 * a service, so that one can override this service with [BC version](https://gist.github.com/1649788).
10806 return function $controller(expression, locals, later, ident) {
10808 // param `later` --- indicates that the controller's constructor is invoked at a later time.
10809 // If true, $controller will allocate the object with the correct
10810 // prototype chain, but will not invoke the controller until a returned
10811 // callback is invoked.
10812 // param `ident` --- An optional label which overrides the label parsed from the controller
10813 // expression, if any.
10814 var instance, match, constructor, identifier;
10815 later = later === true;
10816 if (ident && isString(ident)) {
10817 identifier = ident;
10820 if (isString(expression)) {
10821 match = expression.match(CNTRL_REG);
10823 throw $controllerMinErr('ctrlfmt',
10824 'Badly formed controller string \'{0}\'. ' +
10825 'Must match `__name__ as __id__` or `__name__`.', expression);
10827 constructor = match[1];
10828 identifier = identifier || match[3];
10829 expression = controllers.hasOwnProperty(constructor)
10830 ? controllers[constructor]
10831 : getter(locals.$scope, constructor, true) ||
10832 (globals ? getter($window, constructor, true) : undefined);
10835 throw $controllerMinErr('ctrlreg',
10836 'The controller with the name \'{0}\' is not registered.', constructor);
10839 assertArgFn(expression, constructor, true);
10843 // Instantiate controller later:
10844 // This machinery is used to create an instance of the object before calling the
10845 // controller's constructor itself.
10847 // This allows properties to be added to the controller before the constructor is
10848 // invoked. Primarily, this is used for isolate scope bindings in $compile.
10850 // This feature is not intended for use by applications, and is thus not documented
10852 // Object creation: http://jsperf.com/create-constructor/2
10853 var controllerPrototype = (isArray(expression) ?
10854 expression[expression.length - 1] : expression).prototype;
10855 instance = Object.create(controllerPrototype || null);
10858 addIdentifier(locals, identifier, instance, constructor || expression.name);
10861 return extend(function $controllerInit() {
10862 var result = $injector.invoke(expression, instance, locals, constructor);
10863 if (result !== instance && (isObject(result) || isFunction(result))) {
10866 // If result changed, re-assign controllerAs value to scope.
10867 addIdentifier(locals, identifier, instance, constructor || expression.name);
10872 instance: instance,
10873 identifier: identifier
10877 instance = $injector.instantiate(expression, locals, constructor);
10880 addIdentifier(locals, identifier, instance, constructor || expression.name);
10886 function addIdentifier(locals, identifier, instance, name) {
10887 if (!(locals && isObject(locals.$scope))) {
10888 throw minErr('$controller')('noscp',
10889 'Cannot export controller \'{0}\' as \'{1}\'! No $scope object provided via `locals`.',
10893 locals.$scope[identifier] = instance;
10901 * @requires $window
10905 * A {@link angular.element jQuery or jqLite} wrapper for the browser's `window.document` object.
10908 <example module="documentExample" name="document">
10909 <file name="index.html">
10910 <div ng-controller="ExampleController">
10911 <p>$document title: <b ng-bind="title"></b></p>
10912 <p>window.document title: <b ng-bind="windowTitle"></b></p>
10915 <file name="script.js">
10916 angular.module('documentExample', [])
10917 .controller('ExampleController', ['$scope', '$document', function($scope, $document) {
10918 $scope.title = $document[0].title;
10919 $scope.windowTitle = angular.element(window.document)[0].title;
10924 function $DocumentProvider() {
10925 this.$get = ['$window', function(window) {
10926 return jqLite(window.document);
10934 * Listens for document visibility change and makes the current status accessible.
10936 function $$IsDocumentHiddenProvider() {
10937 this.$get = ['$document', '$rootScope', function($document, $rootScope) {
10938 var doc = $document[0];
10939 var hidden = doc && doc.hidden;
10941 $document.on('visibilitychange', changeListener);
10943 $rootScope.$on('$destroy', function() {
10944 $document.off('visibilitychange', changeListener);
10947 function changeListener() {
10948 hidden = doc.hidden;
10951 return function() {
10959 * @name $exceptionHandler
10960 * @requires ng.$log
10964 * Any uncaught exception in angular expressions is delegated to this service.
10965 * The default implementation simply delegates to `$log.error` which logs it into
10966 * the browser console.
10968 * In unit tests, if `angular-mocks.js` is loaded, this service is overridden by
10969 * {@link ngMock.$exceptionHandler mock $exceptionHandler} which aids in testing.
10973 * The example below will overwrite the default `$exceptionHandler` in order to (a) log uncaught
10974 * errors to the backend for later inspection by the developers and (b) to use `$log.warn()` instead
10975 * of `$log.error()`.
10979 * module('exceptionOverwrite', []).
10980 * factory('$exceptionHandler', ['$log', 'logErrorsToBackend', function($log, logErrorsToBackend) {
10981 * return function myExceptionHandler(exception, cause) {
10982 * logErrorsToBackend(exception, cause);
10983 * $log.warn(exception, cause);
10989 * Note, that code executed in event-listeners (even those registered using jqLite's `on`/`bind`
10990 * methods) does not delegate exceptions to the {@link ng.$exceptionHandler $exceptionHandler}
10991 * (unless executed during a digest).
10993 * If you wish, you can manually delegate exceptions, e.g.
10994 * `try { ... } catch(e) { $exceptionHandler(e); }`
10996 * @param {Error} exception Exception associated with the error.
10997 * @param {string=} cause Optional information about the context in which
10998 * the error was thrown.
11001 function $ExceptionHandlerProvider() {
11002 this.$get = ['$log', function($log) {
11003 return function(exception, cause) {
11004 $log.error.apply($log, arguments);
11009 var $$ForceReflowProvider = /** @this */ function() {
11010 this.$get = ['$document', function($document) {
11011 return function(domNode) {
11012 //the line below will force the browser to perform a repaint so
11013 //that all the animated elements within the animation frame will
11014 //be properly updated and drawn on screen. This is required to
11015 //ensure that the preparation animation is properly flushed so that
11016 //the active state picks up from there. DO NOT REMOVE THIS LINE.
11017 //DO NOT OPTIMIZE THIS LINE. THE MINIFIER WILL REMOVE IT OTHERWISE WHICH
11018 //WILL RESULT IN AN UNPREDICTABLE BUG THAT IS VERY HARD TO TRACK DOWN AND
11019 //WILL TAKE YEARS AWAY FROM YOUR LIFE.
11021 if (!domNode.nodeType && domNode instanceof jqLite) {
11022 domNode = domNode[0];
11025 domNode = $document[0].body;
11027 return domNode.offsetWidth + 1;
11032 var APPLICATION_JSON = 'application/json';
11033 var CONTENT_TYPE_APPLICATION_JSON = {'Content-Type': APPLICATION_JSON + ';charset=utf-8'};
11034 var JSON_START = /^\[|^\{(?!\{)/;
11039 var JSON_PROTECTION_PREFIX = /^\)]\}',?\n/;
11040 var $httpMinErr = minErr('$http');
11042 function serializeValue(v) {
11044 return isDate(v) ? v.toISOString() : toJson(v);
11051 function $HttpParamSerializerProvider() {
11054 * @name $httpParamSerializer
11057 * Default {@link $http `$http`} params serializer that converts objects to strings
11058 * according to the following rules:
11060 * * `{'foo': 'bar'}` results in `foo=bar`
11061 * * `{'foo': Date.now()}` results in `foo=2015-04-01T09%3A50%3A49.262Z` (`toISOString()` and encoded representation of a Date object)
11062 * * `{'foo': ['bar', 'baz']}` results in `foo=bar&foo=baz` (repeated key for each array element)
11063 * * `{'foo': {'bar':'baz'}}` results in `foo=%7B%22bar%22%3A%22baz%22%7D` (stringified and encoded representation of an object)
11065 * Note that serializer will sort the request parameters alphabetically.
11068 this.$get = function() {
11069 return function ngParamSerializer(params) {
11070 if (!params) return '';
11072 forEachSorted(params, function(value, key) {
11073 if (value === null || isUndefined(value)) return;
11074 if (isArray(value)) {
11075 forEach(value, function(v) {
11076 parts.push(encodeUriQuery(key) + '=' + encodeUriQuery(serializeValue(v)));
11079 parts.push(encodeUriQuery(key) + '=' + encodeUriQuery(serializeValue(value)));
11083 return parts.join('&');
11089 function $HttpParamSerializerJQLikeProvider() {
11092 * @name $httpParamSerializerJQLike
11096 * Alternative {@link $http `$http`} params serializer that follows
11097 * jQuery's [`param()`](http://api.jquery.com/jquery.param/) method logic.
11098 * The serializer will also sort the params alphabetically.
11100 * To use it for serializing `$http` request parameters, set it as the `paramSerializer` property:
11106 * params: myParams,
11107 * paramSerializer: '$httpParamSerializerJQLike'
11111 * It is also possible to set it as the default `paramSerializer` in the
11112 * {@link $httpProvider#defaults `$httpProvider`}.
11114 * Additionally, you can inject the serializer and use it explicitly, for example to serialize
11115 * form data for submission:
11118 * .controller(function($http, $httpParamSerializerJQLike) {
11124 * data: $httpParamSerializerJQLike(myData),
11126 * 'Content-Type': 'application/x-www-form-urlencoded'
11134 this.$get = function() {
11135 return function jQueryLikeParamSerializer(params) {
11136 if (!params) return '';
11138 serialize(params, '', true);
11139 return parts.join('&');
11141 function serialize(toSerialize, prefix, topLevel) {
11142 if (toSerialize === null || isUndefined(toSerialize)) return;
11143 if (isArray(toSerialize)) {
11144 forEach(toSerialize, function(value, index) {
11145 serialize(value, prefix + '[' + (isObject(value) ? index : '') + ']');
11147 } else if (isObject(toSerialize) && !isDate(toSerialize)) {
11148 forEachSorted(toSerialize, function(value, key) {
11149 serialize(value, prefix +
11150 (topLevel ? '' : '[') +
11152 (topLevel ? '' : ']'));
11155 parts.push(encodeUriQuery(prefix) + '=' + encodeUriQuery(serializeValue(toSerialize)));
11162 function defaultHttpResponseTransform(data, headers) {
11163 if (isString(data)) {
11164 // Strip json vulnerability protection prefix and trim whitespace
11165 var tempData = data.replace(JSON_PROTECTION_PREFIX, '').trim();
11168 var contentType = headers('Content-Type');
11169 if ((contentType && (contentType.indexOf(APPLICATION_JSON) === 0)) || isJsonLike(tempData)) {
11170 data = fromJson(tempData);
11178 function isJsonLike(str) {
11179 var jsonStart = str.match(JSON_START);
11180 return jsonStart && JSON_ENDS[jsonStart[0]].test(str);
11184 * Parse headers into key value object
11186 * @param {string} headers Raw headers as a string
11187 * @returns {Object} Parsed headers as key value object
11189 function parseHeaders(headers) {
11190 var parsed = createMap(), i;
11192 function fillInParsed(key, val) {
11194 parsed[key] = parsed[key] ? parsed[key] + ', ' + val : val;
11198 if (isString(headers)) {
11199 forEach(headers.split('\n'), function(line) {
11200 i = line.indexOf(':');
11201 fillInParsed(lowercase(trim(line.substr(0, i))), trim(line.substr(i + 1)));
11203 } else if (isObject(headers)) {
11204 forEach(headers, function(headerVal, headerKey) {
11205 fillInParsed(lowercase(headerKey), trim(headerVal));
11214 * Returns a function that provides access to parsed headers.
11216 * Headers are lazy parsed when first requested.
11217 * @see parseHeaders
11219 * @param {(string|Object)} headers Headers to provide access to.
11220 * @returns {function(string=)} Returns a getter function which if called with:
11222 * - if called with an argument returns a single header value or null
11223 * - if called with no arguments returns an object containing all headers.
11225 function headersGetter(headers) {
11228 return function(name) {
11229 if (!headersObj) headersObj = parseHeaders(headers);
11232 var value = headersObj[lowercase(name)];
11233 if (value === undefined) {
11245 * Chain all given functions
11247 * This function is used for both request and response transforming
11249 * @param {*} data Data to transform.
11250 * @param {function(string=)} headers HTTP headers getter fn.
11251 * @param {number} status HTTP status code of the response.
11252 * @param {(Function|Array.<Function>)} fns Function or an array of functions.
11253 * @returns {*} Transformed data.
11255 function transformData(data, headers, status, fns) {
11256 if (isFunction(fns)) {
11257 return fns(data, headers, status);
11260 forEach(fns, function(fn) {
11261 data = fn(data, headers, status);
11268 function isSuccess(status) {
11269 return 200 <= status && status < 300;
11275 * @name $httpProvider
11279 * Use `$httpProvider` to change the default behavior of the {@link ng.$http $http} service.
11281 function $HttpProvider() {
11284 * @name $httpProvider#defaults
11287 * Object containing default values for all {@link ng.$http $http} requests.
11289 * - **`defaults.cache`** - {boolean|Object} - A boolean value or object created with
11290 * {@link ng.$cacheFactory `$cacheFactory`} to enable or disable caching of HTTP responses
11291 * by default. See {@link $http#caching $http Caching} for more information.
11293 * - **`defaults.xsrfCookieName`** - {string} - Name of cookie containing the XSRF token.
11294 * Defaults value is `'XSRF-TOKEN'`.
11296 * - **`defaults.xsrfHeaderName`** - {string} - Name of HTTP header to populate with the
11297 * XSRF token. Defaults value is `'X-XSRF-TOKEN'`.
11299 * - **`defaults.headers`** - {Object} - Default headers for all $http requests.
11300 * Refer to {@link ng.$http#setting-http-headers $http} for documentation on
11301 * setting default headers.
11302 * - **`defaults.headers.common`**
11303 * - **`defaults.headers.post`**
11304 * - **`defaults.headers.put`**
11305 * - **`defaults.headers.patch`**
11308 * - **`defaults.paramSerializer`** - `{string|function(Object<string,string>):string}` - A function
11309 * used to the prepare string representation of request parameters (specified as an object).
11310 * If specified as string, it is interpreted as a function registered with the {@link auto.$injector $injector}.
11311 * Defaults to {@link ng.$httpParamSerializer $httpParamSerializer}.
11313 * - **`defaults.jsonpCallbackParam`** - `{string}` - the name of the query parameter that passes the name of the
11314 * callback in a JSONP request. The value of this parameter will be replaced with the expression generated by the
11315 * {@link $jsonpCallbacks} service. Defaults to `'callback'`.
11318 var defaults = this.defaults = {
11319 // transform incoming response data
11320 transformResponse: [defaultHttpResponseTransform],
11322 // transform outgoing request data
11323 transformRequest: [function(d) {
11324 return isObject(d) && !isFile(d) && !isBlob(d) && !isFormData(d) ? toJson(d) : d;
11330 'Accept': 'application/json, text/plain, */*'
11332 post: shallowCopy(CONTENT_TYPE_APPLICATION_JSON),
11333 put: shallowCopy(CONTENT_TYPE_APPLICATION_JSON),
11334 patch: shallowCopy(CONTENT_TYPE_APPLICATION_JSON)
11337 xsrfCookieName: 'XSRF-TOKEN',
11338 xsrfHeaderName: 'X-XSRF-TOKEN',
11340 paramSerializer: '$httpParamSerializer',
11342 jsonpCallbackParam: 'callback'
11345 var useApplyAsync = false;
11348 * @name $httpProvider#useApplyAsync
11351 * Configure $http service to combine processing of multiple http responses received at around
11352 * the same time via {@link ng.$rootScope.Scope#$applyAsync $rootScope.$applyAsync}. This can result in
11353 * significant performance improvement for bigger applications that make many HTTP requests
11354 * concurrently (common during application bootstrap).
11356 * Defaults to false. If no value is specified, returns the current configured value.
11358 * @param {boolean=} value If true, when requests are loaded, they will schedule a deferred
11359 * "apply" on the next tick, giving time for subsequent requests in a roughly ~10ms window
11360 * to load and share the same digest cycle.
11362 * @returns {boolean|Object} If a value is specified, returns the $httpProvider for chaining.
11363 * otherwise, returns the current configured value.
11365 this.useApplyAsync = function(value) {
11366 if (isDefined(value)) {
11367 useApplyAsync = !!value;
11370 return useApplyAsync;
11375 * @name $httpProvider#interceptors
11378 * Array containing service factories for all synchronous or asynchronous {@link ng.$http $http}
11379 * pre-processing of request or postprocessing of responses.
11381 * These service factories are ordered by request, i.e. they are applied in the same order as the
11382 * array, on request, but reverse order, on response.
11384 * {@link ng.$http#interceptors Interceptors detailed info}
11386 var interceptorFactories = this.interceptors = [];
11388 this.$get = ['$browser', '$httpBackend', '$$cookieReader', '$cacheFactory', '$rootScope', '$q', '$injector', '$sce',
11389 function($browser, $httpBackend, $$cookieReader, $cacheFactory, $rootScope, $q, $injector, $sce) {
11391 var defaultCache = $cacheFactory('$http');
11394 * Make sure that default param serializer is exposed as a function
11396 defaults.paramSerializer = isString(defaults.paramSerializer) ?
11397 $injector.get(defaults.paramSerializer) : defaults.paramSerializer;
11400 * Interceptors stored in reverse order. Inner interceptors before outer interceptors.
11401 * The reversal is needed so that we can build up the interception chain around the
11404 var reversedInterceptors = [];
11406 forEach(interceptorFactories, function(interceptorFactory) {
11407 reversedInterceptors.unshift(isString(interceptorFactory)
11408 ? $injector.get(interceptorFactory) : $injector.invoke(interceptorFactory));
11415 * @requires ng.$httpBackend
11416 * @requires $cacheFactory
11417 * @requires $rootScope
11419 * @requires $injector
11422 * The `$http` service is a core Angular service that facilitates communication with the remote
11423 * HTTP servers via the browser's [XMLHttpRequest](https://developer.mozilla.org/en/xmlhttprequest)
11424 * object or via [JSONP](http://en.wikipedia.org/wiki/JSONP).
11426 * For unit testing applications that use `$http` service, see
11427 * {@link ngMock.$httpBackend $httpBackend mock}.
11429 * For a higher level of abstraction, please check out the {@link ngResource.$resource
11430 * $resource} service.
11432 * The $http API is based on the {@link ng.$q deferred/promise APIs} exposed by
11433 * the $q service. While for simple usage patterns this doesn't matter much, for advanced usage
11434 * it is important to familiarize yourself with these APIs and the guarantees they provide.
11438 * The `$http` service is a function which takes a single argument — a {@link $http#usage configuration object} —
11439 * that is used to generate an HTTP request and returns a {@link ng.$q promise}.
11442 * // Simple GET request example:
11446 * }).then(function successCallback(response) {
11447 * // this callback will be called asynchronously
11448 * // when the response is available
11449 * }, function errorCallback(response) {
11450 * // called asynchronously if an error occurs
11451 * // or server returns response with an error status.
11455 * The response object has these properties:
11457 * - **data** – `{string|Object}` – The response body transformed with the transform
11459 * - **status** – `{number}` – HTTP status code of the response.
11460 * - **headers** – `{function([headerName])}` – Header getter function.
11461 * - **config** – `{Object}` – The configuration object that was used to generate the request.
11462 * - **statusText** – `{string}` – HTTP status text of the response.
11464 * A response status code between 200 and 299 is considered a success status and will result in
11465 * the success callback being called. Any response status code outside of that range is
11466 * considered an error status and will result in the error callback being called.
11467 * Also, status codes less than -1 are normalized to zero. -1 usually means the request was
11468 * aborted, e.g. using a `config.timeout`.
11469 * Note that if the response is a redirect, XMLHttpRequest will transparently follow it, meaning
11470 * that the outcome (success or error) will be determined by the final response status code.
11473 * ## Shortcut methods
11475 * Shortcut methods are also available. All shortcut methods require passing in the URL, and
11476 * request data must be passed in for POST/PUT requests. An optional config can be passed as the
11480 * $http.get('/someUrl', config).then(successCallback, errorCallback);
11481 * $http.post('/someUrl', data, config).then(successCallback, errorCallback);
11484 * Complete list of shortcut methods:
11486 * - {@link ng.$http#get $http.get}
11487 * - {@link ng.$http#head $http.head}
11488 * - {@link ng.$http#post $http.post}
11489 * - {@link ng.$http#put $http.put}
11490 * - {@link ng.$http#delete $http.delete}
11491 * - {@link ng.$http#jsonp $http.jsonp}
11492 * - {@link ng.$http#patch $http.patch}
11495 * ## Writing Unit Tests that use $http
11496 * When unit testing (using {@link ngMock ngMock}), it is necessary to call
11497 * {@link ngMock.$httpBackend#flush $httpBackend.flush()} to flush each pending
11498 * request using trained responses.
11501 * $httpBackend.expectGET(...);
11503 * $httpBackend.flush();
11506 * ## Setting HTTP Headers
11508 * The $http service will automatically add certain HTTP headers to all requests. These defaults
11509 * can be fully configured by accessing the `$httpProvider.defaults.headers` configuration
11510 * object, which currently contains this default configuration:
11512 * - `$httpProvider.defaults.headers.common` (headers that are common for all requests):
11513 * - <code>Accept: application/json, text/plain, \*/\*</code>
11514 * - `$httpProvider.defaults.headers.post`: (header defaults for POST requests)
11515 * - `Content-Type: application/json`
11516 * - `$httpProvider.defaults.headers.put` (header defaults for PUT requests)
11517 * - `Content-Type: application/json`
11519 * To add or overwrite these defaults, simply add or remove a property from these configuration
11520 * objects. To add headers for an HTTP method other than POST or PUT, simply add a new object
11521 * with the lowercased HTTP method name as the key, e.g.
11522 * `$httpProvider.defaults.headers.get = { 'My-Header' : 'value' }`.
11524 * The defaults can also be set at runtime via the `$http.defaults` object in the same
11525 * fashion. For example:
11528 * module.run(function($http) {
11529 * $http.defaults.headers.common.Authorization = 'Basic YmVlcDpib29w';
11533 * In addition, you can supply a `headers` property in the config object passed when
11534 * calling `$http(config)`, which overrides the defaults without changing them globally.
11536 * To explicitly remove a header automatically added via $httpProvider.defaults.headers on a per request basis,
11537 * Use the `headers` property, setting the desired header to `undefined`. For example:
11542 * url: 'http://example.com',
11544 * 'Content-Type': undefined
11546 * data: { test: 'test' }
11549 * $http(req).then(function(){...}, function(){...});
11552 * ## Transforming Requests and Responses
11554 * Both requests and responses can be transformed using transformation functions: `transformRequest`
11555 * and `transformResponse`. These properties can be a single function that returns
11556 * the transformed value (`function(data, headersGetter, status)`) or an array of such transformation functions,
11557 * which allows you to `push` or `unshift` a new transformation function into the transformation chain.
11559 * <div class="alert alert-warning">
11560 * **Note:** Angular does not make a copy of the `data` parameter before it is passed into the `transformRequest` pipeline.
11561 * That means changes to the properties of `data` are not local to the transform function (since Javascript passes objects by reference).
11562 * For example, when calling `$http.get(url, $scope.myObject)`, modifications to the object's properties in a transformRequest
11563 * function will be reflected on the scope and in any templates where the object is data-bound.
11564 * To prevent this, transform functions should have no side-effects.
11565 * If you need to modify properties, it is recommended to make a copy of the data, or create new object to return.
11568 * ### Default Transformations
11570 * The `$httpProvider` provider and `$http` service expose `defaults.transformRequest` and
11571 * `defaults.transformResponse` properties. If a request does not provide its own transformations
11572 * then these will be applied.
11574 * You can augment or replace the default transformations by modifying these properties by adding to or
11575 * replacing the array.
11577 * Angular provides the following default transformations:
11579 * Request transformations (`$httpProvider.defaults.transformRequest` and `$http.defaults.transformRequest`):
11581 * - If the `data` property of the request configuration object contains an object, serialize it
11582 * into JSON format.
11584 * Response transformations (`$httpProvider.defaults.transformResponse` and `$http.defaults.transformResponse`):
11586 * - If XSRF prefix is detected, strip it (see Security Considerations section below).
11587 * - If JSON response is detected, deserialize it using a JSON parser.
11590 * ### Overriding the Default Transformations Per Request
11592 * If you wish to override the request/response transformations only for a single request then provide
11593 * `transformRequest` and/or `transformResponse` properties on the configuration object passed
11596 * Note that if you provide these properties on the config object the default transformations will be
11597 * overwritten. If you wish to augment the default transformations then you must include them in your
11598 * local transformation array.
11600 * The following code demonstrates adding a new response transformation to be run after the default response
11601 * transformations have been run.
11604 * function appendTransform(defaults, transform) {
11606 * // We can't guarantee that the default transformation is an array
11607 * defaults = angular.isArray(defaults) ? defaults : [defaults];
11609 * // Append the new transformation to the defaults
11610 * return defaults.concat(transform);
11616 * transformResponse: appendTransform($http.defaults.transformResponse, function(value) {
11617 * return doTransform(value);
11625 * {@link ng.$http `$http`} responses are not cached by default. To enable caching, you must
11626 * set the config.cache value or the default cache value to TRUE or to a cache object (created
11627 * with {@link ng.$cacheFactory `$cacheFactory`}). If defined, the value of config.cache takes
11628 * precedence over the default cache value.
11631 * * cache all responses - set the default cache value to TRUE or to a cache object
11632 * * cache a specific response - set config.cache value to TRUE or to a cache object
11634 * If caching is enabled, but neither the default cache nor config.cache are set to a cache object,
11635 * then the default `$cacheFactory("$http")` object is used.
11637 * The default cache value can be set by updating the
11638 * {@link ng.$http#defaults `$http.defaults.cache`} property or the
11639 * {@link $httpProvider#defaults `$httpProvider.defaults.cache`} property.
11641 * When caching is enabled, {@link ng.$http `$http`} stores the response from the server using
11642 * the relevant cache object. The next time the same request is made, the response is returned
11643 * from the cache without sending a request to the server.
11647 * * Only GET and JSONP requests are cached.
11648 * * The cache key is the request URL including search parameters; headers are not considered.
11649 * * Cached responses are returned asynchronously, in the same way as responses from the server.
11650 * * If multiple identical requests are made using the same cache, which is not yet populated,
11651 * one request will be made to the server and remaining requests will return the same response.
11652 * * A cache-control header on the response does not affect if or how responses are cached.
11657 * Before you start creating interceptors, be sure to understand the
11658 * {@link ng.$q $q and deferred/promise APIs}.
11660 * For purposes of global error handling, authentication, or any kind of synchronous or
11661 * asynchronous pre-processing of request or postprocessing of responses, it is desirable to be
11662 * able to intercept requests before they are handed to the server and
11663 * responses before they are handed over to the application code that
11664 * initiated these requests. The interceptors leverage the {@link ng.$q
11665 * promise APIs} to fulfill this need for both synchronous and asynchronous pre-processing.
11667 * The interceptors are service factories that are registered with the `$httpProvider` by
11668 * adding them to the `$httpProvider.interceptors` array. The factory is called and
11669 * injected with dependencies (if specified) and returns the interceptor.
11671 * There are two kinds of interceptors (and two kinds of rejection interceptors):
11673 * * `request`: interceptors get called with a http {@link $http#usage config} object. The function is free to
11674 * modify the `config` object or create a new one. The function needs to return the `config`
11675 * object directly, or a promise containing the `config` or a new `config` object.
11676 * * `requestError`: interceptor gets called when a previous interceptor threw an error or
11677 * resolved with a rejection.
11678 * * `response`: interceptors get called with http `response` object. The function is free to
11679 * modify the `response` object or create a new one. The function needs to return the `response`
11680 * object directly, or as a promise containing the `response` or a new `response` object.
11681 * * `responseError`: interceptor gets called when a previous interceptor threw an error or
11682 * resolved with a rejection.
11686 * // register the interceptor as a service
11687 * $provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) {
11689 * // optional method
11690 * 'request': function(config) {
11691 * // do something on success
11695 * // optional method
11696 * 'requestError': function(rejection) {
11697 * // do something on error
11698 * if (canRecover(rejection)) {
11699 * return responseOrNewPromise
11701 * return $q.reject(rejection);
11706 * // optional method
11707 * 'response': function(response) {
11708 * // do something on success
11712 * // optional method
11713 * 'responseError': function(rejection) {
11714 * // do something on error
11715 * if (canRecover(rejection)) {
11716 * return responseOrNewPromise
11718 * return $q.reject(rejection);
11723 * $httpProvider.interceptors.push('myHttpInterceptor');
11726 * // alternatively, register the interceptor via an anonymous factory
11727 * $httpProvider.interceptors.push(function($q, dependency1, dependency2) {
11729 * 'request': function(config) {
11733 * 'response': function(response) {
11740 * ## Security Considerations
11742 * When designing web applications, consider security threats from:
11744 * - [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx)
11745 * - [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery)
11747 * Both server and the client must cooperate in order to eliminate these threats. Angular comes
11748 * pre-configured with strategies that address these issues, but for this to work backend server
11749 * cooperation is required.
11751 * ### JSON Vulnerability Protection
11753 * A [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx)
11754 * allows third party website to turn your JSON resource URL into
11755 * [JSONP](http://en.wikipedia.org/wiki/JSONP) request under some conditions. To
11756 * counter this your server can prefix all JSON requests with following string `")]}',\n"`.
11757 * Angular will automatically strip the prefix before processing it as JSON.
11759 * For example if your server needs to return:
11764 * which is vulnerable to attack, your server can return:
11770 * Angular will strip the prefix, before processing the JSON.
11773 * ### Cross Site Request Forgery (XSRF) Protection
11775 * [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery) is an attack technique by
11776 * which the attacker can trick an authenticated user into unknowingly executing actions on your
11777 * website. Angular provides a mechanism to counter XSRF. When performing XHR requests, the
11778 * $http service reads a token from a cookie (by default, `XSRF-TOKEN`) and sets it as an HTTP
11779 * header (`X-XSRF-TOKEN`). Since only JavaScript that runs on your domain could read the
11780 * cookie, your server can be assured that the XHR came from JavaScript running on your domain.
11781 * The header will not be set for cross-domain requests.
11783 * To take advantage of this, your server needs to set a token in a JavaScript readable session
11784 * cookie called `XSRF-TOKEN` on the first HTTP GET request. On subsequent XHR requests the
11785 * server can verify that the cookie matches `X-XSRF-TOKEN` HTTP header, and therefore be sure
11786 * that only JavaScript running on your domain could have sent the request. The token must be
11787 * unique for each user and must be verifiable by the server (to prevent the JavaScript from
11788 * making up its own tokens). We recommend that the token is a digest of your site's
11789 * authentication cookie with a [salt](https://en.wikipedia.org/wiki/Salt_(cryptography))
11790 * for added security.
11792 * The name of the headers can be specified using the xsrfHeaderName and xsrfCookieName
11793 * properties of either $httpProvider.defaults at config-time, $http.defaults at run-time,
11794 * or the per-request config object.
11796 * In order to prevent collisions in environments where multiple Angular apps share the
11797 * same domain or subdomain, we recommend that each application uses unique cookie name.
11799 * @param {object} config Object describing the request to be made and how it should be
11800 * processed. The object has following properties:
11802 * - **method** – `{string}` – HTTP method (e.g. 'GET', 'POST', etc)
11803 * - **url** – `{string|TrustedObject}` – Absolute or relative URL of the resource that is being requested;
11804 * or an object created by a call to `$sce.trustAsResourceUrl(url)`.
11805 * - **params** – `{Object.<string|Object>}` – Map of strings or objects which will be serialized
11806 * with the `paramSerializer` and appended as GET parameters.
11807 * - **data** – `{string|Object}` – Data to be sent as the request message data.
11808 * - **headers** – `{Object}` – Map of strings or functions which return strings representing
11809 * HTTP headers to send to the server. If the return value of a function is null, the
11810 * header will not be sent. Functions accept a config object as an argument.
11811 * - **eventHandlers** - `{Object}` - Event listeners to be bound to the XMLHttpRequest object.
11812 * To bind events to the XMLHttpRequest upload object, use `uploadEventHandlers`.
11813 * The handler will be called in the context of a `$apply` block.
11814 * - **uploadEventHandlers** - `{Object}` - Event listeners to be bound to the XMLHttpRequest upload
11815 * object. To bind events to the XMLHttpRequest object, use `eventHandlers`.
11816 * The handler will be called in the context of a `$apply` block.
11817 * - **xsrfHeaderName** – `{string}` – Name of HTTP header to populate with the XSRF token.
11818 * - **xsrfCookieName** – `{string}` – Name of cookie containing the XSRF token.
11819 * - **transformRequest** –
11820 * `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` –
11821 * transform function or an array of such functions. The transform function takes the http
11822 * request body and headers and returns its transformed (typically serialized) version.
11823 * See {@link ng.$http#overriding-the-default-transformations-per-request
11824 * Overriding the Default Transformations}
11825 * - **transformResponse** –
11826 * `{function(data, headersGetter, status)|Array.<function(data, headersGetter, status)>}` –
11827 * transform function or an array of such functions. The transform function takes the http
11828 * response body, headers and status and returns its transformed (typically deserialized) version.
11829 * See {@link ng.$http#overriding-the-default-transformations-per-request
11830 * Overriding the Default Transformations}
11831 * - **paramSerializer** - `{string|function(Object<string,string>):string}` - A function used to
11832 * prepare the string representation of request parameters (specified as an object).
11833 * If specified as string, it is interpreted as function registered with the
11834 * {@link $injector $injector}, which means you can create your own serializer
11835 * by registering it as a {@link auto.$provide#service service}.
11836 * The default serializer is the {@link $httpParamSerializer $httpParamSerializer};
11837 * alternatively, you can use the {@link $httpParamSerializerJQLike $httpParamSerializerJQLike}
11838 * - **cache** – `{boolean|Object}` – A boolean value or object created with
11839 * {@link ng.$cacheFactory `$cacheFactory`} to enable or disable caching of the HTTP response.
11840 * See {@link $http#caching $http Caching} for more information.
11841 * - **timeout** – `{number|Promise}` – timeout in milliseconds, or {@link ng.$q promise}
11842 * that should abort the request when resolved.
11843 * - **withCredentials** - `{boolean}` - whether to set the `withCredentials` flag on the
11844 * XHR object. See [requests with credentials](https://developer.mozilla.org/docs/Web/HTTP/Access_control_CORS#Requests_with_credentials)
11845 * for more information.
11846 * - **responseType** - `{string}` - see
11847 * [XMLHttpRequest.responseType](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest#xmlhttprequest-responsetype).
11849 * @returns {HttpPromise} Returns a {@link ng.$q `Promise}` that will be resolved to a response object
11850 * when the request succeeds or fails.
11853 * @property {Array.<Object>} pendingRequests Array of config objects for currently pending
11854 * requests. This is primarily meant to be used for debugging purposes.
11858 <example module="httpExample" name="http-service">
11859 <file name="index.html">
11860 <div ng-controller="FetchController">
11861 <select ng-model="method" aria-label="Request method">
11862 <option>GET</option>
11863 <option>JSONP</option>
11865 <input type="text" ng-model="url" size="80" aria-label="URL" />
11866 <button id="fetchbtn" ng-click="fetch()">fetch</button><br>
11867 <button id="samplegetbtn" ng-click="updateModel('GET', 'http-hello.html')">Sample GET</button>
11868 <button id="samplejsonpbtn"
11869 ng-click="updateModel('JSONP',
11870 'https://angularjs.org/greet.php?name=Super%20Hero')">
11873 <button id="invalidjsonpbtn"
11874 ng-click="updateModel('JSONP', 'https://angularjs.org/doesntexist')">
11877 <pre>http status code: {{status}}</pre>
11878 <pre>http response data: {{data}}</pre>
11881 <file name="script.js">
11882 angular.module('httpExample', [])
11883 .config(['$sceDelegateProvider', function($sceDelegateProvider) {
11884 // We must whitelist the JSONP endpoint that we are using to show that we trust it
11885 $sceDelegateProvider.resourceUrlWhitelist([
11887 'https://angularjs.org/**'
11890 .controller('FetchController', ['$scope', '$http', '$templateCache',
11891 function($scope, $http, $templateCache) {
11892 $scope.method = 'GET';
11893 $scope.url = 'http-hello.html';
11895 $scope.fetch = function() {
11896 $scope.code = null;
11897 $scope.response = null;
11899 $http({method: $scope.method, url: $scope.url, cache: $templateCache}).
11900 then(function(response) {
11901 $scope.status = response.status;
11902 $scope.data = response.data;
11903 }, function(response) {
11904 $scope.data = response.data || 'Request failed';
11905 $scope.status = response.status;
11909 $scope.updateModel = function(method, url) {
11910 $scope.method = method;
11915 <file name="http-hello.html">
11918 <file name="protractor.js" type="protractor">
11919 var status = element(by.binding('status'));
11920 var data = element(by.binding('data'));
11921 var fetchBtn = element(by.id('fetchbtn'));
11922 var sampleGetBtn = element(by.id('samplegetbtn'));
11923 var invalidJsonpBtn = element(by.id('invalidjsonpbtn'));
11925 it('should make an xhr GET request', function() {
11926 sampleGetBtn.click();
11928 expect(status.getText()).toMatch('200');
11929 expect(data.getText()).toMatch(/Hello, \$http!/);
11932 // Commented out due to flakes. See https://github.com/angular/angular.js/issues/9185
11933 // it('should make a JSONP request to angularjs.org', function() {
11934 // var sampleJsonpBtn = element(by.id('samplejsonpbtn'));
11935 // sampleJsonpBtn.click();
11936 // fetchBtn.click();
11937 // expect(status.getText()).toMatch('200');
11938 // expect(data.getText()).toMatch(/Super Hero!/);
11941 it('should make JSONP request to invalid URL and invoke the error handler',
11943 invalidJsonpBtn.click();
11945 expect(status.getText()).toMatch('0');
11946 expect(data.getText()).toMatch('Request failed');
11951 function $http(requestConfig) {
11953 if (!isObject(requestConfig)) {
11954 throw minErr('$http')('badreq', 'Http request configuration must be an object. Received: {0}', requestConfig);
11957 if (!isString($sce.valueOf(requestConfig.url))) {
11958 throw minErr('$http')('badreq', 'Http request configuration url must be a string or a $sce trusted object. Received: {0}', requestConfig.url);
11961 var config = extend({
11963 transformRequest: defaults.transformRequest,
11964 transformResponse: defaults.transformResponse,
11965 paramSerializer: defaults.paramSerializer,
11966 jsonpCallbackParam: defaults.jsonpCallbackParam
11969 config.headers = mergeHeaders(requestConfig);
11970 config.method = uppercase(config.method);
11971 config.paramSerializer = isString(config.paramSerializer) ?
11972 $injector.get(config.paramSerializer) : config.paramSerializer;
11974 $browser.$$incOutstandingRequestCount();
11976 var requestInterceptors = [];
11977 var responseInterceptors = [];
11978 var promise = $q.resolve(config);
11980 // apply interceptors
11981 forEach(reversedInterceptors, function(interceptor) {
11982 if (interceptor.request || interceptor.requestError) {
11983 requestInterceptors.unshift(interceptor.request, interceptor.requestError);
11985 if (interceptor.response || interceptor.responseError) {
11986 responseInterceptors.push(interceptor.response, interceptor.responseError);
11990 promise = chainInterceptors(promise, requestInterceptors);
11991 promise = promise.then(serverRequest);
11992 promise = chainInterceptors(promise, responseInterceptors);
11993 promise = promise.finally(completeOutstandingRequest);
11998 function chainInterceptors(promise, interceptors) {
11999 for (var i = 0, ii = interceptors.length; i < ii;) {
12000 var thenFn = interceptors[i++];
12001 var rejectFn = interceptors[i++];
12003 promise = promise.then(thenFn, rejectFn);
12006 interceptors.length = 0;
12011 function completeOutstandingRequest() {
12012 $browser.$$completeOutstandingRequest(noop);
12015 function executeHeaderFns(headers, config) {
12016 var headerContent, processedHeaders = {};
12018 forEach(headers, function(headerFn, header) {
12019 if (isFunction(headerFn)) {
12020 headerContent = headerFn(config);
12021 if (headerContent != null) {
12022 processedHeaders[header] = headerContent;
12025 processedHeaders[header] = headerFn;
12029 return processedHeaders;
12032 function mergeHeaders(config) {
12033 var defHeaders = defaults.headers,
12034 reqHeaders = extend({}, config.headers),
12035 defHeaderName, lowercaseDefHeaderName, reqHeaderName;
12037 defHeaders = extend({}, defHeaders.common, defHeaders[lowercase(config.method)]);
12039 // using for-in instead of forEach to avoid unnecessary iteration after header has been found
12040 defaultHeadersIteration:
12041 for (defHeaderName in defHeaders) {
12042 lowercaseDefHeaderName = lowercase(defHeaderName);
12044 for (reqHeaderName in reqHeaders) {
12045 if (lowercase(reqHeaderName) === lowercaseDefHeaderName) {
12046 continue defaultHeadersIteration;
12050 reqHeaders[defHeaderName] = defHeaders[defHeaderName];
12053 // execute if header value is a function for merged headers
12054 return executeHeaderFns(reqHeaders, shallowCopy(config));
12057 function serverRequest(config) {
12058 var headers = config.headers;
12059 var reqData = transformData(config.data, headersGetter(headers), undefined, config.transformRequest);
12061 // strip content-type if data is undefined
12062 if (isUndefined(reqData)) {
12063 forEach(headers, function(value, header) {
12064 if (lowercase(header) === 'content-type') {
12065 delete headers[header];
12070 if (isUndefined(config.withCredentials) && !isUndefined(defaults.withCredentials)) {
12071 config.withCredentials = defaults.withCredentials;
12075 return sendReq(config, reqData).then(transformResponse, transformResponse);
12078 function transformResponse(response) {
12079 // make a copy since the response must be cacheable
12080 var resp = extend({}, response);
12081 resp.data = transformData(response.data, response.headers, response.status,
12082 config.transformResponse);
12083 return (isSuccess(response.status))
12089 $http.pendingRequests = [];
12096 * Shortcut method to perform `GET` request.
12098 * @param {string|TrustedObject} url Absolute or relative URL of the resource that is being requested;
12099 * or an object created by a call to `$sce.trustAsResourceUrl(url)`.
12100 * @param {Object=} config Optional configuration object
12101 * @returns {HttpPromise} Future object
12106 * @name $http#delete
12109 * Shortcut method to perform `DELETE` request.
12111 * @param {string|TrustedObject} url Absolute or relative URL of the resource that is being requested;
12112 * or an object created by a call to `$sce.trustAsResourceUrl(url)`.
12113 * @param {Object=} config Optional configuration object
12114 * @returns {HttpPromise} Future object
12122 * Shortcut method to perform `HEAD` request.
12124 * @param {string|TrustedObject} url Absolute or relative URL of the resource that is being requested;
12125 * or an object created by a call to `$sce.trustAsResourceUrl(url)`.
12126 * @param {Object=} config Optional configuration object
12127 * @returns {HttpPromise} Future object
12132 * @name $http#jsonp
12135 * Shortcut method to perform `JSONP` request.
12137 * Note that, since JSONP requests are sensitive because the response is given full access to the browser,
12138 * the url must be declared, via {@link $sce} as a trusted resource URL.
12139 * You can trust a URL by adding it to the whitelist via
12140 * {@link $sceDelegateProvider#resourceUrlWhitelist `$sceDelegateProvider.resourceUrlWhitelist`} or
12141 * by explicitly trusting the URL via {@link $sce#trustAsResourceUrl `$sce.trustAsResourceUrl(url)`}.
12143 * JSONP requests must specify a callback to be used in the response from the server. This callback
12144 * is passed as a query parameter in the request. You must specify the name of this parameter by
12145 * setting the `jsonpCallbackParam` property on the request config object.
12148 * $http.jsonp('some/trusted/url', {jsonpCallbackParam: 'callback'})
12151 * You can also specify a default callback parameter name in `$http.defaults.jsonpCallbackParam`.
12152 * Initially this is set to `'callback'`.
12154 * <div class="alert alert-danger">
12155 * You can no longer use the `JSON_CALLBACK` string as a placeholder for specifying where the callback
12156 * parameter value should go.
12159 * If you would like to customise where and how the callbacks are stored then try overriding
12160 * or decorating the {@link $jsonpCallbacks} service.
12162 * @param {string|TrustedObject} url Absolute or relative URL of the resource that is being requested;
12163 * or an object created by a call to `$sce.trustAsResourceUrl(url)`.
12164 * @param {Object=} config Optional configuration object
12165 * @returns {HttpPromise} Future object
12167 createShortMethods('get', 'delete', 'head', 'jsonp');
12174 * Shortcut method to perform `POST` request.
12176 * @param {string} url Relative or absolute URL specifying the destination of the request
12177 * @param {*} data Request content
12178 * @param {Object=} config Optional configuration object
12179 * @returns {HttpPromise} Future object
12187 * Shortcut method to perform `PUT` request.
12189 * @param {string} url Relative or absolute URL specifying the destination of the request
12190 * @param {*} data Request content
12191 * @param {Object=} config Optional configuration object
12192 * @returns {HttpPromise} Future object
12197 * @name $http#patch
12200 * Shortcut method to perform `PATCH` request.
12202 * @param {string} url Relative or absolute URL specifying the destination of the request
12203 * @param {*} data Request content
12204 * @param {Object=} config Optional configuration object
12205 * @returns {HttpPromise} Future object
12207 createShortMethodsWithData('post', 'put', 'patch');
12211 * @name $http#defaults
12214 * Runtime equivalent of the `$httpProvider.defaults` property. Allows configuration of
12215 * default headers, withCredentials as well as request and response transformations.
12217 * See "Setting HTTP Headers" and "Transforming Requests and Responses" sections above.
12219 $http.defaults = defaults;
12225 function createShortMethods(names) {
12226 forEach(arguments, function(name) {
12227 $http[name] = function(url, config) {
12228 return $http(extend({}, config || {}, {
12237 function createShortMethodsWithData(name) {
12238 forEach(arguments, function(name) {
12239 $http[name] = function(url, data, config) {
12240 return $http(extend({}, config || {}, {
12251 * Makes the request.
12253 * !!! ACCESSES CLOSURE VARS:
12254 * $httpBackend, defaults, $log, $rootScope, defaultCache, $http.pendingRequests
12256 function sendReq(config, reqData) {
12257 var deferred = $q.defer(),
12258 promise = deferred.promise,
12261 reqHeaders = config.headers,
12262 isJsonp = lowercase(config.method) === 'jsonp',
12266 // JSONP is a pretty sensitive operation where we're allowing a script to have full access to
12267 // our DOM and JS space. So we require that the URL satisfies SCE.RESOURCE_URL.
12268 url = $sce.getTrustedResourceUrl(url);
12269 } else if (!isString(url)) {
12270 // If it is not a string then the URL must be a $sce trusted object
12271 url = $sce.valueOf(url);
12274 url = buildUrl(url, config.paramSerializer(config.params));
12277 // Check the url and add the JSONP callback placeholder
12278 url = sanitizeJsonpCallbackParam(url, config.jsonpCallbackParam);
12281 $http.pendingRequests.push(config);
12282 promise.then(removePendingReq, removePendingReq);
12284 if ((config.cache || defaults.cache) && config.cache !== false &&
12285 (config.method === 'GET' || config.method === 'JSONP')) {
12286 cache = isObject(config.cache) ? config.cache
12287 : isObject(/** @type {?} */ (defaults).cache)
12288 ? /** @type {?} */ (defaults).cache
12293 cachedResp = cache.get(url);
12294 if (isDefined(cachedResp)) {
12295 if (isPromiseLike(cachedResp)) {
12296 // cached request has already been sent, but there is no response yet
12297 cachedResp.then(resolvePromiseWithResult, resolvePromiseWithResult);
12299 // serving from cache
12300 if (isArray(cachedResp)) {
12301 resolvePromise(cachedResp[1], cachedResp[0], shallowCopy(cachedResp[2]), cachedResp[3]);
12303 resolvePromise(cachedResp, 200, {}, 'OK');
12307 // put the promise for the non-transformed response into cache as a placeholder
12308 cache.put(url, promise);
12313 // if we won't have the response in cache, set the xsrf headers and
12314 // send the request to the backend
12315 if (isUndefined(cachedResp)) {
12316 var xsrfValue = urlIsSameOrigin(config.url)
12317 ? $$cookieReader()[config.xsrfCookieName || defaults.xsrfCookieName]
12320 reqHeaders[(config.xsrfHeaderName || defaults.xsrfHeaderName)] = xsrfValue;
12323 $httpBackend(config.method, url, reqData, done, reqHeaders, config.timeout,
12324 config.withCredentials, config.responseType,
12325 createApplyHandlers(config.eventHandlers),
12326 createApplyHandlers(config.uploadEventHandlers));
12331 function createApplyHandlers(eventHandlers) {
12332 if (eventHandlers) {
12333 var applyHandlers = {};
12334 forEach(eventHandlers, function(eventHandler, key) {
12335 applyHandlers[key] = function(event) {
12336 if (useApplyAsync) {
12337 $rootScope.$applyAsync(callEventHandler);
12338 } else if ($rootScope.$$phase) {
12339 callEventHandler();
12341 $rootScope.$apply(callEventHandler);
12344 function callEventHandler() {
12345 eventHandler(event);
12349 return applyHandlers;
12355 * Callback registered to $httpBackend():
12356 * - caches the response if desired
12357 * - resolves the raw $http promise
12360 function done(status, response, headersString, statusText) {
12362 if (isSuccess(status)) {
12363 cache.put(url, [status, response, parseHeaders(headersString), statusText]);
12365 // remove promise from the cache
12370 function resolveHttpPromise() {
12371 resolvePromise(response, status, headersString, statusText);
12374 if (useApplyAsync) {
12375 $rootScope.$applyAsync(resolveHttpPromise);
12377 resolveHttpPromise();
12378 if (!$rootScope.$$phase) $rootScope.$apply();
12384 * Resolves the raw $http promise.
12386 function resolvePromise(response, status, headers, statusText) {
12387 //status: HTTP response status code, 0, -1 (aborted by timeout / promise)
12388 status = status >= -1 ? status : 0;
12390 (isSuccess(status) ? deferred.resolve : deferred.reject)({
12393 headers: headersGetter(headers),
12395 statusText: statusText
12399 function resolvePromiseWithResult(result) {
12400 resolvePromise(result.data, result.status, shallowCopy(result.headers()), result.statusText);
12403 function removePendingReq() {
12404 var idx = $http.pendingRequests.indexOf(config);
12405 if (idx !== -1) $http.pendingRequests.splice(idx, 1);
12410 function buildUrl(url, serializedParams) {
12411 if (serializedParams.length > 0) {
12412 url += ((url.indexOf('?') === -1) ? '?' : '&') + serializedParams;
12417 function sanitizeJsonpCallbackParam(url, key) {
12418 if (/[&?][^=]+=JSON_CALLBACK/.test(url)) {
12419 // Throw if the url already contains a reference to JSON_CALLBACK
12420 throw $httpMinErr('badjsonp', 'Illegal use of JSON_CALLBACK in url, "{0}"', url);
12423 var callbackParamRegex = new RegExp('[&?]' + key + '=');
12424 if (callbackParamRegex.test(url)) {
12425 // Throw if the callback param was already provided
12426 throw $httpMinErr('badjsonp', 'Illegal use of callback param, "{0}", in url, "{1}"', key, url);
12429 // Add in the JSON_CALLBACK callback param value
12430 url += ((url.indexOf('?') === -1) ? '?' : '&') + key + '=JSON_CALLBACK';
12439 * @name $xhrFactory
12443 * Factory function used to create XMLHttpRequest objects.
12445 * Replace or decorate this service to create your own custom XMLHttpRequest objects.
12448 * angular.module('myApp', [])
12449 * .factory('$xhrFactory', function() {
12450 * return function createXhr(method, url) {
12451 * return new window.XMLHttpRequest({mozSystem: true});
12456 * @param {string} method HTTP method of the request (GET, POST, PUT, ..)
12457 * @param {string} url URL of the request.
12459 function $xhrFactoryProvider() {
12460 this.$get = function() {
12461 return function createXhr() {
12462 return new window.XMLHttpRequest();
12469 * @name $httpBackend
12470 * @requires $jsonpCallbacks
12471 * @requires $document
12472 * @requires $xhrFactory
12476 * HTTP backend used by the {@link ng.$http service} that delegates to
12477 * XMLHttpRequest object or JSONP and deals with browser incompatibilities.
12479 * You should never need to use this service directly, instead use the higher-level abstractions:
12480 * {@link ng.$http $http} or {@link ngResource.$resource $resource}.
12482 * During testing this implementation is swapped with {@link ngMock.$httpBackend mock
12483 * $httpBackend} which can be trained with responses.
12485 function $HttpBackendProvider() {
12486 this.$get = ['$browser', '$jsonpCallbacks', '$document', '$xhrFactory', function($browser, $jsonpCallbacks, $document, $xhrFactory) {
12487 return createHttpBackend($browser, $xhrFactory, $browser.defer, $jsonpCallbacks, $document[0]);
12491 function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDocument) {
12492 // TODO(vojta): fix the signature
12493 return function(method, url, post, callback, headers, timeout, withCredentials, responseType, eventHandlers, uploadEventHandlers) {
12494 url = url || $browser.url();
12496 if (lowercase(method) === 'jsonp') {
12497 var callbackPath = callbacks.createCallback(url);
12498 var jsonpDone = jsonpReq(url, callbackPath, function(status, text) {
12499 // jsonpReq only ever sets status to 200 (OK), 404 (ERROR) or -1 (WAITING)
12500 var response = (status === 200) && callbacks.getResponse(callbackPath);
12501 completeRequest(callback, status, response, '', text);
12502 callbacks.removeCallback(callbackPath);
12506 var xhr = createXhr(method, url);
12508 xhr.open(method, url, true);
12509 forEach(headers, function(value, key) {
12510 if (isDefined(value)) {
12511 xhr.setRequestHeader(key, value);
12515 xhr.onload = function requestLoaded() {
12516 var statusText = xhr.statusText || '';
12518 // responseText is the old-school way of retrieving response (supported by IE9)
12519 // response/responseType properties were introduced in XHR Level2 spec (supported by IE10)
12520 var response = ('response' in xhr) ? xhr.response : xhr.responseText;
12522 // normalize IE9 bug (http://bugs.jquery.com/ticket/1450)
12523 var status = xhr.status === 1223 ? 204 : xhr.status;
12525 // fix status code when it is 0 (0 status is undocumented).
12526 // Occurs when accessing file resources or on Android 4.1 stock browser
12527 // while retrieving files from application cache.
12528 if (status === 0) {
12529 status = response ? 200 : urlResolve(url).protocol === 'file' ? 404 : 0;
12532 completeRequest(callback,
12535 xhr.getAllResponseHeaders(),
12539 var requestError = function() {
12540 // The response is always empty
12541 // See https://xhr.spec.whatwg.org/#request-error-steps and https://fetch.spec.whatwg.org/#concept-network-error
12542 completeRequest(callback, -1, null, null, '');
12545 xhr.onerror = requestError;
12546 xhr.onabort = requestError;
12547 xhr.ontimeout = requestError;
12549 forEach(eventHandlers, function(value, key) {
12550 xhr.addEventListener(key, value);
12553 forEach(uploadEventHandlers, function(value, key) {
12554 xhr.upload.addEventListener(key, value);
12557 if (withCredentials) {
12558 xhr.withCredentials = true;
12561 if (responseType) {
12563 xhr.responseType = responseType;
12565 // WebKit added support for the json responseType value on 09/03/2013
12566 // https://bugs.webkit.org/show_bug.cgi?id=73648. Versions of Safari prior to 7 are
12567 // known to throw when setting the value "json" as the response type. Other older
12568 // browsers implementing the responseType
12570 // The json response type can be ignored if not supported, because JSON payloads are
12571 // parsed on the client-side regardless.
12572 if (responseType !== 'json') {
12578 xhr.send(isUndefined(post) ? null : post);
12582 var timeoutId = $browserDefer(timeoutRequest, timeout);
12583 } else if (isPromiseLike(timeout)) {
12584 timeout.then(timeoutRequest);
12588 function timeoutRequest() {
12597 function completeRequest(callback, status, response, headersString, statusText) {
12598 // cancel timeout and subsequent timeout promise resolution
12599 if (isDefined(timeoutId)) {
12600 $browserDefer.cancel(timeoutId);
12602 jsonpDone = xhr = null;
12604 callback(status, response, headersString, statusText);
12608 function jsonpReq(url, callbackPath, done) {
12609 url = url.replace('JSON_CALLBACK', callbackPath);
12610 // we can't use jQuery/jqLite here because jQuery does crazy stuff with script elements, e.g.:
12611 // - fetches local scripts via XHR and evals them
12612 // - adds and immediately removes script elements from the document
12613 var script = rawDocument.createElement('script'), callback = null;
12614 script.type = 'text/javascript';
12616 script.async = true;
12618 callback = function(event) {
12619 script.removeEventListener('load', callback);
12620 script.removeEventListener('error', callback);
12621 rawDocument.body.removeChild(script);
12624 var text = 'unknown';
12627 if (event.type === 'load' && !callbacks.wasCalled(callbackPath)) {
12628 event = { type: 'error' };
12631 status = event.type === 'error' ? 404 : 200;
12635 done(status, text);
12639 script.addEventListener('load', callback);
12640 script.addEventListener('error', callback);
12641 rawDocument.body.appendChild(script);
12646 var $interpolateMinErr = angular.$interpolateMinErr = minErr('$interpolate');
12647 $interpolateMinErr.throwNoconcat = function(text) {
12648 throw $interpolateMinErr('noconcat',
12649 'Error while interpolating: {0}\nStrict Contextual Escaping disallows ' +
12650 'interpolations that concatenate multiple expressions when a trusted value is ' +
12651 'required. See http://docs.angularjs.org/api/ng.$sce', text);
12654 $interpolateMinErr.interr = function(text, err) {
12655 return $interpolateMinErr('interr', 'Can\'t interpolate: {0}\n{1}', text, err.toString());
12660 * @name $interpolateProvider
12665 * Used for configuring the interpolation markup. Defaults to `{{` and `}}`.
12667 * <div class="alert alert-danger">
12668 * This feature is sometimes used to mix different markup languages, e.g. to wrap an Angular
12669 * template within a Python Jinja template (or any other template language). Mixing templating
12670 * languages is **very dangerous**. The embedding template language will not safely escape Angular
12671 * expressions, so any user-controlled values in the template will cause Cross Site Scripting (XSS)
12676 <example name="custom-interpolation-markup" module="customInterpolationApp">
12677 <file name="index.html">
12679 var customInterpolationApp = angular.module('customInterpolationApp', []);
12681 customInterpolationApp.config(function($interpolateProvider) {
12682 $interpolateProvider.startSymbol('//');
12683 $interpolateProvider.endSymbol('//');
12687 customInterpolationApp.controller('DemoController', function() {
12688 this.label = "This binding is brought you by // interpolation symbols.";
12691 <div ng-controller="DemoController as demo">
12695 <file name="protractor.js" type="protractor">
12696 it('should interpolate binding with custom symbols', function() {
12697 expect(element(by.binding('demo.label')).getText()).toBe('This binding is brought you by // interpolation symbols.');
12702 function $InterpolateProvider() {
12703 var startSymbol = '{{';
12704 var endSymbol = '}}';
12708 * @name $interpolateProvider#startSymbol
12710 * Symbol to denote start of expression in the interpolated string. Defaults to `{{`.
12712 * @param {string=} value new value to set the starting symbol to.
12713 * @returns {string|self} Returns the symbol when used as getter and self if used as setter.
12715 this.startSymbol = function(value) {
12717 startSymbol = value;
12720 return startSymbol;
12726 * @name $interpolateProvider#endSymbol
12728 * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
12730 * @param {string=} value new value to set the ending symbol to.
12731 * @returns {string|self} Returns the symbol when used as getter and self if used as setter.
12733 this.endSymbol = function(value) {
12743 this.$get = ['$parse', '$exceptionHandler', '$sce', function($parse, $exceptionHandler, $sce) {
12744 var startSymbolLength = startSymbol.length,
12745 endSymbolLength = endSymbol.length,
12746 escapedStartRegexp = new RegExp(startSymbol.replace(/./g, escape), 'g'),
12747 escapedEndRegexp = new RegExp(endSymbol.replace(/./g, escape), 'g');
12749 function escape(ch) {
12750 return '\\\\\\' + ch;
12753 function unescapeText(text) {
12754 return text.replace(escapedStartRegexp, startSymbol).
12755 replace(escapedEndRegexp, endSymbol);
12758 // TODO: this is the same as the constantWatchDelegate in parse.js
12759 function constantWatchDelegate(scope, listener, objectEquality, constantInterp) {
12760 var unwatch = scope.$watch(function constantInterpolateWatch(scope) {
12762 return constantInterp(scope);
12763 }, listener, objectEquality);
12769 * @name $interpolate
12777 * Compiles a string with markup into an interpolation function. This service is used by the
12778 * HTML {@link ng.$compile $compile} service for data binding. See
12779 * {@link ng.$interpolateProvider $interpolateProvider} for configuring the
12780 * interpolation markup.
12784 * var $interpolate = ...; // injected
12785 * var exp = $interpolate('Hello {{name | uppercase}}!');
12786 * expect(exp({name:'Angular'})).toEqual('Hello ANGULAR!');
12789 * `$interpolate` takes an optional fourth argument, `allOrNothing`. If `allOrNothing` is
12790 * `true`, the interpolation function will return `undefined` unless all embedded expressions
12791 * evaluate to a value other than `undefined`.
12794 * var $interpolate = ...; // injected
12795 * var context = {greeting: 'Hello', name: undefined };
12797 * // default "forgiving" mode
12798 * var exp = $interpolate('{{greeting}} {{name}}!');
12799 * expect(exp(context)).toEqual('Hello !');
12801 * // "allOrNothing" mode
12802 * exp = $interpolate('{{greeting}} {{name}}!', false, null, true);
12803 * expect(exp(context)).toBeUndefined();
12804 * context.name = 'Angular';
12805 * expect(exp(context)).toEqual('Hello Angular!');
12808 * `allOrNothing` is useful for interpolating URLs. `ngSrc` and `ngSrcset` use this behavior.
12810 * #### Escaped Interpolation
12811 * $interpolate provides a mechanism for escaping interpolation markers. Start and end markers
12812 * can be escaped by preceding each of their characters with a REVERSE SOLIDUS U+005C (backslash).
12813 * It will be rendered as a regular start/end marker, and will not be interpreted as an expression
12816 * This enables web-servers to prevent script injection attacks and defacing attacks, to some
12817 * degree, while also enabling code examples to work without relying on the
12818 * {@link ng.directive:ngNonBindable ngNonBindable} directive.
12820 * **For security purposes, it is strongly encouraged that web servers escape user-supplied data,
12821 * replacing angle brackets (<, >) with &lt; and &gt; respectively, and replacing all
12822 * interpolation start/end markers with their escaped counterparts.**
12824 * Escaped interpolation markers are only replaced with the actual interpolation markers in rendered
12825 * output when the $interpolate service processes the text. So, for HTML elements interpolated
12826 * by {@link ng.$compile $compile}, or otherwise interpolated with the `mustHaveExpression` parameter
12827 * set to `true`, the interpolated text must contain an unescaped interpolation expression. As such,
12828 * this is typically useful only when user-data is used in rendering a template from the server, or
12829 * when otherwise untrusted data is used by a directive.
12831 * <example name="interpolation">
12832 * <file name="index.html">
12833 * <div ng-init="username='A user'">
12834 * <p ng-init="apptitle='Escaping demo'">{{apptitle}}: \{\{ username = "defaced value"; \}\}
12836 * <p><strong>{{username}}</strong> attempts to inject code which will deface the
12837 * application, but fails to accomplish their task, because the server has correctly
12838 * escaped the interpolation start/end markers with REVERSE SOLIDUS U+005C (backslash)
12840 * <p>Instead, the result of the attempted script injection is visible, and can be removed
12841 * from the database by an administrator.</p>
12847 * It is currently not possible for an interpolated expression to contain the interpolation end
12848 * symbol. For example, `{{ '}}' }}` will be incorrectly interpreted as `{{ ' }}` + `' }}`, i.e.
12849 * an interpolated expression consisting of a single-quote (`'`) and the `' }}` string.
12852 * All directives and components must use the standard `{{` `}}` interpolation symbols
12853 * in their templates. If you change the application interpolation symbols the {@link $compile}
12854 * service will attempt to denormalize the standard symbols to the custom symbols.
12855 * The denormalization process is not clever enough to know not to replace instances of the standard
12856 * symbols where they would not normally be treated as interpolation symbols. For example in the following
12857 * code snippet the closing braces of the literal object will get incorrectly denormalized:
12860 * <div data-context='{"context":{"id":3,"type":"page"}}">
12863 * The workaround is to ensure that such instances are separated by whitespace:
12865 * <div data-context='{"context":{"id":3,"type":"page"} }">
12868 * See https://github.com/angular/angular.js/pull/14610#issuecomment-219401099 for more information.
12870 * @param {string} text The text with markup to interpolate.
12871 * @param {boolean=} mustHaveExpression if set to true then the interpolation string must have
12872 * embedded expression in order to return an interpolation function. Strings with no
12873 * embedded expression will return null for the interpolation function.
12874 * @param {string=} trustedContext when provided, the returned function passes the interpolated
12875 * result through {@link ng.$sce#getTrusted $sce.getTrusted(interpolatedResult,
12876 * trustedContext)} before returning it. Refer to the {@link ng.$sce $sce} service that
12877 * provides Strict Contextual Escaping for details.
12878 * @param {boolean=} allOrNothing if `true`, then the returned function returns undefined
12879 * unless all embedded expressions evaluate to a value other than `undefined`.
12880 * @returns {function(context)} an interpolation function which is used to compute the
12881 * interpolated string. The function has these parameters:
12883 * - `context`: evaluation context for all expressions embedded in the interpolated text
12885 function $interpolate(text, mustHaveExpression, trustedContext, allOrNothing) {
12886 // Provide a quick exit and simplified result function for text with no interpolation
12887 if (!text.length || text.indexOf(startSymbol) === -1) {
12888 var constantInterp;
12889 if (!mustHaveExpression) {
12890 var unescapedText = unescapeText(text);
12891 constantInterp = valueFn(unescapedText);
12892 constantInterp.exp = text;
12893 constantInterp.expressions = [];
12894 constantInterp.$$watchDelegate = constantWatchDelegate;
12896 return constantInterp;
12899 allOrNothing = !!allOrNothing;
12905 textLength = text.length,
12908 expressionPositions = [];
12910 while (index < textLength) {
12911 if (((startIndex = text.indexOf(startSymbol, index)) !== -1) &&
12912 ((endIndex = text.indexOf(endSymbol, startIndex + startSymbolLength)) !== -1)) {
12913 if (index !== startIndex) {
12914 concat.push(unescapeText(text.substring(index, startIndex)));
12916 exp = text.substring(startIndex + startSymbolLength, endIndex);
12917 expressions.push(exp);
12918 parseFns.push($parse(exp, parseStringifyInterceptor));
12919 index = endIndex + endSymbolLength;
12920 expressionPositions.push(concat.length);
12923 // we did not find an interpolation, so we have to add the remainder to the separators array
12924 if (index !== textLength) {
12925 concat.push(unescapeText(text.substring(index)));
12931 // Concatenating expressions makes it hard to reason about whether some combination of
12932 // concatenated values are unsafe to use and could easily lead to XSS. By requiring that a
12933 // single expression be used for iframe[src], object[src], etc., we ensure that the value
12934 // that's used is assigned or constructed by some JS code somewhere that is more testable or
12935 // make it obvious that you bound the value to some user controlled value. This helps reduce
12936 // the load when auditing for XSS issues.
12937 if (trustedContext && concat.length > 1) {
12938 $interpolateMinErr.throwNoconcat(text);
12941 if (!mustHaveExpression || expressions.length) {
12942 var compute = function(values) {
12943 for (var i = 0, ii = expressions.length; i < ii; i++) {
12944 if (allOrNothing && isUndefined(values[i])) return;
12945 concat[expressionPositions[i]] = values[i];
12947 return concat.join('');
12950 var getValue = function(value) {
12951 return trustedContext ?
12952 $sce.getTrusted(trustedContext, value) :
12953 $sce.valueOf(value);
12956 return extend(function interpolationFn(context) {
12958 var ii = expressions.length;
12959 var values = new Array(ii);
12962 for (; i < ii; i++) {
12963 values[i] = parseFns[i](context);
12966 return compute(values);
12968 $exceptionHandler($interpolateMinErr.interr(text, err));
12972 // all of these properties are undocumented for now
12973 exp: text, //just for compatibility with regular watchers created via $watch
12974 expressions: expressions,
12975 $$watchDelegate: function(scope, listener) {
12977 return scope.$watchGroup(parseFns, /** @this */ function interpolateFnWatcher(values, oldValues) {
12978 var currValue = compute(values);
12979 if (isFunction(listener)) {
12980 listener.call(this, currValue, values !== oldValues ? lastValue : currValue, scope);
12982 lastValue = currValue;
12988 function parseStringifyInterceptor(value) {
12990 value = getValue(value);
12991 return allOrNothing && !isDefined(value) ? value : stringify(value);
12993 $exceptionHandler($interpolateMinErr.interr(text, err));
13001 * @name $interpolate#startSymbol
13003 * Symbol to denote the start of expression in the interpolated string. Defaults to `{{`.
13005 * Use {@link ng.$interpolateProvider#startSymbol `$interpolateProvider.startSymbol`} to change
13008 * @returns {string} start symbol.
13010 $interpolate.startSymbol = function() {
13011 return startSymbol;
13017 * @name $interpolate#endSymbol
13019 * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
13021 * Use {@link ng.$interpolateProvider#endSymbol `$interpolateProvider.endSymbol`} to change
13024 * @returns {string} end symbol.
13026 $interpolate.endSymbol = function() {
13030 return $interpolate;
13035 function $IntervalProvider() {
13036 this.$get = ['$rootScope', '$window', '$q', '$$q', '$browser',
13037 function($rootScope, $window, $q, $$q, $browser) {
13038 var intervals = {};
13046 * Angular's wrapper for `window.setInterval`. The `fn` function is executed every `delay`
13049 * The return value of registering an interval function is a promise. This promise will be
13050 * notified upon each tick of the interval, and will be resolved after `count` iterations, or
13051 * run indefinitely if `count` is not defined. The value of the notification will be the
13052 * number of iterations that have run.
13053 * To cancel an interval, call `$interval.cancel(promise)`.
13055 * In tests you can use {@link ngMock.$interval#flush `$interval.flush(millis)`} to
13056 * move forward by `millis` milliseconds and trigger any functions scheduled to run in that
13059 * <div class="alert alert-warning">
13060 * **Note**: Intervals created by this service must be explicitly destroyed when you are finished
13061 * with them. In particular they are not automatically destroyed when a controller's scope or a
13062 * directive's element are destroyed.
13063 * You should take this into consideration and make sure to always cancel the interval at the
13064 * appropriate moment. See the example below for more details on how and when to do this.
13067 * @param {function()} fn A function that should be called repeatedly. If no additional arguments
13068 * are passed (see below), the function is called with the current iteration count.
13069 * @param {number} delay Number of milliseconds between each function call.
13070 * @param {number=} [count=0] Number of times to repeat. If not set, or 0, will repeat
13072 * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
13073 * will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
13074 * @param {...*=} Pass additional parameters to the executed function.
13075 * @returns {promise} A promise which will be notified on each iteration.
13078 * <example module="intervalExample" name="interval-service">
13079 * <file name="index.html">
13081 * angular.module('intervalExample', [])
13082 * .controller('ExampleController', ['$scope', '$interval',
13083 * function($scope, $interval) {
13084 * $scope.format = 'M/d/yy h:mm:ss a';
13085 * $scope.blood_1 = 100;
13086 * $scope.blood_2 = 120;
13089 * $scope.fight = function() {
13090 * // Don't start a new fight if we are already fighting
13091 * if ( angular.isDefined(stop) ) return;
13093 * stop = $interval(function() {
13094 * if ($scope.blood_1 > 0 && $scope.blood_2 > 0) {
13095 * $scope.blood_1 = $scope.blood_1 - 3;
13096 * $scope.blood_2 = $scope.blood_2 - 4;
13098 * $scope.stopFight();
13103 * $scope.stopFight = function() {
13104 * if (angular.isDefined(stop)) {
13105 * $interval.cancel(stop);
13106 * stop = undefined;
13110 * $scope.resetFight = function() {
13111 * $scope.blood_1 = 100;
13112 * $scope.blood_2 = 120;
13115 * $scope.$on('$destroy', function() {
13116 * // Make sure that the interval is destroyed too
13117 * $scope.stopFight();
13120 * // Register the 'myCurrentTime' directive factory method.
13121 * // We inject $interval and dateFilter service since the factory method is DI.
13122 * .directive('myCurrentTime', ['$interval', 'dateFilter',
13123 * function($interval, dateFilter) {
13124 * // return the directive link function. (compile function not needed)
13125 * return function(scope, element, attrs) {
13126 * var format, // date format
13127 * stopTime; // so that we can cancel the time updates
13129 * // used to update the UI
13130 * function updateTime() {
13131 * element.text(dateFilter(new Date(), format));
13134 * // watch the expression, and update the UI on change.
13135 * scope.$watch(attrs.myCurrentTime, function(value) {
13140 * stopTime = $interval(updateTime, 1000);
13142 * // listen on DOM destroy (removal) event, and cancel the next UI update
13143 * // to prevent updating time after the DOM element was removed.
13144 * element.on('$destroy', function() {
13145 * $interval.cancel(stopTime);
13152 * <div ng-controller="ExampleController">
13153 * <label>Date format: <input ng-model="format"></label> <hr/>
13154 * Current time is: <span my-current-time="format"></span>
13156 * Blood 1 : <font color='red'>{{blood_1}}</font>
13157 * Blood 2 : <font color='red'>{{blood_2}}</font>
13158 * <button type="button" data-ng-click="fight()">Fight</button>
13159 * <button type="button" data-ng-click="stopFight()">StopFight</button>
13160 * <button type="button" data-ng-click="resetFight()">resetFight</button>
13167 function interval(fn, delay, count, invokeApply) {
13168 var hasParams = arguments.length > 4,
13169 args = hasParams ? sliceArgs(arguments, 4) : [],
13170 setInterval = $window.setInterval,
13171 clearInterval = $window.clearInterval,
13173 skipApply = (isDefined(invokeApply) && !invokeApply),
13174 deferred = (skipApply ? $$q : $q).defer(),
13175 promise = deferred.promise;
13177 count = isDefined(count) ? count : 0;
13179 promise.$$intervalId = setInterval(function tick() {
13181 $browser.defer(callback);
13183 $rootScope.$evalAsync(callback);
13185 deferred.notify(iteration++);
13187 if (count > 0 && iteration >= count) {
13188 deferred.resolve(iteration);
13189 clearInterval(promise.$$intervalId);
13190 delete intervals[promise.$$intervalId];
13193 if (!skipApply) $rootScope.$apply();
13197 intervals[promise.$$intervalId] = deferred;
13201 function callback() {
13205 fn.apply(null, args);
13213 * @name $interval#cancel
13216 * Cancels a task associated with the `promise`.
13218 * @param {Promise=} promise returned by the `$interval` function.
13219 * @returns {boolean} Returns `true` if the task was successfully canceled.
13221 interval.cancel = function(promise) {
13222 if (promise && promise.$$intervalId in intervals) {
13223 // Interval cancels should not report as unhandled promise.
13224 intervals[promise.$$intervalId].promise.catch(noop);
13225 intervals[promise.$$intervalId].reject('canceled');
13226 $window.clearInterval(promise.$$intervalId);
13227 delete intervals[promise.$$intervalId];
13239 * @name $jsonpCallbacks
13240 * @requires $window
13242 * This service handles the lifecycle of callbacks to handle JSONP requests.
13243 * Override this service if you wish to customise where the callbacks are stored and
13244 * how they vary compared to the requested url.
13246 var $jsonpCallbacksProvider = /** @this */ function() {
13247 this.$get = function() {
13248 var callbacks = angular.callbacks;
13249 var callbackMap = {};
13251 function createCallback(callbackId) {
13252 var callback = function(data) {
13253 callback.data = data;
13254 callback.called = true;
13256 callback.id = callbackId;
13263 * @name $jsonpCallbacks#createCallback
13264 * @param {string} url the url of the JSONP request
13265 * @returns {string} the callback path to send to the server as part of the JSONP request
13267 * {@link $httpBackend} calls this method to create a callback and get hold of the path to the callback
13268 * to pass to the server, which will be used to call the callback with its payload in the JSONP response.
13270 createCallback: function(url) {
13271 var callbackId = '_' + (callbacks.$$counter++).toString(36);
13272 var callbackPath = 'angular.callbacks.' + callbackId;
13273 var callback = createCallback(callbackId);
13274 callbackMap[callbackPath] = callbacks[callbackId] = callback;
13275 return callbackPath;
13279 * @name $jsonpCallbacks#wasCalled
13280 * @param {string} callbackPath the path to the callback that was sent in the JSONP request
13281 * @returns {boolean} whether the callback has been called, as a result of the JSONP response
13283 * {@link $httpBackend} calls this method to find out whether the JSONP response actually called the
13284 * callback that was passed in the request.
13286 wasCalled: function(callbackPath) {
13287 return callbackMap[callbackPath].called;
13291 * @name $jsonpCallbacks#getResponse
13292 * @param {string} callbackPath the path to the callback that was sent in the JSONP request
13293 * @returns {*} the data received from the response via the registered callback
13295 * {@link $httpBackend} calls this method to get hold of the data that was provided to the callback
13296 * in the JSONP response.
13298 getResponse: function(callbackPath) {
13299 return callbackMap[callbackPath].data;
13303 * @name $jsonpCallbacks#removeCallback
13304 * @param {string} callbackPath the path to the callback that was sent in the JSONP request
13306 * {@link $httpBackend} calls this method to remove the callback after the JSONP request has
13307 * completed or timed-out.
13309 removeCallback: function(callbackPath) {
13310 var callback = callbackMap[callbackPath];
13311 delete callbacks[callback.id];
13312 delete callbackMap[callbackPath];
13323 * $locale service provides localization rules for various Angular components. As of right now the
13324 * only public api is:
13326 * * `id` – `{string}` – locale id formatted as `languageId-countryId` (e.g. `en-us`)
13329 var PATH_MATCH = /^([^?#]*)(\?([^#]*))?(#(.*))?$/,
13330 DEFAULT_PORTS = {'http': 80, 'https': 443, 'ftp': 21};
13331 var $locationMinErr = minErr('$location');
13335 * Encode path using encodeUriSegment, ignoring forward slashes
13337 * @param {string} path Path to encode
13338 * @returns {string}
13340 function encodePath(path) {
13341 var segments = path.split('/'),
13342 i = segments.length;
13345 segments[i] = encodeUriSegment(segments[i]);
13348 return segments.join('/');
13351 function parseAbsoluteUrl(absoluteUrl, locationObj) {
13352 var parsedUrl = urlResolve(absoluteUrl);
13354 locationObj.$$protocol = parsedUrl.protocol;
13355 locationObj.$$host = parsedUrl.hostname;
13356 locationObj.$$port = toInt(parsedUrl.port) || DEFAULT_PORTS[parsedUrl.protocol] || null;
13359 var DOUBLE_SLASH_REGEX = /^\s*[\\/]{2,}/;
13360 function parseAppUrl(url, locationObj) {
13362 if (DOUBLE_SLASH_REGEX.test(url)) {
13363 throw $locationMinErr('badpath', 'Invalid url "{0}".', url);
13366 var prefixed = (url.charAt(0) !== '/');
13370 var match = urlResolve(url);
13371 locationObj.$$path = decodeURIComponent(prefixed && match.pathname.charAt(0) === '/' ?
13372 match.pathname.substring(1) : match.pathname);
13373 locationObj.$$search = parseKeyValue(match.search);
13374 locationObj.$$hash = decodeURIComponent(match.hash);
13376 // make sure path starts with '/';
13377 if (locationObj.$$path && locationObj.$$path.charAt(0) !== '/') {
13378 locationObj.$$path = '/' + locationObj.$$path;
13382 function startsWith(str, search) {
13383 return str.slice(0, search.length) === search;
13388 * @param {string} base
13389 * @param {string} url
13390 * @returns {string} returns text from `url` after `base` or `undefined` if it does not begin with
13391 * the expected string.
13393 function stripBaseUrl(base, url) {
13394 if (startsWith(url, base)) {
13395 return url.substr(base.length);
13400 function stripHash(url) {
13401 var index = url.indexOf('#');
13402 return index === -1 ? url : url.substr(0, index);
13405 function trimEmptyHash(url) {
13406 return url.replace(/(#.+)|#$/, '$1');
13410 function stripFile(url) {
13411 return url.substr(0, stripHash(url).lastIndexOf('/') + 1);
13414 /* return the server only (scheme://host:port) */
13415 function serverBase(url) {
13416 return url.substring(0, url.indexOf('/', url.indexOf('//') + 2));
13421 * LocationHtml5Url represents a URL
13422 * This object is exposed as $location service when HTML5 mode is enabled and supported
13425 * @param {string} appBase application base URL
13426 * @param {string} appBaseNoFile application base URL stripped of any filename
13427 * @param {string} basePrefix URL path prefix
13429 function LocationHtml5Url(appBase, appBaseNoFile, basePrefix) {
13430 this.$$html5 = true;
13431 basePrefix = basePrefix || '';
13432 parseAbsoluteUrl(appBase, this);
13436 * Parse given HTML5 (regular) URL string into properties
13437 * @param {string} url HTML5 URL
13440 this.$$parse = function(url) {
13441 var pathUrl = stripBaseUrl(appBaseNoFile, url);
13442 if (!isString(pathUrl)) {
13443 throw $locationMinErr('ipthprfx', 'Invalid url "{0}", missing path prefix "{1}".', url,
13447 parseAppUrl(pathUrl, this);
13449 if (!this.$$path) {
13457 * Compose url and update `absUrl` property
13460 this.$$compose = function() {
13461 var search = toKeyValue(this.$$search),
13462 hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
13464 this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
13465 this.$$absUrl = appBaseNoFile + this.$$url.substr(1); // first char is always '/'
13467 this.$$urlUpdatedByLocation = true;
13470 this.$$parseLinkUrl = function(url, relHref) {
13471 if (relHref && relHref[0] === '#') {
13472 // special case for links to hash fragments:
13473 // keep the old url and only replace the hash fragment
13474 this.hash(relHref.slice(1));
13477 var appUrl, prevAppUrl;
13481 if (isDefined(appUrl = stripBaseUrl(appBase, url))) {
13482 prevAppUrl = appUrl;
13483 if (basePrefix && isDefined(appUrl = stripBaseUrl(basePrefix, appUrl))) {
13484 rewrittenUrl = appBaseNoFile + (stripBaseUrl('/', appUrl) || appUrl);
13486 rewrittenUrl = appBase + prevAppUrl;
13488 } else if (isDefined(appUrl = stripBaseUrl(appBaseNoFile, url))) {
13489 rewrittenUrl = appBaseNoFile + appUrl;
13490 } else if (appBaseNoFile === url + '/') {
13491 rewrittenUrl = appBaseNoFile;
13493 if (rewrittenUrl) {
13494 this.$$parse(rewrittenUrl);
13496 return !!rewrittenUrl;
13502 * LocationHashbangUrl represents URL
13503 * This object is exposed as $location service when developer doesn't opt into html5 mode.
13504 * It also serves as the base class for html5 mode fallback on legacy browsers.
13507 * @param {string} appBase application base URL
13508 * @param {string} appBaseNoFile application base URL stripped of any filename
13509 * @param {string} hashPrefix hashbang prefix
13511 function LocationHashbangUrl(appBase, appBaseNoFile, hashPrefix) {
13513 parseAbsoluteUrl(appBase, this);
13517 * Parse given hashbang URL into properties
13518 * @param {string} url Hashbang URL
13521 this.$$parse = function(url) {
13522 var withoutBaseUrl = stripBaseUrl(appBase, url) || stripBaseUrl(appBaseNoFile, url);
13523 var withoutHashUrl;
13525 if (!isUndefined(withoutBaseUrl) && withoutBaseUrl.charAt(0) === '#') {
13527 // The rest of the URL starts with a hash so we have
13528 // got either a hashbang path or a plain hash fragment
13529 withoutHashUrl = stripBaseUrl(hashPrefix, withoutBaseUrl);
13530 if (isUndefined(withoutHashUrl)) {
13531 // There was no hashbang prefix so we just have a hash fragment
13532 withoutHashUrl = withoutBaseUrl;
13536 // There was no hashbang path nor hash fragment:
13537 // If we are in HTML5 mode we use what is left as the path;
13538 // Otherwise we ignore what is left
13539 if (this.$$html5) {
13540 withoutHashUrl = withoutBaseUrl;
13542 withoutHashUrl = '';
13543 if (isUndefined(withoutBaseUrl)) {
13545 /** @type {?} */ (this).replace();
13550 parseAppUrl(withoutHashUrl, this);
13552 this.$$path = removeWindowsDriveName(this.$$path, withoutHashUrl, appBase);
13557 * In Windows, on an anchor node on documents loaded from
13558 * the filesystem, the browser will return a pathname
13559 * prefixed with the drive name ('/C:/path') when a
13560 * pathname without a drive is set:
13561 * * a.setAttribute('href', '/foo')
13562 * * a.pathname === '/C:/foo' //true
13564 * Inside of Angular, we're always using pathnames that
13565 * do not include drive names for routing.
13567 function removeWindowsDriveName(path, url, base) {
13569 Matches paths for file protocol on windows,
13570 such as /C:/foo/bar, and captures only /foo/bar.
13572 var windowsFilePathExp = /^\/[A-Z]:(\/.*)/;
13574 var firstPathSegmentMatch;
13576 //Get the relative path from the input URL.
13577 if (startsWith(url, base)) {
13578 url = url.replace(base, '');
13581 // The input URL intentionally contains a first path segment that ends with a colon.
13582 if (windowsFilePathExp.exec(url)) {
13586 firstPathSegmentMatch = windowsFilePathExp.exec(path);
13587 return firstPathSegmentMatch ? firstPathSegmentMatch[1] : path;
13592 * Compose hashbang URL and update `absUrl` property
13595 this.$$compose = function() {
13596 var search = toKeyValue(this.$$search),
13597 hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
13599 this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
13600 this.$$absUrl = appBase + (this.$$url ? hashPrefix + this.$$url : '');
13602 this.$$urlUpdatedByLocation = true;
13605 this.$$parseLinkUrl = function(url, relHref) {
13606 if (stripHash(appBase) === stripHash(url)) {
13616 * LocationHashbangUrl represents URL
13617 * This object is exposed as $location service when html5 history api is enabled but the browser
13618 * does not support it.
13621 * @param {string} appBase application base URL
13622 * @param {string} appBaseNoFile application base URL stripped of any filename
13623 * @param {string} hashPrefix hashbang prefix
13625 function LocationHashbangInHtml5Url(appBase, appBaseNoFile, hashPrefix) {
13626 this.$$html5 = true;
13627 LocationHashbangUrl.apply(this, arguments);
13629 this.$$parseLinkUrl = function(url, relHref) {
13630 if (relHref && relHref[0] === '#') {
13631 // special case for links to hash fragments:
13632 // keep the old url and only replace the hash fragment
13633 this.hash(relHref.slice(1));
13640 if (appBase === stripHash(url)) {
13641 rewrittenUrl = url;
13642 } else if ((appUrl = stripBaseUrl(appBaseNoFile, url))) {
13643 rewrittenUrl = appBase + hashPrefix + appUrl;
13644 } else if (appBaseNoFile === url + '/') {
13645 rewrittenUrl = appBaseNoFile;
13647 if (rewrittenUrl) {
13648 this.$$parse(rewrittenUrl);
13650 return !!rewrittenUrl;
13653 this.$$compose = function() {
13654 var search = toKeyValue(this.$$search),
13655 hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
13657 this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
13658 // include hashPrefix in $$absUrl when $$url is empty so IE9 does not reload page because of removal of '#'
13659 this.$$absUrl = appBase + hashPrefix + this.$$url;
13661 this.$$urlUpdatedByLocation = true;
13667 var locationPrototype = {
13670 * Ensure absolute URL is initialized.
13676 * Are we in html5 mode?
13682 * Has any change been replacing?
13689 * @name $location#absUrl
13692 * This method is getter only.
13694 * Return full URL representation with all segments encoded according to rules specified in
13695 * [RFC 3986](http://www.ietf.org/rfc/rfc3986.txt).
13699 * // given URL http://example.com/#/some/path?foo=bar&baz=xoxo
13700 * var absUrl = $location.absUrl();
13701 * // => "http://example.com/#/some/path?foo=bar&baz=xoxo"
13704 * @return {string} full URL
13706 absUrl: locationGetter('$$absUrl'),
13710 * @name $location#url
13713 * This method is getter / setter.
13715 * Return URL (e.g. `/path?a=b#hash`) when called without any parameter.
13717 * Change path, search and hash, when called with parameter and return `$location`.
13721 * // given URL http://example.com/#/some/path?foo=bar&baz=xoxo
13722 * var url = $location.url();
13723 * // => "/some/path?foo=bar&baz=xoxo"
13726 * @param {string=} url New URL without base prefix (e.g. `/path?a=b#hash`)
13727 * @return {string} url
13729 url: function(url) {
13730 if (isUndefined(url)) {
13734 var match = PATH_MATCH.exec(url);
13735 if (match[1] || url === '') this.path(decodeURIComponent(match[1]));
13736 if (match[2] || match[1] || url === '') this.search(match[3] || '');
13737 this.hash(match[5] || '');
13744 * @name $location#protocol
13747 * This method is getter only.
13749 * Return protocol of current URL.
13753 * // given URL http://example.com/#/some/path?foo=bar&baz=xoxo
13754 * var protocol = $location.protocol();
13758 * @return {string} protocol of current URL
13760 protocol: locationGetter('$$protocol'),
13764 * @name $location#host
13767 * This method is getter only.
13769 * Return host of current URL.
13771 * Note: compared to the non-angular version `location.host` which returns `hostname:port`, this returns the `hostname` portion only.
13775 * // given URL http://example.com/#/some/path?foo=bar&baz=xoxo
13776 * var host = $location.host();
13777 * // => "example.com"
13779 * // given URL http://user:password@example.com:8080/#/some/path?foo=bar&baz=xoxo
13780 * host = $location.host();
13781 * // => "example.com"
13782 * host = location.host;
13783 * // => "example.com:8080"
13786 * @return {string} host of current URL.
13788 host: locationGetter('$$host'),
13792 * @name $location#port
13795 * This method is getter only.
13797 * Return port of current URL.
13801 * // given URL http://example.com/#/some/path?foo=bar&baz=xoxo
13802 * var port = $location.port();
13806 * @return {Number} port
13808 port: locationGetter('$$port'),
13812 * @name $location#path
13815 * This method is getter / setter.
13817 * Return path of current URL when called without any parameter.
13819 * Change path when called with parameter and return `$location`.
13821 * Note: Path should always begin with forward slash (/), this method will add the forward slash
13822 * if it is missing.
13826 * // given URL http://example.com/#/some/path?foo=bar&baz=xoxo
13827 * var path = $location.path();
13828 * // => "/some/path"
13831 * @param {(string|number)=} path New path
13832 * @return {(string|object)} path if called with no parameters, or `$location` if called with a parameter
13834 path: locationGetterSetter('$$path', function(path) {
13835 path = path !== null ? path.toString() : '';
13836 return path.charAt(0) === '/' ? path : '/' + path;
13841 * @name $location#search
13844 * This method is getter / setter.
13846 * Return search part (as object) of current URL when called without any parameter.
13848 * Change search part when called with parameter and return `$location`.
13852 * // given URL http://example.com/#/some/path?foo=bar&baz=xoxo
13853 * var searchObject = $location.search();
13854 * // => {foo: 'bar', baz: 'xoxo'}
13856 * // set foo to 'yipee'
13857 * $location.search('foo', 'yipee');
13858 * // $location.search() => {foo: 'yipee', baz: 'xoxo'}
13861 * @param {string|Object.<string>|Object.<Array.<string>>} search New search params - string or
13864 * When called with a single argument the method acts as a setter, setting the `search` component
13865 * of `$location` to the specified value.
13867 * If the argument is a hash object containing an array of values, these values will be encoded
13868 * as duplicate search parameters in the URL.
13870 * @param {(string|Number|Array<string>|boolean)=} paramValue If `search` is a string or number, then `paramValue`
13871 * will override only a single search property.
13873 * If `paramValue` is an array, it will override the property of the `search` component of
13874 * `$location` specified via the first argument.
13876 * If `paramValue` is `null`, the property specified via the first argument will be deleted.
13878 * If `paramValue` is `true`, the property specified via the first argument will be added with no
13879 * value nor trailing equal sign.
13881 * @return {Object} If called with no arguments returns the parsed `search` object. If called with
13882 * one or more arguments returns `$location` object itself.
13884 search: function(search, paramValue) {
13885 switch (arguments.length) {
13887 return this.$$search;
13889 if (isString(search) || isNumber(search)) {
13890 search = search.toString();
13891 this.$$search = parseKeyValue(search);
13892 } else if (isObject(search)) {
13893 search = copy(search, {});
13894 // remove object undefined or null properties
13895 forEach(search, function(value, key) {
13896 if (value == null) delete search[key];
13899 this.$$search = search;
13901 throw $locationMinErr('isrcharg',
13902 'The first argument of the `$location#search()` call must be a string or an object.');
13906 if (isUndefined(paramValue) || paramValue === null) {
13907 delete this.$$search[search];
13909 this.$$search[search] = paramValue;
13919 * @name $location#hash
13922 * This method is getter / setter.
13924 * Returns the hash fragment when called without any parameters.
13926 * Changes the hash fragment when called with a parameter and returns `$location`.
13930 * // given URL http://example.com/#/some/path?foo=bar&baz=xoxo#hashValue
13931 * var hash = $location.hash();
13932 * // => "hashValue"
13935 * @param {(string|number)=} hash New hash fragment
13936 * @return {string} hash
13938 hash: locationGetterSetter('$$hash', function(hash) {
13939 return hash !== null ? hash.toString() : '';
13944 * @name $location#replace
13947 * If called, all changes to $location during the current `$digest` will replace the current history
13948 * record, instead of adding a new one.
13950 replace: function() {
13951 this.$$replace = true;
13956 forEach([LocationHashbangInHtml5Url, LocationHashbangUrl, LocationHtml5Url], function(Location) {
13957 Location.prototype = Object.create(locationPrototype);
13961 * @name $location#state
13964 * This method is getter / setter.
13966 * Return the history state object when called without any parameter.
13968 * Change the history state object when called with one parameter and return `$location`.
13969 * The state object is later passed to `pushState` or `replaceState`.
13971 * NOTE: This method is supported only in HTML5 mode and only in browsers supporting
13972 * the HTML5 History API (i.e. methods `pushState` and `replaceState`). If you need to support
13973 * older browsers (like IE9 or Android < 4.0), don't use this method.
13975 * @param {object=} state State object for pushState or replaceState
13976 * @return {object} state
13978 Location.prototype.state = function(state) {
13979 if (!arguments.length) {
13980 return this.$$state;
13983 if (Location !== LocationHtml5Url || !this.$$html5) {
13984 throw $locationMinErr('nostate', 'History API state support is available only ' +
13985 'in HTML5 mode and only in browsers supporting HTML5 History API');
13987 // The user might modify `stateObject` after invoking `$location.state(stateObject)`
13988 // but we're changing the $$state reference to $browser.state() during the $digest
13989 // so the modification window is narrow.
13990 this.$$state = isUndefined(state) ? null : state;
13991 this.$$urlUpdatedByLocation = true;
13998 function locationGetter(property) {
13999 return /** @this */ function() {
14000 return this[property];
14005 function locationGetterSetter(property, preprocess) {
14006 return /** @this */ function(value) {
14007 if (isUndefined(value)) {
14008 return this[property];
14011 this[property] = preprocess(value);
14023 * @requires $rootElement
14026 * The $location service parses the URL in the browser address bar (based on the
14027 * [window.location](https://developer.mozilla.org/en/window.location)) and makes the URL
14028 * available to your application. Changes to the URL in the address bar are reflected into
14029 * $location service and changes to $location are reflected into the browser address bar.
14031 * **The $location service:**
14033 * - Exposes the current URL in the browser address bar, so you can
14034 * - Watch and observe the URL.
14035 * - Change the URL.
14036 * - Synchronizes the URL with the browser when the user
14037 * - Changes the address bar.
14038 * - Clicks the back or forward button (or clicks a History link).
14039 * - Clicks on a link.
14040 * - Represents the URL object as a set of methods (protocol, host, port, path, search, hash).
14042 * For more information see {@link guide/$location Developer Guide: Using $location}
14047 * @name $locationProvider
14051 * Use the `$locationProvider` to configure how the application deep linking paths are stored.
14053 function $LocationProvider() {
14054 var hashPrefix = '!',
14063 * @name $locationProvider#hashPrefix
14065 * The default value for the prefix is `'!'`.
14066 * @param {string=} prefix Prefix for hash part (containing path and search)
14067 * @returns {*} current value if used as getter or itself (chaining) if used as setter
14069 this.hashPrefix = function(prefix) {
14070 if (isDefined(prefix)) {
14071 hashPrefix = prefix;
14080 * @name $locationProvider#html5Mode
14082 * @param {(boolean|Object)=} mode If boolean, sets `html5Mode.enabled` to value.
14083 * If object, sets `enabled`, `requireBase` and `rewriteLinks` to respective values. Supported
14085 * - **enabled** – `{boolean}` – (default: false) If true, will rely on `history.pushState` to
14086 * change urls where supported. Will fall back to hash-prefixed paths in browsers that do not
14087 * support `pushState`.
14088 * - **requireBase** - `{boolean}` - (default: `true`) When html5Mode is enabled, specifies
14089 * whether or not a <base> tag is required to be present. If `enabled` and `requireBase` are
14090 * true, and a base tag is not present, an error will be thrown when `$location` is injected.
14091 * See the {@link guide/$location $location guide for more information}
14092 * - **rewriteLinks** - `{boolean|string}` - (default: `true`) When html5Mode is enabled,
14093 * enables/disables URL rewriting for relative links. If set to a string, URL rewriting will
14094 * only happen on links with an attribute that matches the given string. For example, if set
14095 * to `'internal-link'`, then the URL will only be rewritten for `<a internal-link>` links.
14096 * Note that [attribute name normalization](guide/directive#normalization) does not apply
14097 * here, so `'internalLink'` will **not** match `'internal-link'`.
14099 * @returns {Object} html5Mode object if used as getter or itself (chaining) if used as setter
14101 this.html5Mode = function(mode) {
14102 if (isBoolean(mode)) {
14103 html5Mode.enabled = mode;
14105 } else if (isObject(mode)) {
14107 if (isBoolean(mode.enabled)) {
14108 html5Mode.enabled = mode.enabled;
14111 if (isBoolean(mode.requireBase)) {
14112 html5Mode.requireBase = mode.requireBase;
14115 if (isBoolean(mode.rewriteLinks) || isString(mode.rewriteLinks)) {
14116 html5Mode.rewriteLinks = mode.rewriteLinks;
14127 * @name $location#$locationChangeStart
14128 * @eventType broadcast on root scope
14130 * Broadcasted before a URL will change.
14132 * This change can be prevented by calling
14133 * `preventDefault` method of the event. See {@link ng.$rootScope.Scope#$on} for more
14134 * details about event object. Upon successful change
14135 * {@link ng.$location#$locationChangeSuccess $locationChangeSuccess} is fired.
14137 * The `newState` and `oldState` parameters may be defined only in HTML5 mode and when
14138 * the browser supports the HTML5 History API.
14140 * @param {Object} angularEvent Synthetic event object.
14141 * @param {string} newUrl New URL
14142 * @param {string=} oldUrl URL that was before it was changed.
14143 * @param {string=} newState New history state object
14144 * @param {string=} oldState History state object that was before it was changed.
14149 * @name $location#$locationChangeSuccess
14150 * @eventType broadcast on root scope
14152 * Broadcasted after a URL was changed.
14154 * The `newState` and `oldState` parameters may be defined only in HTML5 mode and when
14155 * the browser supports the HTML5 History API.
14157 * @param {Object} angularEvent Synthetic event object.
14158 * @param {string} newUrl New URL
14159 * @param {string=} oldUrl URL that was before it was changed.
14160 * @param {string=} newState New history state object
14161 * @param {string=} oldState History state object that was before it was changed.
14164 this.$get = ['$rootScope', '$browser', '$sniffer', '$rootElement', '$window',
14165 function($rootScope, $browser, $sniffer, $rootElement, $window) {
14168 baseHref = $browser.baseHref(), // if base[href] is undefined, it defaults to ''
14169 initialUrl = $browser.url(),
14172 if (html5Mode.enabled) {
14173 if (!baseHref && html5Mode.requireBase) {
14174 throw $locationMinErr('nobase',
14175 '$location in HTML5 mode requires a <base> tag to be present!');
14177 appBase = serverBase(initialUrl) + (baseHref || '/');
14178 LocationMode = $sniffer.history ? LocationHtml5Url : LocationHashbangInHtml5Url;
14180 appBase = stripHash(initialUrl);
14181 LocationMode = LocationHashbangUrl;
14183 var appBaseNoFile = stripFile(appBase);
14185 $location = new LocationMode(appBase, appBaseNoFile, '#' + hashPrefix);
14186 $location.$$parseLinkUrl(initialUrl, initialUrl);
14188 $location.$$state = $browser.state();
14190 var IGNORE_URI_REGEXP = /^\s*(javascript|mailto):/i;
14192 function setBrowserUrlWithFallback(url, replace, state) {
14193 var oldUrl = $location.url();
14194 var oldState = $location.$$state;
14196 $browser.url(url, replace, state);
14198 // Make sure $location.state() returns referentially identical (not just deeply equal)
14199 // state object; this makes possible quick checking if the state changed in the digest
14200 // loop. Checking deep equality would be too expensive.
14201 $location.$$state = $browser.state();
14203 // Restore old values if pushState fails
14204 $location.url(oldUrl);
14205 $location.$$state = oldState;
14211 $rootElement.on('click', function(event) {
14212 var rewriteLinks = html5Mode.rewriteLinks;
14213 // TODO(vojta): rewrite link when opening in new tab/window (in legacy browser)
14214 // currently we open nice url link and redirect then
14216 if (!rewriteLinks || event.ctrlKey || event.metaKey || event.shiftKey || event.which === 2 || event.button === 2) return;
14218 var elm = jqLite(event.target);
14220 // traverse the DOM up to find first A tag
14221 while (nodeName_(elm[0]) !== 'a') {
14222 // ignore rewriting if no A tag (reached root element, or no parent - removed from document)
14223 if (elm[0] === $rootElement[0] || !(elm = elm.parent())[0]) return;
14226 if (isString(rewriteLinks) && isUndefined(elm.attr(rewriteLinks))) return;
14228 var absHref = elm.prop('href');
14229 // get the actual href attribute - see
14230 // http://msdn.microsoft.com/en-us/library/ie/dd347148(v=vs.85).aspx
14231 var relHref = elm.attr('href') || elm.attr('xlink:href');
14233 if (isObject(absHref) && absHref.toString() === '[object SVGAnimatedString]') {
14234 // SVGAnimatedString.animVal should be identical to SVGAnimatedString.baseVal, unless during
14236 absHref = urlResolve(absHref.animVal).href;
14239 // Ignore when url is started with javascript: or mailto:
14240 if (IGNORE_URI_REGEXP.test(absHref)) return;
14242 if (absHref && !elm.attr('target') && !event.isDefaultPrevented()) {
14243 if ($location.$$parseLinkUrl(absHref, relHref)) {
14244 // We do a preventDefault for all urls that are part of the angular application,
14245 // in html5mode and also without, so that we are able to abort navigation without
14246 // getting double entries in the location history.
14247 event.preventDefault();
14248 // update location manually
14249 if ($location.absUrl() !== $browser.url()) {
14250 $rootScope.$apply();
14251 // hack to work around FF6 bug 684208 when scenario runner clicks on links
14252 $window.angular['ff-684208-preventDefault'] = true;
14259 // rewrite hashbang url <> html5 url
14260 if (trimEmptyHash($location.absUrl()) !== trimEmptyHash(initialUrl)) {
14261 $browser.url($location.absUrl(), true);
14264 var initializing = true;
14266 // update $location when $browser url changes
14267 $browser.onUrlChange(function(newUrl, newState) {
14269 if (!startsWith(newUrl, appBaseNoFile)) {
14270 // If we are navigating outside of the app then force a reload
14271 $window.location.href = newUrl;
14275 $rootScope.$evalAsync(function() {
14276 var oldUrl = $location.absUrl();
14277 var oldState = $location.$$state;
14278 var defaultPrevented;
14279 newUrl = trimEmptyHash(newUrl);
14280 $location.$$parse(newUrl);
14281 $location.$$state = newState;
14283 defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
14284 newState, oldState).defaultPrevented;
14286 // if the location was changed by a `$locationChangeStart` handler then stop
14287 // processing this location change
14288 if ($location.absUrl() !== newUrl) return;
14290 if (defaultPrevented) {
14291 $location.$$parse(oldUrl);
14292 $location.$$state = oldState;
14293 setBrowserUrlWithFallback(oldUrl, false, oldState);
14295 initializing = false;
14296 afterLocationChange(oldUrl, oldState);
14299 if (!$rootScope.$$phase) $rootScope.$digest();
14303 $rootScope.$watch(function $locationWatch() {
14304 if (initializing || $location.$$urlUpdatedByLocation) {
14305 $location.$$urlUpdatedByLocation = false;
14307 var oldUrl = trimEmptyHash($browser.url());
14308 var newUrl = trimEmptyHash($location.absUrl());
14309 var oldState = $browser.state();
14310 var currentReplace = $location.$$replace;
14311 var urlOrStateChanged = oldUrl !== newUrl ||
14312 ($location.$$html5 && $sniffer.history && oldState !== $location.$$state);
14314 if (initializing || urlOrStateChanged) {
14315 initializing = false;
14317 $rootScope.$evalAsync(function() {
14318 var newUrl = $location.absUrl();
14319 var defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
14320 $location.$$state, oldState).defaultPrevented;
14322 // if the location was changed by a `$locationChangeStart` handler then stop
14323 // processing this location change
14324 if ($location.absUrl() !== newUrl) return;
14326 if (defaultPrevented) {
14327 $location.$$parse(oldUrl);
14328 $location.$$state = oldState;
14330 if (urlOrStateChanged) {
14331 setBrowserUrlWithFallback(newUrl, currentReplace,
14332 oldState === $location.$$state ? null : $location.$$state);
14334 afterLocationChange(oldUrl, oldState);
14340 $location.$$replace = false;
14342 // we don't need to return anything because $evalAsync will make the digest loop dirty when
14343 // there is a change
14348 function afterLocationChange(oldUrl, oldState) {
14349 $rootScope.$broadcast('$locationChangeSuccess', $location.absUrl(), oldUrl,
14350 $location.$$state, oldState);
14358 * @requires $window
14361 * Simple service for logging. Default implementation safely writes the message
14362 * into the browser's console (if present).
14364 * The main purpose of this service is to simplify debugging and troubleshooting.
14366 * The default is to log `debug` messages. You can use
14367 * {@link ng.$logProvider ng.$logProvider#debugEnabled} to change this.
14370 <example module="logExample" name="log-service">
14371 <file name="script.js">
14372 angular.module('logExample', [])
14373 .controller('LogController', ['$scope', '$log', function($scope, $log) {
14374 $scope.$log = $log;
14375 $scope.message = 'Hello World!';
14378 <file name="index.html">
14379 <div ng-controller="LogController">
14380 <p>Reload this page with open console, enter text and hit the log button...</p>
14382 <input type="text" ng-model="message" /></label>
14383 <button ng-click="$log.log(message)">log</button>
14384 <button ng-click="$log.warn(message)">warn</button>
14385 <button ng-click="$log.info(message)">info</button>
14386 <button ng-click="$log.error(message)">error</button>
14387 <button ng-click="$log.debug(message)">debug</button>
14395 * @name $logProvider
14399 * Use the `$logProvider` to configure how the application logs messages
14401 function $LogProvider() {
14407 * @name $logProvider#debugEnabled
14409 * @param {boolean=} flag enable or disable debug level messages
14410 * @returns {*} current value if used as getter or itself (chaining) if used as setter
14412 this.debugEnabled = function(flag) {
14413 if (isDefined(flag)) {
14421 this.$get = ['$window', function($window) {
14422 // Support: IE 9-11, Edge 12-14+
14423 // IE/Edge display errors in such a way that it requires the user to click in 4 places
14424 // to see the stack trace. There is no way to feature-detect it so there's a chance
14425 // of the user agent sniffing to go wrong but since it's only about logging, this shouldn't
14426 // break apps. Other browsers display errors in a sensible way and some of them map stack
14427 // traces along source maps if available so it makes sense to let browsers display it
14429 var formatStackTrace = msie || /\bEdge\//.test($window.navigator && $window.navigator.userAgent);
14437 * Write a log message
14439 log: consoleLog('log'),
14446 * Write an information message
14448 info: consoleLog('info'),
14455 * Write a warning message
14457 warn: consoleLog('warn'),
14464 * Write an error message
14466 error: consoleLog('error'),
14473 * Write a debug message
14475 debug: (function() {
14476 var fn = consoleLog('debug');
14478 return function() {
14480 fn.apply(self, arguments);
14486 function formatError(arg) {
14487 if (arg instanceof Error) {
14488 if (arg.stack && formatStackTrace) {
14489 arg = (arg.message && arg.stack.indexOf(arg.message) === -1)
14490 ? 'Error: ' + arg.message + '\n' + arg.stack
14492 } else if (arg.sourceURL) {
14493 arg = arg.message + '\n' + arg.sourceURL + ':' + arg.line;
14499 function consoleLog(type) {
14500 var console = $window.console || {},
14501 logFn = console[type] || console.log || noop,
14504 // Note: reading logFn.apply throws an error in IE11 in IE8 document mode.
14505 // The reason behind this is that console.log has type "object" in IE8...
14507 hasApply = !!logFn.apply;
14508 } catch (e) { /* empty */ }
14511 return function() {
14513 forEach(arguments, function(arg) {
14514 args.push(formatError(arg));
14516 return logFn.apply(console, args);
14520 // we are IE which either doesn't have window.console => this is noop and we do nothing,
14521 // or we are IE where console.log doesn't have apply so we log at least first 2 args
14522 return function(arg1, arg2) {
14523 logFn(arg1, arg2 == null ? '' : arg2);
14529 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
14530 * Any commits to this file should be reviewed with security in mind. *
14531 * Changes to this file can potentially create security vulnerabilities. *
14532 * An approval from 2 Core members with history of modifying *
14533 * this file is required. *
14535 * Does the change somehow allow for arbitrary javascript to be executed? *
14536 * Or allows for someone to change the prototype of built-in objects? *
14537 * Or gives undesired access to variables likes document or window? *
14538 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
14540 var $parseMinErr = minErr('$parse');
14542 var objectValueOf = {}.constructor.prototype.valueOf;
14544 // Sandboxing Angular Expressions
14545 // ------------------------------
14546 // Angular expressions are no longer sandboxed. So it is now even easier to access arbitrary JS code by
14547 // various means such as obtaining a reference to native JS functions like the Function constructor.
14549 // As an example, consider the following Angular expression:
14551 // {}.toString.constructor('alert("evil JS code")')
14553 // It is important to realize that if you create an expression from a string that contains user provided
14554 // content then it is possible that your application contains a security vulnerability to an XSS style attack.
14556 // See https://docs.angularjs.org/guide/security
14559 function getStringValue(name) {
14560 // Property names must be strings. This means that non-string objects cannot be used
14561 // as keys in an object. Any non-string object, including a number, is typecasted
14562 // into a string via the toString method.
14563 // -- MDN, https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Property_accessors#Property_names
14565 // So, to ensure that we are checking the same `name` that JavaScript would use, we cast it
14566 // to a string. It's not always possible. If `name` is an object and its `toString` method is
14567 // 'broken' (doesn't return a string, isn't a function, etc.), an error will be thrown:
14569 // TypeError: Cannot convert object to primitive value
14571 // For performance reasons, we don't catch this error here and allow it to propagate up the call
14572 // stack. Note that you'll get the same error in JavaScript if you try to access a property using
14573 // such a 'broken' object as a key.
14578 var OPERATORS = createMap();
14579 forEach('+ - * / % === !== == != < > <= >= && || ! = |'.split(' '), function(operator) { OPERATORS[operator] = true; });
14580 var ESCAPE = {'n':'\n', 'f':'\f', 'r':'\r', 't':'\t', 'v':'\v', '\'':'\'', '"':'"'};
14583 /////////////////////////////////////////
14589 var Lexer = function Lexer(options) {
14590 this.options = options;
14593 Lexer.prototype = {
14594 constructor: Lexer,
14596 lex: function(text) {
14601 while (this.index < this.text.length) {
14602 var ch = this.text.charAt(this.index);
14603 if (ch === '"' || ch === '\'') {
14604 this.readString(ch);
14605 } else if (this.isNumber(ch) || ch === '.' && this.isNumber(this.peek())) {
14607 } else if (this.isIdentifierStart(this.peekMultichar())) {
14609 } else if (this.is(ch, '(){}[].,;:?')) {
14610 this.tokens.push({index: this.index, text: ch});
14612 } else if (this.isWhitespace(ch)) {
14615 var ch2 = ch + this.peek();
14616 var ch3 = ch2 + this.peek(2);
14617 var op1 = OPERATORS[ch];
14618 var op2 = OPERATORS[ch2];
14619 var op3 = OPERATORS[ch3];
14620 if (op1 || op2 || op3) {
14621 var token = op3 ? ch3 : (op2 ? ch2 : ch);
14622 this.tokens.push({index: this.index, text: token, operator: true});
14623 this.index += token.length;
14625 this.throwError('Unexpected next character ', this.index, this.index + 1);
14629 return this.tokens;
14632 is: function(ch, chars) {
14633 return chars.indexOf(ch) !== -1;
14636 peek: function(i) {
14638 return (this.index + num < this.text.length) ? this.text.charAt(this.index + num) : false;
14641 isNumber: function(ch) {
14642 return ('0' <= ch && ch <= '9') && typeof ch === 'string';
14645 isWhitespace: function(ch) {
14646 // IE treats non-breaking space as \u00A0
14647 return (ch === ' ' || ch === '\r' || ch === '\t' ||
14648 ch === '\n' || ch === '\v' || ch === '\u00A0');
14651 isIdentifierStart: function(ch) {
14652 return this.options.isIdentifierStart ?
14653 this.options.isIdentifierStart(ch, this.codePointAt(ch)) :
14654 this.isValidIdentifierStart(ch);
14657 isValidIdentifierStart: function(ch) {
14658 return ('a' <= ch && ch <= 'z' ||
14659 'A' <= ch && ch <= 'Z' ||
14660 '_' === ch || ch === '$');
14663 isIdentifierContinue: function(ch) {
14664 return this.options.isIdentifierContinue ?
14665 this.options.isIdentifierContinue(ch, this.codePointAt(ch)) :
14666 this.isValidIdentifierContinue(ch);
14669 isValidIdentifierContinue: function(ch, cp) {
14670 return this.isValidIdentifierStart(ch, cp) || this.isNumber(ch);
14673 codePointAt: function(ch) {
14674 if (ch.length === 1) return ch.charCodeAt(0);
14675 // eslint-disable-next-line no-bitwise
14676 return (ch.charCodeAt(0) << 10) + ch.charCodeAt(1) - 0x35FDC00;
14679 peekMultichar: function() {
14680 var ch = this.text.charAt(this.index);
14681 var peek = this.peek();
14685 var cp1 = ch.charCodeAt(0);
14686 var cp2 = peek.charCodeAt(0);
14687 if (cp1 >= 0xD800 && cp1 <= 0xDBFF && cp2 >= 0xDC00 && cp2 <= 0xDFFF) {
14693 isExpOperator: function(ch) {
14694 return (ch === '-' || ch === '+' || this.isNumber(ch));
14697 throwError: function(error, start, end) {
14698 end = end || this.index;
14699 var colStr = (isDefined(start)
14700 ? 's ' + start + '-' + this.index + ' [' + this.text.substring(start, end) + ']'
14702 throw $parseMinErr('lexerr', 'Lexer Error: {0} at column{1} in expression [{2}].',
14703 error, colStr, this.text);
14706 readNumber: function() {
14708 var start = this.index;
14709 while (this.index < this.text.length) {
14710 var ch = lowercase(this.text.charAt(this.index));
14711 if (ch === '.' || this.isNumber(ch)) {
14714 var peekCh = this.peek();
14715 if (ch === 'e' && this.isExpOperator(peekCh)) {
14717 } else if (this.isExpOperator(ch) &&
14718 peekCh && this.isNumber(peekCh) &&
14719 number.charAt(number.length - 1) === 'e') {
14721 } else if (this.isExpOperator(ch) &&
14722 (!peekCh || !this.isNumber(peekCh)) &&
14723 number.charAt(number.length - 1) === 'e') {
14724 this.throwError('Invalid exponent');
14735 value: Number(number)
14739 readIdent: function() {
14740 var start = this.index;
14741 this.index += this.peekMultichar().length;
14742 while (this.index < this.text.length) {
14743 var ch = this.peekMultichar();
14744 if (!this.isIdentifierContinue(ch)) {
14747 this.index += ch.length;
14751 text: this.text.slice(start, this.index),
14756 readString: function(quote) {
14757 var start = this.index;
14760 var rawString = quote;
14761 var escape = false;
14762 while (this.index < this.text.length) {
14763 var ch = this.text.charAt(this.index);
14767 var hex = this.text.substring(this.index + 1, this.index + 5);
14768 if (!hex.match(/[\da-f]{4}/i)) {
14769 this.throwError('Invalid unicode escape [\\u' + hex + ']');
14772 string += String.fromCharCode(parseInt(hex, 16));
14774 var rep = ESCAPE[ch];
14775 string = string + (rep || ch);
14778 } else if (ch === '\\') {
14780 } else if (ch === quote) {
14794 this.throwError('Unterminated quote', start);
14798 var AST = function AST(lexer, options) {
14799 this.lexer = lexer;
14800 this.options = options;
14803 AST.Program = 'Program';
14804 AST.ExpressionStatement = 'ExpressionStatement';
14805 AST.AssignmentExpression = 'AssignmentExpression';
14806 AST.ConditionalExpression = 'ConditionalExpression';
14807 AST.LogicalExpression = 'LogicalExpression';
14808 AST.BinaryExpression = 'BinaryExpression';
14809 AST.UnaryExpression = 'UnaryExpression';
14810 AST.CallExpression = 'CallExpression';
14811 AST.MemberExpression = 'MemberExpression';
14812 AST.Identifier = 'Identifier';
14813 AST.Literal = 'Literal';
14814 AST.ArrayExpression = 'ArrayExpression';
14815 AST.Property = 'Property';
14816 AST.ObjectExpression = 'ObjectExpression';
14817 AST.ThisExpression = 'ThisExpression';
14818 AST.LocalsExpression = 'LocalsExpression';
14820 // Internal use only
14821 AST.NGValueParameter = 'NGValueParameter';
14824 ast: function(text) {
14826 this.tokens = this.lexer.lex(text);
14828 var value = this.program();
14830 if (this.tokens.length !== 0) {
14831 this.throwError('is an unexpected token', this.tokens[0]);
14837 program: function() {
14840 if (this.tokens.length > 0 && !this.peek('}', ')', ';', ']'))
14841 body.push(this.expressionStatement());
14842 if (!this.expect(';')) {
14843 return { type: AST.Program, body: body};
14848 expressionStatement: function() {
14849 return { type: AST.ExpressionStatement, expression: this.filterChain() };
14852 filterChain: function() {
14853 var left = this.expression();
14854 while (this.expect('|')) {
14855 left = this.filter(left);
14860 expression: function() {
14861 return this.assignment();
14864 assignment: function() {
14865 var result = this.ternary();
14866 if (this.expect('=')) {
14867 if (!isAssignable(result)) {
14868 throw $parseMinErr('lval', 'Trying to assign a value to a non l-value');
14871 result = { type: AST.AssignmentExpression, left: result, right: this.assignment(), operator: '='};
14876 ternary: function() {
14877 var test = this.logicalOR();
14880 if (this.expect('?')) {
14881 alternate = this.expression();
14882 if (this.consume(':')) {
14883 consequent = this.expression();
14884 return { type: AST.ConditionalExpression, test: test, alternate: alternate, consequent: consequent};
14890 logicalOR: function() {
14891 var left = this.logicalAND();
14892 while (this.expect('||')) {
14893 left = { type: AST.LogicalExpression, operator: '||', left: left, right: this.logicalAND() };
14898 logicalAND: function() {
14899 var left = this.equality();
14900 while (this.expect('&&')) {
14901 left = { type: AST.LogicalExpression, operator: '&&', left: left, right: this.equality()};
14906 equality: function() {
14907 var left = this.relational();
14909 while ((token = this.expect('==','!=','===','!=='))) {
14910 left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.relational() };
14915 relational: function() {
14916 var left = this.additive();
14918 while ((token = this.expect('<', '>', '<=', '>='))) {
14919 left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.additive() };
14924 additive: function() {
14925 var left = this.multiplicative();
14927 while ((token = this.expect('+','-'))) {
14928 left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.multiplicative() };
14933 multiplicative: function() {
14934 var left = this.unary();
14936 while ((token = this.expect('*','/','%'))) {
14937 left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.unary() };
14942 unary: function() {
14944 if ((token = this.expect('+', '-', '!'))) {
14945 return { type: AST.UnaryExpression, operator: token.text, prefix: true, argument: this.unary() };
14947 return this.primary();
14951 primary: function() {
14953 if (this.expect('(')) {
14954 primary = this.filterChain();
14956 } else if (this.expect('[')) {
14957 primary = this.arrayDeclaration();
14958 } else if (this.expect('{')) {
14959 primary = this.object();
14960 } else if (this.selfReferential.hasOwnProperty(this.peek().text)) {
14961 primary = copy(this.selfReferential[this.consume().text]);
14962 } else if (this.options.literals.hasOwnProperty(this.peek().text)) {
14963 primary = { type: AST.Literal, value: this.options.literals[this.consume().text]};
14964 } else if (this.peek().identifier) {
14965 primary = this.identifier();
14966 } else if (this.peek().constant) {
14967 primary = this.constant();
14969 this.throwError('not a primary expression', this.peek());
14973 while ((next = this.expect('(', '[', '.'))) {
14974 if (next.text === '(') {
14975 primary = {type: AST.CallExpression, callee: primary, arguments: this.parseArguments() };
14977 } else if (next.text === '[') {
14978 primary = { type: AST.MemberExpression, object: primary, property: this.expression(), computed: true };
14980 } else if (next.text === '.') {
14981 primary = { type: AST.MemberExpression, object: primary, property: this.identifier(), computed: false };
14983 this.throwError('IMPOSSIBLE');
14989 filter: function(baseExpression) {
14990 var args = [baseExpression];
14991 var result = {type: AST.CallExpression, callee: this.identifier(), arguments: args, filter: true};
14993 while (this.expect(':')) {
14994 args.push(this.expression());
15000 parseArguments: function() {
15002 if (this.peekToken().text !== ')') {
15004 args.push(this.filterChain());
15005 } while (this.expect(','));
15010 identifier: function() {
15011 var token = this.consume();
15012 if (!token.identifier) {
15013 this.throwError('is not a valid identifier', token);
15015 return { type: AST.Identifier, name: token.text };
15018 constant: function() {
15019 // TODO check that it is a constant
15020 return { type: AST.Literal, value: this.consume().value };
15023 arrayDeclaration: function() {
15025 if (this.peekToken().text !== ']') {
15027 if (this.peek(']')) {
15028 // Support trailing commas per ES5.1.
15031 elements.push(this.expression());
15032 } while (this.expect(','));
15036 return { type: AST.ArrayExpression, elements: elements };
15039 object: function() {
15040 var properties = [], property;
15041 if (this.peekToken().text !== '}') {
15043 if (this.peek('}')) {
15044 // Support trailing commas per ES5.1.
15047 property = {type: AST.Property, kind: 'init'};
15048 if (this.peek().constant) {
15049 property.key = this.constant();
15050 property.computed = false;
15052 property.value = this.expression();
15053 } else if (this.peek().identifier) {
15054 property.key = this.identifier();
15055 property.computed = false;
15056 if (this.peek(':')) {
15058 property.value = this.expression();
15060 property.value = property.key;
15062 } else if (this.peek('[')) {
15064 property.key = this.expression();
15066 property.computed = true;
15068 property.value = this.expression();
15070 this.throwError('invalid key', this.peek());
15072 properties.push(property);
15073 } while (this.expect(','));
15077 return {type: AST.ObjectExpression, properties: properties };
15080 throwError: function(msg, token) {
15081 throw $parseMinErr('syntax',
15082 'Syntax Error: Token \'{0}\' {1} at column {2} of the expression [{3}] starting at [{4}].',
15083 token.text, msg, (token.index + 1), this.text, this.text.substring(token.index));
15086 consume: function(e1) {
15087 if (this.tokens.length === 0) {
15088 throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text);
15091 var token = this.expect(e1);
15093 this.throwError('is unexpected, expecting [' + e1 + ']', this.peek());
15098 peekToken: function() {
15099 if (this.tokens.length === 0) {
15100 throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text);
15102 return this.tokens[0];
15105 peek: function(e1, e2, e3, e4) {
15106 return this.peekAhead(0, e1, e2, e3, e4);
15109 peekAhead: function(i, e1, e2, e3, e4) {
15110 if (this.tokens.length > i) {
15111 var token = this.tokens[i];
15112 var t = token.text;
15113 if (t === e1 || t === e2 || t === e3 || t === e4 ||
15114 (!e1 && !e2 && !e3 && !e4)) {
15121 expect: function(e1, e2, e3, e4) {
15122 var token = this.peek(e1, e2, e3, e4);
15124 this.tokens.shift();
15131 'this': {type: AST.ThisExpression },
15132 '$locals': {type: AST.LocalsExpression }
15136 function ifDefined(v, d) {
15137 return typeof v !== 'undefined' ? v : d;
15140 function plusFn(l, r) {
15141 if (typeof l === 'undefined') return r;
15142 if (typeof r === 'undefined') return l;
15146 function isStateless($filter, filterName) {
15147 var fn = $filter(filterName);
15148 return !fn.$stateful;
15151 function findConstantAndWatchExpressions(ast, $filter) {
15154 var isStatelessFilter;
15155 switch (ast.type) {
15157 allConstants = true;
15158 forEach(ast.body, function(expr) {
15159 findConstantAndWatchExpressions(expr.expression, $filter);
15160 allConstants = allConstants && expr.expression.constant;
15162 ast.constant = allConstants;
15165 ast.constant = true;
15168 case AST.UnaryExpression:
15169 findConstantAndWatchExpressions(ast.argument, $filter);
15170 ast.constant = ast.argument.constant;
15171 ast.toWatch = ast.argument.toWatch;
15173 case AST.BinaryExpression:
15174 findConstantAndWatchExpressions(ast.left, $filter);
15175 findConstantAndWatchExpressions(ast.right, $filter);
15176 ast.constant = ast.left.constant && ast.right.constant;
15177 ast.toWatch = ast.left.toWatch.concat(ast.right.toWatch);
15179 case AST.LogicalExpression:
15180 findConstantAndWatchExpressions(ast.left, $filter);
15181 findConstantAndWatchExpressions(ast.right, $filter);
15182 ast.constant = ast.left.constant && ast.right.constant;
15183 ast.toWatch = ast.constant ? [] : [ast];
15185 case AST.ConditionalExpression:
15186 findConstantAndWatchExpressions(ast.test, $filter);
15187 findConstantAndWatchExpressions(ast.alternate, $filter);
15188 findConstantAndWatchExpressions(ast.consequent, $filter);
15189 ast.constant = ast.test.constant && ast.alternate.constant && ast.consequent.constant;
15190 ast.toWatch = ast.constant ? [] : [ast];
15192 case AST.Identifier:
15193 ast.constant = false;
15194 ast.toWatch = [ast];
15196 case AST.MemberExpression:
15197 findConstantAndWatchExpressions(ast.object, $filter);
15198 if (ast.computed) {
15199 findConstantAndWatchExpressions(ast.property, $filter);
15201 ast.constant = ast.object.constant && (!ast.computed || ast.property.constant);
15202 ast.toWatch = [ast];
15204 case AST.CallExpression:
15205 isStatelessFilter = ast.filter ? isStateless($filter, ast.callee.name) : false;
15206 allConstants = isStatelessFilter;
15208 forEach(ast.arguments, function(expr) {
15209 findConstantAndWatchExpressions(expr, $filter);
15210 allConstants = allConstants && expr.constant;
15211 if (!expr.constant) {
15212 argsToWatch.push.apply(argsToWatch, expr.toWatch);
15215 ast.constant = allConstants;
15216 ast.toWatch = isStatelessFilter ? argsToWatch : [ast];
15218 case AST.AssignmentExpression:
15219 findConstantAndWatchExpressions(ast.left, $filter);
15220 findConstantAndWatchExpressions(ast.right, $filter);
15221 ast.constant = ast.left.constant && ast.right.constant;
15222 ast.toWatch = [ast];
15224 case AST.ArrayExpression:
15225 allConstants = true;
15227 forEach(ast.elements, function(expr) {
15228 findConstantAndWatchExpressions(expr, $filter);
15229 allConstants = allConstants && expr.constant;
15230 if (!expr.constant) {
15231 argsToWatch.push.apply(argsToWatch, expr.toWatch);
15234 ast.constant = allConstants;
15235 ast.toWatch = argsToWatch;
15237 case AST.ObjectExpression:
15238 allConstants = true;
15240 forEach(ast.properties, function(property) {
15241 findConstantAndWatchExpressions(property.value, $filter);
15242 allConstants = allConstants && property.value.constant && !property.computed;
15243 if (!property.value.constant) {
15244 argsToWatch.push.apply(argsToWatch, property.value.toWatch);
15246 if (property.computed) {
15247 findConstantAndWatchExpressions(property.key, $filter);
15248 if (!property.key.constant) {
15249 argsToWatch.push.apply(argsToWatch, property.key.toWatch);
15254 ast.constant = allConstants;
15255 ast.toWatch = argsToWatch;
15257 case AST.ThisExpression:
15258 ast.constant = false;
15261 case AST.LocalsExpression:
15262 ast.constant = false;
15268 function getInputs(body) {
15269 if (body.length !== 1) return;
15270 var lastExpression = body[0].expression;
15271 var candidate = lastExpression.toWatch;
15272 if (candidate.length !== 1) return candidate;
15273 return candidate[0] !== lastExpression ? candidate : undefined;
15276 function isAssignable(ast) {
15277 return ast.type === AST.Identifier || ast.type === AST.MemberExpression;
15280 function assignableAST(ast) {
15281 if (ast.body.length === 1 && isAssignable(ast.body[0].expression)) {
15282 return {type: AST.AssignmentExpression, left: ast.body[0].expression, right: {type: AST.NGValueParameter}, operator: '='};
15286 function isLiteral(ast) {
15287 return ast.body.length === 0 ||
15288 ast.body.length === 1 && (
15289 ast.body[0].expression.type === AST.Literal ||
15290 ast.body[0].expression.type === AST.ArrayExpression ||
15291 ast.body[0].expression.type === AST.ObjectExpression);
15294 function isConstant(ast) {
15295 return ast.constant;
15298 function ASTCompiler(astBuilder, $filter) {
15299 this.astBuilder = astBuilder;
15300 this.$filter = $filter;
15303 ASTCompiler.prototype = {
15304 compile: function(expression) {
15306 var ast = this.astBuilder.ast(expression);
15310 fn: {vars: [], body: [], own: {}},
15311 assign: {vars: [], body: [], own: {}},
15314 findConstantAndWatchExpressions(ast, self.$filter);
15317 this.stage = 'assign';
15318 if ((assignable = assignableAST(ast))) {
15319 this.state.computing = 'assign';
15320 var result = this.nextId();
15321 this.recurse(assignable, result);
15322 this.return_(result);
15323 extra = 'fn.assign=' + this.generateFunction('assign', 's,v,l');
15325 var toWatch = getInputs(ast.body);
15326 self.stage = 'inputs';
15327 forEach(toWatch, function(watch, key) {
15328 var fnKey = 'fn' + key;
15329 self.state[fnKey] = {vars: [], body: [], own: {}};
15330 self.state.computing = fnKey;
15331 var intoId = self.nextId();
15332 self.recurse(watch, intoId);
15333 self.return_(intoId);
15334 self.state.inputs.push(fnKey);
15335 watch.watchId = key;
15337 this.state.computing = 'fn';
15338 this.stage = 'main';
15341 // The build and minification steps remove the string "use strict" from the code, but this is done using a regex.
15342 // This is a workaround for this until we do a better job at only removing the prefix only when we should.
15343 '"' + this.USE + ' ' + this.STRICT + '";\n' +
15344 this.filterPrefix() +
15345 'var fn=' + this.generateFunction('fn', 's,l,a,i') +
15350 // eslint-disable-next-line no-new-func
15351 var fn = (new Function('$filter',
15360 this.state = this.stage = undefined;
15361 fn.literal = isLiteral(ast);
15362 fn.constant = isConstant(ast);
15370 watchFns: function() {
15372 var fns = this.state.inputs;
15374 forEach(fns, function(name) {
15375 result.push('var ' + name + '=' + self.generateFunction(name, 's'));
15378 result.push('fn.inputs=[' + fns.join(',') + '];');
15380 return result.join('');
15383 generateFunction: function(name, params) {
15384 return 'function(' + params + '){' +
15385 this.varsPrefix(name) +
15390 filterPrefix: function() {
15393 forEach(this.state.filters, function(id, filter) {
15394 parts.push(id + '=$filter(' + self.escape(filter) + ')');
15396 if (parts.length) return 'var ' + parts.join(',') + ';';
15400 varsPrefix: function(section) {
15401 return this.state[section].vars.length ? 'var ' + this.state[section].vars.join(',') + ';' : '';
15404 body: function(section) {
15405 return this.state[section].body.join('');
15408 recurse: function(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck) {
15409 var left, right, self = this, args, expression, computed;
15410 recursionFn = recursionFn || noop;
15411 if (!skipWatchIdCheck && isDefined(ast.watchId)) {
15412 intoId = intoId || this.nextId();
15414 this.lazyAssign(intoId, this.computedMember('i', ast.watchId)),
15415 this.lazyRecurse(ast, intoId, nameId, recursionFn, create, true)
15419 switch (ast.type) {
15421 forEach(ast.body, function(expression, pos) {
15422 self.recurse(expression.expression, undefined, undefined, function(expr) { right = expr; });
15423 if (pos !== ast.body.length - 1) {
15424 self.current().body.push(right, ';');
15426 self.return_(right);
15431 expression = this.escape(ast.value);
15432 this.assign(intoId, expression);
15433 recursionFn(intoId || expression);
15435 case AST.UnaryExpression:
15436 this.recurse(ast.argument, undefined, undefined, function(expr) { right = expr; });
15437 expression = ast.operator + '(' + this.ifDefined(right, 0) + ')';
15438 this.assign(intoId, expression);
15439 recursionFn(expression);
15441 case AST.BinaryExpression:
15442 this.recurse(ast.left, undefined, undefined, function(expr) { left = expr; });
15443 this.recurse(ast.right, undefined, undefined, function(expr) { right = expr; });
15444 if (ast.operator === '+') {
15445 expression = this.plus(left, right);
15446 } else if (ast.operator === '-') {
15447 expression = this.ifDefined(left, 0) + ast.operator + this.ifDefined(right, 0);
15449 expression = '(' + left + ')' + ast.operator + '(' + right + ')';
15451 this.assign(intoId, expression);
15452 recursionFn(expression);
15454 case AST.LogicalExpression:
15455 intoId = intoId || this.nextId();
15456 self.recurse(ast.left, intoId);
15457 self.if_(ast.operator === '&&' ? intoId : self.not(intoId), self.lazyRecurse(ast.right, intoId));
15458 recursionFn(intoId);
15460 case AST.ConditionalExpression:
15461 intoId = intoId || this.nextId();
15462 self.recurse(ast.test, intoId);
15463 self.if_(intoId, self.lazyRecurse(ast.alternate, intoId), self.lazyRecurse(ast.consequent, intoId));
15464 recursionFn(intoId);
15466 case AST.Identifier:
15467 intoId = intoId || this.nextId();
15469 nameId.context = self.stage === 'inputs' ? 's' : this.assign(this.nextId(), this.getHasOwnProperty('l', ast.name) + '?l:s');
15470 nameId.computed = false;
15471 nameId.name = ast.name;
15473 self.if_(self.stage === 'inputs' || self.not(self.getHasOwnProperty('l', ast.name)),
15475 self.if_(self.stage === 'inputs' || 's', function() {
15476 if (create && create !== 1) {
15478 self.isNull(self.nonComputedMember('s', ast.name)),
15479 self.lazyAssign(self.nonComputedMember('s', ast.name), '{}'));
15481 self.assign(intoId, self.nonComputedMember('s', ast.name));
15483 }, intoId && self.lazyAssign(intoId, self.nonComputedMember('l', ast.name))
15485 recursionFn(intoId);
15487 case AST.MemberExpression:
15488 left = nameId && (nameId.context = this.nextId()) || this.nextId();
15489 intoId = intoId || this.nextId();
15490 self.recurse(ast.object, left, undefined, function() {
15491 self.if_(self.notNull(left), function() {
15492 if (ast.computed) {
15493 right = self.nextId();
15494 self.recurse(ast.property, right);
15495 self.getStringValue(right);
15496 if (create && create !== 1) {
15497 self.if_(self.not(self.computedMember(left, right)), self.lazyAssign(self.computedMember(left, right), '{}'));
15499 expression = self.computedMember(left, right);
15500 self.assign(intoId, expression);
15502 nameId.computed = true;
15503 nameId.name = right;
15506 if (create && create !== 1) {
15507 self.if_(self.isNull(self.nonComputedMember(left, ast.property.name)), self.lazyAssign(self.nonComputedMember(left, ast.property.name), '{}'));
15509 expression = self.nonComputedMember(left, ast.property.name);
15510 self.assign(intoId, expression);
15512 nameId.computed = false;
15513 nameId.name = ast.property.name;
15517 self.assign(intoId, 'undefined');
15519 recursionFn(intoId);
15522 case AST.CallExpression:
15523 intoId = intoId || this.nextId();
15525 right = self.filter(ast.callee.name);
15527 forEach(ast.arguments, function(expr) {
15528 var argument = self.nextId();
15529 self.recurse(expr, argument);
15530 args.push(argument);
15532 expression = right + '(' + args.join(',') + ')';
15533 self.assign(intoId, expression);
15534 recursionFn(intoId);
15536 right = self.nextId();
15539 self.recurse(ast.callee, right, left, function() {
15540 self.if_(self.notNull(right), function() {
15541 forEach(ast.arguments, function(expr) {
15542 self.recurse(expr, ast.constant ? undefined : self.nextId(), undefined, function(argument) {
15543 args.push(argument);
15547 expression = self.member(left.context, left.name, left.computed) + '(' + args.join(',') + ')';
15549 expression = right + '(' + args.join(',') + ')';
15551 self.assign(intoId, expression);
15553 self.assign(intoId, 'undefined');
15555 recursionFn(intoId);
15559 case AST.AssignmentExpression:
15560 right = this.nextId();
15562 this.recurse(ast.left, undefined, left, function() {
15563 self.if_(self.notNull(left.context), function() {
15564 self.recurse(ast.right, right);
15565 expression = self.member(left.context, left.name, left.computed) + ast.operator + right;
15566 self.assign(intoId, expression);
15567 recursionFn(intoId || expression);
15571 case AST.ArrayExpression:
15573 forEach(ast.elements, function(expr) {
15574 self.recurse(expr, ast.constant ? undefined : self.nextId(), undefined, function(argument) {
15575 args.push(argument);
15578 expression = '[' + args.join(',') + ']';
15579 this.assign(intoId, expression);
15580 recursionFn(intoId || expression);
15582 case AST.ObjectExpression:
15585 forEach(ast.properties, function(property) {
15586 if (property.computed) {
15591 intoId = intoId || this.nextId();
15592 this.assign(intoId, '{}');
15593 forEach(ast.properties, function(property) {
15594 if (property.computed) {
15595 left = self.nextId();
15596 self.recurse(property.key, left);
15598 left = property.key.type === AST.Identifier ?
15599 property.key.name :
15600 ('' + property.key.value);
15602 right = self.nextId();
15603 self.recurse(property.value, right);
15604 self.assign(self.member(intoId, left, property.computed), right);
15607 forEach(ast.properties, function(property) {
15608 self.recurse(property.value, ast.constant ? undefined : self.nextId(), undefined, function(expr) {
15609 args.push(self.escape(
15610 property.key.type === AST.Identifier ? property.key.name :
15611 ('' + property.key.value)) +
15615 expression = '{' + args.join(',') + '}';
15616 this.assign(intoId, expression);
15618 recursionFn(intoId || expression);
15620 case AST.ThisExpression:
15621 this.assign(intoId, 's');
15622 recursionFn(intoId || 's');
15624 case AST.LocalsExpression:
15625 this.assign(intoId, 'l');
15626 recursionFn(intoId || 'l');
15628 case AST.NGValueParameter:
15629 this.assign(intoId, 'v');
15630 recursionFn(intoId || 'v');
15635 getHasOwnProperty: function(element, property) {
15636 var key = element + '.' + property;
15637 var own = this.current().own;
15638 if (!own.hasOwnProperty(key)) {
15639 own[key] = this.nextId(false, element + '&&(' + this.escape(property) + ' in ' + element + ')');
15644 assign: function(id, value) {
15646 this.current().body.push(id, '=', value, ';');
15650 filter: function(filterName) {
15651 if (!this.state.filters.hasOwnProperty(filterName)) {
15652 this.state.filters[filterName] = this.nextId(true);
15654 return this.state.filters[filterName];
15657 ifDefined: function(id, defaultValue) {
15658 return 'ifDefined(' + id + ',' + this.escape(defaultValue) + ')';
15661 plus: function(left, right) {
15662 return 'plus(' + left + ',' + right + ')';
15665 return_: function(id) {
15666 this.current().body.push('return ', id, ';');
15669 if_: function(test, alternate, consequent) {
15670 if (test === true) {
15673 var body = this.current().body;
15674 body.push('if(', test, '){');
15678 body.push('else{');
15685 not: function(expression) {
15686 return '!(' + expression + ')';
15689 isNull: function(expression) {
15690 return expression + '==null';
15693 notNull: function(expression) {
15694 return expression + '!=null';
15697 nonComputedMember: function(left, right) {
15698 var SAFE_IDENTIFIER = /^[$_a-zA-Z][$_a-zA-Z0-9]*$/;
15699 var UNSAFE_CHARACTERS = /[^$_a-zA-Z0-9]/g;
15700 if (SAFE_IDENTIFIER.test(right)) {
15701 return left + '.' + right;
15703 return left + '["' + right.replace(UNSAFE_CHARACTERS, this.stringEscapeFn) + '"]';
15707 computedMember: function(left, right) {
15708 return left + '[' + right + ']';
15711 member: function(left, right, computed) {
15712 if (computed) return this.computedMember(left, right);
15713 return this.nonComputedMember(left, right);
15716 getStringValue: function(item) {
15717 this.assign(item, 'getStringValue(' + item + ')');
15720 lazyRecurse: function(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck) {
15722 return function() {
15723 self.recurse(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck);
15727 lazyAssign: function(id, value) {
15729 return function() {
15730 self.assign(id, value);
15734 stringEscapeRegex: /[^ a-zA-Z0-9]/g,
15736 stringEscapeFn: function(c) {
15737 return '\\u' + ('0000' + c.charCodeAt(0).toString(16)).slice(-4);
15740 escape: function(value) {
15741 if (isString(value)) return '\'' + value.replace(this.stringEscapeRegex, this.stringEscapeFn) + '\'';
15742 if (isNumber(value)) return value.toString();
15743 if (value === true) return 'true';
15744 if (value === false) return 'false';
15745 if (value === null) return 'null';
15746 if (typeof value === 'undefined') return 'undefined';
15748 throw $parseMinErr('esc', 'IMPOSSIBLE');
15751 nextId: function(skip, init) {
15752 var id = 'v' + (this.state.nextId++);
15754 this.current().vars.push(id + (init ? '=' + init : ''));
15759 current: function() {
15760 return this.state[this.state.computing];
15765 function ASTInterpreter(astBuilder, $filter) {
15766 this.astBuilder = astBuilder;
15767 this.$filter = $filter;
15770 ASTInterpreter.prototype = {
15771 compile: function(expression) {
15773 var ast = this.astBuilder.ast(expression);
15774 findConstantAndWatchExpressions(ast, self.$filter);
15777 if ((assignable = assignableAST(ast))) {
15778 assign = this.recurse(assignable);
15780 var toWatch = getInputs(ast.body);
15784 forEach(toWatch, function(watch, key) {
15785 var input = self.recurse(watch);
15786 watch.input = input;
15787 inputs.push(input);
15788 watch.watchId = key;
15791 var expressions = [];
15792 forEach(ast.body, function(expression) {
15793 expressions.push(self.recurse(expression.expression));
15795 var fn = ast.body.length === 0 ? noop :
15796 ast.body.length === 1 ? expressions[0] :
15797 function(scope, locals) {
15799 forEach(expressions, function(exp) {
15800 lastValue = exp(scope, locals);
15805 fn.assign = function(scope, value, locals) {
15806 return assign(scope, locals, value);
15810 fn.inputs = inputs;
15812 fn.literal = isLiteral(ast);
15813 fn.constant = isConstant(ast);
15817 recurse: function(ast, context, create) {
15818 var left, right, self = this, args;
15820 return this.inputs(ast.input, ast.watchId);
15822 switch (ast.type) {
15824 return this.value(ast.value, context);
15825 case AST.UnaryExpression:
15826 right = this.recurse(ast.argument);
15827 return this['unary' + ast.operator](right, context);
15828 case AST.BinaryExpression:
15829 left = this.recurse(ast.left);
15830 right = this.recurse(ast.right);
15831 return this['binary' + ast.operator](left, right, context);
15832 case AST.LogicalExpression:
15833 left = this.recurse(ast.left);
15834 right = this.recurse(ast.right);
15835 return this['binary' + ast.operator](left, right, context);
15836 case AST.ConditionalExpression:
15837 return this['ternary?:'](
15838 this.recurse(ast.test),
15839 this.recurse(ast.alternate),
15840 this.recurse(ast.consequent),
15843 case AST.Identifier:
15844 return self.identifier(ast.name, context, create);
15845 case AST.MemberExpression:
15846 left = this.recurse(ast.object, false, !!create);
15847 if (!ast.computed) {
15848 right = ast.property.name;
15850 if (ast.computed) right = this.recurse(ast.property);
15851 return ast.computed ?
15852 this.computedMember(left, right, context, create) :
15853 this.nonComputedMember(left, right, context, create);
15854 case AST.CallExpression:
15856 forEach(ast.arguments, function(expr) {
15857 args.push(self.recurse(expr));
15859 if (ast.filter) right = this.$filter(ast.callee.name);
15860 if (!ast.filter) right = this.recurse(ast.callee, true);
15861 return ast.filter ?
15862 function(scope, locals, assign, inputs) {
15864 for (var i = 0; i < args.length; ++i) {
15865 values.push(args[i](scope, locals, assign, inputs));
15867 var value = right.apply(undefined, values, inputs);
15868 return context ? {context: undefined, name: undefined, value: value} : value;
15870 function(scope, locals, assign, inputs) {
15871 var rhs = right(scope, locals, assign, inputs);
15873 if (rhs.value != null) {
15875 for (var i = 0; i < args.length; ++i) {
15876 values.push(args[i](scope, locals, assign, inputs));
15878 value = rhs.value.apply(rhs.context, values);
15880 return context ? {value: value} : value;
15882 case AST.AssignmentExpression:
15883 left = this.recurse(ast.left, true, 1);
15884 right = this.recurse(ast.right);
15885 return function(scope, locals, assign, inputs) {
15886 var lhs = left(scope, locals, assign, inputs);
15887 var rhs = right(scope, locals, assign, inputs);
15888 lhs.context[lhs.name] = rhs;
15889 return context ? {value: rhs} : rhs;
15891 case AST.ArrayExpression:
15893 forEach(ast.elements, function(expr) {
15894 args.push(self.recurse(expr));
15896 return function(scope, locals, assign, inputs) {
15898 for (var i = 0; i < args.length; ++i) {
15899 value.push(args[i](scope, locals, assign, inputs));
15901 return context ? {value: value} : value;
15903 case AST.ObjectExpression:
15905 forEach(ast.properties, function(property) {
15906 if (property.computed) {
15907 args.push({key: self.recurse(property.key),
15909 value: self.recurse(property.value)
15912 args.push({key: property.key.type === AST.Identifier ?
15913 property.key.name :
15914 ('' + property.key.value),
15916 value: self.recurse(property.value)
15920 return function(scope, locals, assign, inputs) {
15922 for (var i = 0; i < args.length; ++i) {
15923 if (args[i].computed) {
15924 value[args[i].key(scope, locals, assign, inputs)] = args[i].value(scope, locals, assign, inputs);
15926 value[args[i].key] = args[i].value(scope, locals, assign, inputs);
15929 return context ? {value: value} : value;
15931 case AST.ThisExpression:
15932 return function(scope) {
15933 return context ? {value: scope} : scope;
15935 case AST.LocalsExpression:
15936 return function(scope, locals) {
15937 return context ? {value: locals} : locals;
15939 case AST.NGValueParameter:
15940 return function(scope, locals, assign) {
15941 return context ? {value: assign} : assign;
15946 'unary+': function(argument, context) {
15947 return function(scope, locals, assign, inputs) {
15948 var arg = argument(scope, locals, assign, inputs);
15949 if (isDefined(arg)) {
15954 return context ? {value: arg} : arg;
15957 'unary-': function(argument, context) {
15958 return function(scope, locals, assign, inputs) {
15959 var arg = argument(scope, locals, assign, inputs);
15960 if (isDefined(arg)) {
15965 return context ? {value: arg} : arg;
15968 'unary!': function(argument, context) {
15969 return function(scope, locals, assign, inputs) {
15970 var arg = !argument(scope, locals, assign, inputs);
15971 return context ? {value: arg} : arg;
15974 'binary+': function(left, right, context) {
15975 return function(scope, locals, assign, inputs) {
15976 var lhs = left(scope, locals, assign, inputs);
15977 var rhs = right(scope, locals, assign, inputs);
15978 var arg = plusFn(lhs, rhs);
15979 return context ? {value: arg} : arg;
15982 'binary-': function(left, right, context) {
15983 return function(scope, locals, assign, inputs) {
15984 var lhs = left(scope, locals, assign, inputs);
15985 var rhs = right(scope, locals, assign, inputs);
15986 var arg = (isDefined(lhs) ? lhs : 0) - (isDefined(rhs) ? rhs : 0);
15987 return context ? {value: arg} : arg;
15990 'binary*': function(left, right, context) {
15991 return function(scope, locals, assign, inputs) {
15992 var arg = left(scope, locals, assign, inputs) * right(scope, locals, assign, inputs);
15993 return context ? {value: arg} : arg;
15996 'binary/': function(left, right, context) {
15997 return function(scope, locals, assign, inputs) {
15998 var arg = left(scope, locals, assign, inputs) / right(scope, locals, assign, inputs);
15999 return context ? {value: arg} : arg;
16002 'binary%': function(left, right, context) {
16003 return function(scope, locals, assign, inputs) {
16004 var arg = left(scope, locals, assign, inputs) % right(scope, locals, assign, inputs);
16005 return context ? {value: arg} : arg;
16008 'binary===': function(left, right, context) {
16009 return function(scope, locals, assign, inputs) {
16010 var arg = left(scope, locals, assign, inputs) === right(scope, locals, assign, inputs);
16011 return context ? {value: arg} : arg;
16014 'binary!==': function(left, right, context) {
16015 return function(scope, locals, assign, inputs) {
16016 var arg = left(scope, locals, assign, inputs) !== right(scope, locals, assign, inputs);
16017 return context ? {value: arg} : arg;
16020 'binary==': function(left, right, context) {
16021 return function(scope, locals, assign, inputs) {
16022 // eslint-disable-next-line eqeqeq
16023 var arg = left(scope, locals, assign, inputs) == right(scope, locals, assign, inputs);
16024 return context ? {value: arg} : arg;
16027 'binary!=': function(left, right, context) {
16028 return function(scope, locals, assign, inputs) {
16029 // eslint-disable-next-line eqeqeq
16030 var arg = left(scope, locals, assign, inputs) != right(scope, locals, assign, inputs);
16031 return context ? {value: arg} : arg;
16034 'binary<': function(left, right, context) {
16035 return function(scope, locals, assign, inputs) {
16036 var arg = left(scope, locals, assign, inputs) < right(scope, locals, assign, inputs);
16037 return context ? {value: arg} : arg;
16040 'binary>': function(left, right, context) {
16041 return function(scope, locals, assign, inputs) {
16042 var arg = left(scope, locals, assign, inputs) > right(scope, locals, assign, inputs);
16043 return context ? {value: arg} : arg;
16046 'binary<=': function(left, right, context) {
16047 return function(scope, locals, assign, inputs) {
16048 var arg = left(scope, locals, assign, inputs) <= right(scope, locals, assign, inputs);
16049 return context ? {value: arg} : arg;
16052 'binary>=': function(left, right, context) {
16053 return function(scope, locals, assign, inputs) {
16054 var arg = left(scope, locals, assign, inputs) >= right(scope, locals, assign, inputs);
16055 return context ? {value: arg} : arg;
16058 'binary&&': function(left, right, context) {
16059 return function(scope, locals, assign, inputs) {
16060 var arg = left(scope, locals, assign, inputs) && right(scope, locals, assign, inputs);
16061 return context ? {value: arg} : arg;
16064 'binary||': function(left, right, context) {
16065 return function(scope, locals, assign, inputs) {
16066 var arg = left(scope, locals, assign, inputs) || right(scope, locals, assign, inputs);
16067 return context ? {value: arg} : arg;
16070 'ternary?:': function(test, alternate, consequent, context) {
16071 return function(scope, locals, assign, inputs) {
16072 var arg = test(scope, locals, assign, inputs) ? alternate(scope, locals, assign, inputs) : consequent(scope, locals, assign, inputs);
16073 return context ? {value: arg} : arg;
16076 value: function(value, context) {
16077 return function() { return context ? {context: undefined, name: undefined, value: value} : value; };
16079 identifier: function(name, context, create) {
16080 return function(scope, locals, assign, inputs) {
16081 var base = locals && (name in locals) ? locals : scope;
16082 if (create && create !== 1 && base && base[name] == null) {
16085 var value = base ? base[name] : undefined;
16087 return {context: base, name: name, value: value};
16093 computedMember: function(left, right, context, create) {
16094 return function(scope, locals, assign, inputs) {
16095 var lhs = left(scope, locals, assign, inputs);
16099 rhs = right(scope, locals, assign, inputs);
16100 rhs = getStringValue(rhs);
16101 if (create && create !== 1) {
16102 if (lhs && !(lhs[rhs])) {
16109 return {context: lhs, name: rhs, value: value};
16115 nonComputedMember: function(left, right, context, create) {
16116 return function(scope, locals, assign, inputs) {
16117 var lhs = left(scope, locals, assign, inputs);
16118 if (create && create !== 1) {
16119 if (lhs && lhs[right] == null) {
16123 var value = lhs != null ? lhs[right] : undefined;
16125 return {context: lhs, name: right, value: value};
16131 inputs: function(input, watchId) {
16132 return function(scope, value, locals, inputs) {
16133 if (inputs) return inputs[watchId];
16134 return input(scope, value, locals);
16142 var Parser = function Parser(lexer, $filter, options) {
16143 this.lexer = lexer;
16144 this.$filter = $filter;
16145 this.options = options;
16146 this.ast = new AST(lexer, options);
16147 this.astCompiler = options.csp ? new ASTInterpreter(this.ast, $filter) :
16148 new ASTCompiler(this.ast, $filter);
16151 Parser.prototype = {
16152 constructor: Parser,
16154 parse: function(text) {
16155 return this.astCompiler.compile(text);
16159 function getValueOf(value) {
16160 return isFunction(value.valueOf) ? value.valueOf() : objectValueOf.call(value);
16163 ///////////////////////////////////
16172 * Converts Angular {@link guide/expression expression} into a function.
16175 * var getter = $parse('user.name');
16176 * var setter = getter.assign;
16177 * var context = {user:{name:'angular'}};
16178 * var locals = {user:{name:'local'}};
16180 * expect(getter(context)).toEqual('angular');
16181 * setter(context, 'newValue');
16182 * expect(context.user.name).toEqual('newValue');
16183 * expect(getter(context, locals)).toEqual('local');
16187 * @param {string} expression String expression to compile.
16188 * @returns {function(context, locals)} a function which represents the compiled expression:
16190 * * `context` – `{object}` – an object against which any expressions embedded in the strings
16191 * are evaluated against (typically a scope object).
16192 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
16195 * The returned function also has the following properties:
16196 * * `literal` – `{boolean}` – whether the expression's top-level node is a JavaScript
16198 * * `constant` – `{boolean}` – whether the expression is made entirely of JavaScript
16199 * constant literals.
16200 * * `assign` – `{?function(context, value)}` – if the expression is assignable, this will be
16201 * set to a function to change its value on the given context.
16208 * @name $parseProvider
16212 * `$parseProvider` can be used for configuring the default behavior of the {@link ng.$parse $parse}
16215 function $ParseProvider() {
16216 var cache = createMap();
16221 'undefined': undefined
16223 var identStart, identContinue;
16227 * @name $parseProvider#addLiteral
16230 * Configure $parse service to add literal values that will be present as literal at expressions.
16232 * @param {string} literalName Token for the literal value. The literal name value must be a valid literal name.
16233 * @param {*} literalValue Value for this literal. All literal values must be primitives or `undefined`.
16236 this.addLiteral = function(literalName, literalValue) {
16237 literals[literalName] = literalValue;
16242 * @name $parseProvider#setIdentifierFns
16246 * Allows defining the set of characters that are allowed in Angular expressions. The function
16247 * `identifierStart` will get called to know if a given character is a valid character to be the
16248 * first character for an identifier. The function `identifierContinue` will get called to know if
16249 * a given character is a valid character to be a follow-up identifier character. The functions
16250 * `identifierStart` and `identifierContinue` will receive as arguments the single character to be
16251 * identifier and the character code point. These arguments will be `string` and `numeric`. Keep in
16252 * mind that the `string` parameter can be two characters long depending on the character
16253 * representation. It is expected for the function to return `true` or `false`, whether that
16254 * character is allowed or not.
16256 * Since this function will be called extensively, keep the implementation of these functions fast,
16257 * as the performance of these functions have a direct impact on the expressions parsing speed.
16259 * @param {function=} identifierStart The function that will decide whether the given character is
16260 * a valid identifier start character.
16261 * @param {function=} identifierContinue The function that will decide whether the given character is
16262 * a valid identifier continue character.
16264 this.setIdentifierFns = function(identifierStart, identifierContinue) {
16265 identStart = identifierStart;
16266 identContinue = identifierContinue;
16270 this.$get = ['$filter', function($filter) {
16271 var noUnsafeEval = csp().noUnsafeEval;
16272 var $parseOptions = {
16274 literals: copy(literals),
16275 isIdentifierStart: isFunction(identStart) && identStart,
16276 isIdentifierContinue: isFunction(identContinue) && identContinue
16280 function $parse(exp, interceptorFn) {
16281 var parsedExpression, oneTime, cacheKey;
16283 switch (typeof exp) {
16288 parsedExpression = cache[cacheKey];
16290 if (!parsedExpression) {
16291 if (exp.charAt(0) === ':' && exp.charAt(1) === ':') {
16293 exp = exp.substring(2);
16295 var lexer = new Lexer($parseOptions);
16296 var parser = new Parser(lexer, $filter, $parseOptions);
16297 parsedExpression = parser.parse(exp);
16298 if (parsedExpression.constant) {
16299 parsedExpression.$$watchDelegate = constantWatchDelegate;
16300 } else if (oneTime) {
16301 parsedExpression.$$watchDelegate = parsedExpression.literal ?
16302 oneTimeLiteralWatchDelegate : oneTimeWatchDelegate;
16303 } else if (parsedExpression.inputs) {
16304 parsedExpression.$$watchDelegate = inputsWatchDelegate;
16306 cache[cacheKey] = parsedExpression;
16308 return addInterceptor(parsedExpression, interceptorFn);
16311 return addInterceptor(exp, interceptorFn);
16314 return addInterceptor(noop, interceptorFn);
16318 function expressionInputDirtyCheck(newValue, oldValueOfValue, compareObjectIdentity) {
16320 if (newValue == null || oldValueOfValue == null) { // null/undefined
16321 return newValue === oldValueOfValue;
16324 if (typeof newValue === 'object' && !compareObjectIdentity) {
16326 // attempt to convert the value to a primitive type
16327 // TODO(docs): add a note to docs that by implementing valueOf even objects and arrays can
16328 // be cheaply dirty-checked
16329 newValue = getValueOf(newValue);
16331 if (typeof newValue === 'object') {
16332 // objects/arrays are not supported - deep-watching them would be too expensive
16336 // fall-through to the primitive equality check
16340 // eslint-disable-next-line no-self-compare
16341 return newValue === oldValueOfValue || (newValue !== newValue && oldValueOfValue !== oldValueOfValue);
16344 function inputsWatchDelegate(scope, listener, objectEquality, parsedExpression, prettyPrintExpression) {
16345 var inputExpressions = parsedExpression.inputs;
16348 if (inputExpressions.length === 1) {
16349 var oldInputValueOf = expressionInputDirtyCheck; // init to something unique so that equals check fails
16350 inputExpressions = inputExpressions[0];
16351 return scope.$watch(function expressionInputWatch(scope) {
16352 var newInputValue = inputExpressions(scope);
16353 if (!expressionInputDirtyCheck(newInputValue, oldInputValueOf, parsedExpression.literal)) {
16354 lastResult = parsedExpression(scope, undefined, undefined, [newInputValue]);
16355 oldInputValueOf = newInputValue && getValueOf(newInputValue);
16358 }, listener, objectEquality, prettyPrintExpression);
16361 var oldInputValueOfValues = [];
16362 var oldInputValues = [];
16363 for (var i = 0, ii = inputExpressions.length; i < ii; i++) {
16364 oldInputValueOfValues[i] = expressionInputDirtyCheck; // init to something unique so that equals check fails
16365 oldInputValues[i] = null;
16368 return scope.$watch(function expressionInputsWatch(scope) {
16369 var changed = false;
16371 for (var i = 0, ii = inputExpressions.length; i < ii; i++) {
16372 var newInputValue = inputExpressions[i](scope);
16373 if (changed || (changed = !expressionInputDirtyCheck(newInputValue, oldInputValueOfValues[i], parsedExpression.literal))) {
16374 oldInputValues[i] = newInputValue;
16375 oldInputValueOfValues[i] = newInputValue && getValueOf(newInputValue);
16380 lastResult = parsedExpression(scope, undefined, undefined, oldInputValues);
16384 }, listener, objectEquality, prettyPrintExpression);
16387 function oneTimeWatchDelegate(scope, listener, objectEquality, parsedExpression, prettyPrintExpression) {
16388 var unwatch, lastValue;
16389 if (parsedExpression.inputs) {
16390 unwatch = inputsWatchDelegate(scope, oneTimeListener, objectEquality, parsedExpression, prettyPrintExpression);
16392 unwatch = scope.$watch(oneTimeWatch, oneTimeListener, objectEquality);
16396 function oneTimeWatch(scope) {
16397 return parsedExpression(scope);
16399 function oneTimeListener(value, old, scope) {
16401 if (isFunction(listener)) {
16402 listener(value, old, scope);
16404 if (isDefined(value)) {
16405 scope.$$postDigest(function() {
16406 if (isDefined(lastValue)) {
16414 function oneTimeLiteralWatchDelegate(scope, listener, objectEquality, parsedExpression) {
16415 var unwatch, lastValue;
16416 unwatch = scope.$watch(function oneTimeWatch(scope) {
16417 return parsedExpression(scope);
16418 }, function oneTimeListener(value, old, scope) {
16420 if (isFunction(listener)) {
16421 listener(value, old, scope);
16423 if (isAllDefined(value)) {
16424 scope.$$postDigest(function() {
16425 if (isAllDefined(lastValue)) unwatch();
16428 }, objectEquality);
16432 function isAllDefined(value) {
16433 var allDefined = true;
16434 forEach(value, function(val) {
16435 if (!isDefined(val)) allDefined = false;
16441 function constantWatchDelegate(scope, listener, objectEquality, parsedExpression) {
16442 var unwatch = scope.$watch(function constantWatch(scope) {
16444 return parsedExpression(scope);
16445 }, listener, objectEquality);
16449 function addInterceptor(parsedExpression, interceptorFn) {
16450 if (!interceptorFn) return parsedExpression;
16451 var watchDelegate = parsedExpression.$$watchDelegate;
16452 var useInputs = false;
16455 watchDelegate !== oneTimeLiteralWatchDelegate &&
16456 watchDelegate !== oneTimeWatchDelegate;
16458 var fn = regularWatch ? function regularInterceptedExpression(scope, locals, assign, inputs) {
16459 var value = useInputs && inputs ? inputs[0] : parsedExpression(scope, locals, assign, inputs);
16460 return interceptorFn(value, scope, locals);
16461 } : function oneTimeInterceptedExpression(scope, locals, assign, inputs) {
16462 var value = parsedExpression(scope, locals, assign, inputs);
16463 var result = interceptorFn(value, scope, locals);
16464 // we only return the interceptor's result if the
16465 // initial value is defined (for bind-once)
16466 return isDefined(value) ? result : value;
16469 // Propagate $$watchDelegates other then inputsWatchDelegate
16470 useInputs = !parsedExpression.inputs;
16471 if (parsedExpression.$$watchDelegate &&
16472 parsedExpression.$$watchDelegate !== inputsWatchDelegate) {
16473 fn.$$watchDelegate = parsedExpression.$$watchDelegate;
16474 fn.inputs = parsedExpression.inputs;
16475 } else if (!interceptorFn.$stateful) {
16476 // If there is an interceptor, but no watchDelegate then treat the interceptor like
16477 // we treat filters - it is assumed to be a pure function unless flagged with $stateful
16478 fn.$$watchDelegate = inputsWatchDelegate;
16479 fn.inputs = parsedExpression.inputs ? parsedExpression.inputs : [parsedExpression];
16490 * @requires $rootScope
16493 * A service that helps you run functions asynchronously, and use their return values (or exceptions)
16494 * when they are done processing.
16496 * This is a [Promises/A+](https://promisesaplus.com/)-compliant implementation of promises/deferred
16497 * objects inspired by [Kris Kowal's Q](https://github.com/kriskowal/q).
16499 * $q can be used in two fashions --- one which is more similar to Kris Kowal's Q or jQuery's Deferred
16500 * implementations, and the other which resembles ES6 (ES2015) promises to some degree.
16504 * The streamlined ES6 style promise is essentially just using $q as a constructor which takes a `resolver`
16505 * function as the first argument. This is similar to the native Promise implementation from ES6,
16506 * see [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise).
16508 * While the constructor-style use is supported, not all of the supporting methods from ES6 promises are
16511 * It can be used like so:
16514 * // for the purpose of this example let's assume that variables `$q` and `okToGreet`
16515 * // are available in the current lexical scope (they could have been injected or passed in).
16517 * function asyncGreet(name) {
16518 * // perform some asynchronous operation, resolve or reject the promise when appropriate.
16519 * return $q(function(resolve, reject) {
16520 * setTimeout(function() {
16521 * if (okToGreet(name)) {
16522 * resolve('Hello, ' + name + '!');
16524 * reject('Greeting ' + name + ' is not allowed.');
16530 * var promise = asyncGreet('Robin Hood');
16531 * promise.then(function(greeting) {
16532 * alert('Success: ' + greeting);
16533 * }, function(reason) {
16534 * alert('Failed: ' + reason);
16538 * Note: progress/notify callbacks are not currently supported via the ES6-style interface.
16540 * Note: unlike ES6 behavior, an exception thrown in the constructor function will NOT implicitly reject the promise.
16542 * However, the more traditional CommonJS-style usage is still available, and documented below.
16544 * [The CommonJS Promise proposal](http://wiki.commonjs.org/wiki/Promises) describes a promise as an
16545 * interface for interacting with an object that represents the result of an action that is
16546 * performed asynchronously, and may or may not be finished at any given point in time.
16548 * From the perspective of dealing with error handling, deferred and promise APIs are to
16549 * asynchronous programming what `try`, `catch` and `throw` keywords are to synchronous programming.
16552 * // for the purpose of this example let's assume that variables `$q` and `okToGreet`
16553 * // are available in the current lexical scope (they could have been injected or passed in).
16555 * function asyncGreet(name) {
16556 * var deferred = $q.defer();
16558 * setTimeout(function() {
16559 * deferred.notify('About to greet ' + name + '.');
16561 * if (okToGreet(name)) {
16562 * deferred.resolve('Hello, ' + name + '!');
16564 * deferred.reject('Greeting ' + name + ' is not allowed.');
16568 * return deferred.promise;
16571 * var promise = asyncGreet('Robin Hood');
16572 * promise.then(function(greeting) {
16573 * alert('Success: ' + greeting);
16574 * }, function(reason) {
16575 * alert('Failed: ' + reason);
16576 * }, function(update) {
16577 * alert('Got notification: ' + update);
16581 * At first it might not be obvious why this extra complexity is worth the trouble. The payoff
16582 * comes in the way of guarantees that promise and deferred APIs make, see
16583 * https://github.com/kriskowal/uncommonjs/blob/master/promises/specification.md.
16585 * Additionally the promise api allows for composition that is very hard to do with the
16586 * traditional callback ([CPS](http://en.wikipedia.org/wiki/Continuation-passing_style)) approach.
16587 * For more on this please see the [Q documentation](https://github.com/kriskowal/q) especially the
16588 * section on serial or parallel joining of promises.
16590 * # The Deferred API
16592 * A new instance of deferred is constructed by calling `$q.defer()`.
16594 * The purpose of the deferred object is to expose the associated Promise instance as well as APIs
16595 * that can be used for signaling the successful or unsuccessful completion, as well as the status
16600 * - `resolve(value)` – resolves the derived promise with the `value`. If the value is a rejection
16601 * constructed via `$q.reject`, the promise will be rejected instead.
16602 * - `reject(reason)` – rejects the derived promise with the `reason`. This is equivalent to
16603 * resolving it with a rejection constructed via `$q.reject`.
16604 * - `notify(value)` - provides updates on the status of the promise's execution. This may be called
16605 * multiple times before the promise is either resolved or rejected.
16609 * - promise – `{Promise}` – promise object associated with this deferred.
16612 * # The Promise API
16614 * A new promise instance is created when a deferred instance is created and can be retrieved by
16615 * calling `deferred.promise`.
16617 * The purpose of the promise object is to allow for interested parties to get access to the result
16618 * of the deferred task when it completes.
16622 * - `then(successCallback, [errorCallback], [notifyCallback])` – regardless of when the promise was or
16623 * will be resolved or rejected, `then` calls one of the success or error callbacks asynchronously
16624 * as soon as the result is available. The callbacks are called with a single argument: the result
16625 * or rejection reason. Additionally, the notify callback may be called zero or more times to
16626 * provide a progress indication, before the promise is resolved or rejected.
16628 * This method *returns a new promise* which is resolved or rejected via the return value of the
16629 * `successCallback`, `errorCallback` (unless that value is a promise, in which case it is resolved
16630 * with the value which is resolved in that promise using
16631 * [promise chaining](http://www.html5rocks.com/en/tutorials/es6/promises/#toc-promises-queues)).
16632 * It also notifies via the return value of the `notifyCallback` method. The promise cannot be
16633 * resolved or rejected from the notifyCallback method. The errorCallback and notifyCallback
16634 * arguments are optional.
16636 * - `catch(errorCallback)` – shorthand for `promise.then(null, errorCallback)`
16638 * - `finally(callback, notifyCallback)` – allows you to observe either the fulfillment or rejection of a promise,
16639 * but to do so without modifying the final value. This is useful to release resources or do some
16640 * clean-up that needs to be done whether the promise was rejected or resolved. See the [full
16641 * specification](https://github.com/kriskowal/q/wiki/API-Reference#promisefinallycallback) for
16642 * more information.
16644 * # Chaining promises
16646 * Because calling the `then` method of a promise returns a new derived promise, it is easily
16647 * possible to create a chain of promises:
16650 * promiseB = promiseA.then(function(result) {
16651 * return result + 1;
16654 * // promiseB will be resolved immediately after promiseA is resolved and its value
16655 * // will be the result of promiseA incremented by 1
16658 * It is possible to create chains of any length and since a promise can be resolved with another
16659 * promise (which will defer its resolution further), it is possible to pause/defer resolution of
16660 * the promises at any point in the chain. This makes it possible to implement powerful APIs like
16661 * $http's response interceptors.
16664 * # Differences between Kris Kowal's Q and $q
16666 * There are two main differences:
16668 * - $q is integrated with the {@link ng.$rootScope.Scope} Scope model observation
16669 * mechanism in angular, which means faster propagation of resolution or rejection into your
16670 * models and avoiding unnecessary browser repaints, which would result in flickering UI.
16671 * - Q has many more features than $q, but that comes at a cost of bytes. $q is tiny, but contains
16672 * all the important functionality needed for common async tasks.
16677 * it('should simulate promise', inject(function($q, $rootScope) {
16678 * var deferred = $q.defer();
16679 * var promise = deferred.promise;
16680 * var resolvedValue;
16682 * promise.then(function(value) { resolvedValue = value; });
16683 * expect(resolvedValue).toBeUndefined();
16685 * // Simulate resolving of promise
16686 * deferred.resolve(123);
16687 * // Note that the 'then' function does not get called synchronously.
16688 * // This is because we want the promise API to always be async, whether or not
16689 * // it got called synchronously or asynchronously.
16690 * expect(resolvedValue).toBeUndefined();
16692 * // Propagate promise resolution to 'then' functions using $apply().
16693 * $rootScope.$apply();
16694 * expect(resolvedValue).toEqual(123);
16698 * @param {function(function, function)} resolver Function which is responsible for resolving or
16699 * rejecting the newly created promise. The first parameter is a function which resolves the
16700 * promise, the second parameter is a function which rejects the promise.
16702 * @returns {Promise} The newly created promise.
16711 function $QProvider() {
16712 var errorOnUnhandledRejections = true;
16713 this.$get = ['$rootScope', '$exceptionHandler', function($rootScope, $exceptionHandler) {
16714 return qFactory(function(callback) {
16715 $rootScope.$evalAsync(callback);
16716 }, $exceptionHandler, errorOnUnhandledRejections);
16721 * @name $qProvider#errorOnUnhandledRejections
16725 * Retrieves or overrides whether to generate an error when a rejected promise is not handled.
16726 * This feature is enabled by default.
16728 * @param {boolean=} value Whether to generate an error when a rejected promise is not handled.
16729 * @returns {boolean|ng.$qProvider} Current value when called without a new value or self for
16730 * chaining otherwise.
16732 this.errorOnUnhandledRejections = function(value) {
16733 if (isDefined(value)) {
16734 errorOnUnhandledRejections = value;
16737 return errorOnUnhandledRejections;
16743 function $$QProvider() {
16744 var errorOnUnhandledRejections = true;
16745 this.$get = ['$browser', '$exceptionHandler', function($browser, $exceptionHandler) {
16746 return qFactory(function(callback) {
16747 $browser.defer(callback);
16748 }, $exceptionHandler, errorOnUnhandledRejections);
16751 this.errorOnUnhandledRejections = function(value) {
16752 if (isDefined(value)) {
16753 errorOnUnhandledRejections = value;
16756 return errorOnUnhandledRejections;
16762 * Constructs a promise manager.
16764 * @param {function(function)} nextTick Function for executing functions in the next turn.
16765 * @param {function(...*)} exceptionHandler Function into which unexpected exceptions are passed for
16766 * debugging purposes.
16767 @ param {=boolean} errorOnUnhandledRejections Whether an error should be generated on unhandled
16768 * promises rejections.
16769 * @returns {object} Promise manager.
16771 function qFactory(nextTick, exceptionHandler, errorOnUnhandledRejections) {
16772 var $qMinErr = minErr('$q', TypeError);
16774 var checkQueue = [];
16778 * @name ng.$q#defer
16782 * Creates a `Deferred` object which represents a task which will finish in the future.
16784 * @returns {Deferred} Returns a new instance of deferred.
16787 return new Deferred();
16790 function Deferred() {
16791 var promise = this.promise = new Promise();
16792 //Non prototype methods necessary to support unbound execution :/
16793 this.resolve = function(val) { resolvePromise(promise, val); };
16794 this.reject = function(reason) { rejectPromise(promise, reason); };
16795 this.notify = function(progress) { notifyPromise(promise, progress); };
16799 function Promise() {
16800 this.$$state = { status: 0 };
16803 extend(Promise.prototype, {
16804 then: function(onFulfilled, onRejected, progressBack) {
16805 if (isUndefined(onFulfilled) && isUndefined(onRejected) && isUndefined(progressBack)) {
16808 var result = new Promise();
16810 this.$$state.pending = this.$$state.pending || [];
16811 this.$$state.pending.push([result, onFulfilled, onRejected, progressBack]);
16812 if (this.$$state.status > 0) scheduleProcessQueue(this.$$state);
16817 'catch': function(callback) {
16818 return this.then(null, callback);
16821 'finally': function(callback, progressBack) {
16822 return this.then(function(value) {
16823 return handleCallback(value, resolve, callback);
16824 }, function(error) {
16825 return handleCallback(error, reject, callback);
16830 function processQueue(state) {
16831 var fn, promise, pending;
16833 pending = state.pending;
16834 state.processScheduled = false;
16835 state.pending = undefined;
16837 for (var i = 0, ii = pending.length; i < ii; ++i) {
16839 promise = pending[i][0];
16840 fn = pending[i][state.status];
16842 if (isFunction(fn)) {
16843 resolvePromise(promise, fn(state.value));
16844 } else if (state.status === 1) {
16845 resolvePromise(promise, state.value);
16847 rejectPromise(promise, state.value);
16850 rejectPromise(promise, e);
16855 if (errorOnUnhandledRejections && queueSize === 0) {
16856 nextTick(processChecks);
16861 function processChecks() {
16862 // eslint-disable-next-line no-unmodified-loop-condition
16863 while (!queueSize && checkQueue.length) {
16864 var toCheck = checkQueue.shift();
16865 if (!toCheck.pur) {
16866 toCheck.pur = true;
16867 var errorMessage = 'Possibly unhandled rejection: ' + toDebugString(toCheck.value);
16868 if (toCheck.value instanceof Error) {
16869 exceptionHandler(toCheck.value, errorMessage);
16871 exceptionHandler(errorMessage);
16877 function scheduleProcessQueue(state) {
16878 if (errorOnUnhandledRejections && !state.pending && state.status === 2 && !state.pur) {
16879 if (queueSize === 0 && checkQueue.length === 0) {
16880 nextTick(processChecks);
16882 checkQueue.push(state);
16884 if (state.processScheduled || !state.pending) return;
16885 state.processScheduled = true;
16887 nextTick(function() { processQueue(state); });
16890 function resolvePromise(promise, val) {
16891 if (promise.$$state.status) return;
16892 if (val === promise) {
16893 $$reject(promise, $qMinErr(
16895 'Expected promise to be resolved with value other than itself \'{0}\'',
16898 $$resolve(promise, val);
16903 function $$resolve(promise, val) {
16907 if (isObject(val) || isFunction(val)) then = val.then;
16908 if (isFunction(then)) {
16909 promise.$$state.status = -1;
16910 then.call(val, doResolve, doReject, doNotify);
16912 promise.$$state.value = val;
16913 promise.$$state.status = 1;
16914 scheduleProcessQueue(promise.$$state);
16920 function doResolve(val) {
16923 $$resolve(promise, val);
16925 function doReject(val) {
16928 $$reject(promise, val);
16930 function doNotify(progress) {
16931 notifyPromise(promise, progress);
16935 function rejectPromise(promise, reason) {
16936 if (promise.$$state.status) return;
16937 $$reject(promise, reason);
16940 function $$reject(promise, reason) {
16941 promise.$$state.value = reason;
16942 promise.$$state.status = 2;
16943 scheduleProcessQueue(promise.$$state);
16946 function notifyPromise(promise, progress) {
16947 var callbacks = promise.$$state.pending;
16949 if ((promise.$$state.status <= 0) && callbacks && callbacks.length) {
16950 nextTick(function() {
16951 var callback, result;
16952 for (var i = 0, ii = callbacks.length; i < ii; i++) {
16953 result = callbacks[i][0];
16954 callback = callbacks[i][3];
16956 notifyPromise(result, isFunction(callback) ? callback(progress) : progress);
16958 exceptionHandler(e);
16971 * Creates a promise that is resolved as rejected with the specified `reason`. This api should be
16972 * used to forward rejection in a chain of promises. If you are dealing with the last promise in
16973 * a promise chain, you don't need to worry about it.
16975 * When comparing deferreds/promises to the familiar behavior of try/catch/throw, think of
16976 * `reject` as the `throw` keyword in JavaScript. This also means that if you "catch" an error via
16977 * a promise error callback and you want to forward the error to the promise derived from the
16978 * current promise, you have to "rethrow" the error by returning a rejection constructed via
16982 * promiseB = promiseA.then(function(result) {
16983 * // success: do something and resolve promiseB
16984 * // with the old or a new result
16986 * }, function(reason) {
16987 * // error: handle the error if possible and
16988 * // resolve promiseB with newPromiseOrValue,
16989 * // otherwise forward the rejection to promiseB
16990 * if (canHandle(reason)) {
16991 * // handle the error and recover
16992 * return newPromiseOrValue;
16994 * return $q.reject(reason);
16998 * @param {*} reason Constant, message, exception or an object representing the rejection reason.
16999 * @returns {Promise} Returns a promise that was already resolved as rejected with the `reason`.
17001 function reject(reason) {
17002 var result = new Promise();
17003 rejectPromise(result, reason);
17007 function handleCallback(value, resolver, callback) {
17008 var callbackOutput = null;
17010 if (isFunction(callback)) callbackOutput = callback();
17014 if (isPromiseLike(callbackOutput)) {
17015 return callbackOutput.then(function() {
17016 return resolver(value);
17019 return resolver(value);
17029 * Wraps an object that might be a value or a (3rd party) then-able promise into a $q promise.
17030 * This is useful when you are dealing with an object that might or might not be a promise, or if
17031 * the promise comes from a source that can't be trusted.
17033 * @param {*} value Value or a promise
17034 * @param {Function=} successCallback
17035 * @param {Function=} errorCallback
17036 * @param {Function=} progressCallback
17037 * @returns {Promise} Returns a promise of the passed value or promise
17041 function when(value, callback, errback, progressBack) {
17042 var result = new Promise();
17043 resolvePromise(result, value);
17044 return result.then(callback, errback, progressBack);
17053 * Alias of {@link ng.$q#when when} to maintain naming consistency with ES6.
17055 * @param {*} value Value or a promise
17056 * @param {Function=} successCallback
17057 * @param {Function=} errorCallback
17058 * @param {Function=} progressCallback
17059 * @returns {Promise} Returns a promise of the passed value or promise
17061 var resolve = when;
17069 * Combines multiple promises into a single promise that is resolved when all of the input
17070 * promises are resolved.
17072 * @param {Array.<Promise>|Object.<Promise>} promises An array or hash of promises.
17073 * @returns {Promise} Returns a single promise that will be resolved with an array/hash of values,
17074 * each value corresponding to the promise at the same index/key in the `promises` array/hash.
17075 * If any of the promises is resolved with a rejection, this resulting promise will be rejected
17076 * with the same rejection value.
17079 function all(promises) {
17080 var result = new Promise(),
17082 results = isArray(promises) ? [] : {};
17084 forEach(promises, function(promise, key) {
17086 when(promise).then(function(value) {
17087 results[key] = value;
17088 if (!(--counter)) resolvePromise(result, results);
17089 }, function(reason) {
17090 rejectPromise(result, reason);
17094 if (counter === 0) {
17095 resolvePromise(result, results);
17107 * Returns a promise that resolves or rejects as soon as one of those promises
17108 * resolves or rejects, with the value or reason from that promise.
17110 * @param {Array.<Promise>|Object.<Promise>} promises An array or hash of promises.
17111 * @returns {Promise} a promise that resolves or rejects as soon as one of the `promises`
17112 * resolves or rejects, with the value or reason from that promise.
17115 function race(promises) {
17116 var deferred = defer();
17118 forEach(promises, function(promise) {
17119 when(promise).then(deferred.resolve, deferred.reject);
17122 return deferred.promise;
17125 function $Q(resolver) {
17126 if (!isFunction(resolver)) {
17127 throw $qMinErr('norslvr', 'Expected resolverFn, got \'{0}\'', resolver);
17130 var promise = new Promise();
17132 function resolveFn(value) {
17133 resolvePromise(promise, value);
17136 function rejectFn(reason) {
17137 rejectPromise(promise, reason);
17140 resolver(resolveFn, rejectFn);
17145 // Let's make the instanceof operator work for promises, so that
17146 // `new $q(fn) instanceof $q` would evaluate to true.
17147 $Q.prototype = Promise.prototype;
17150 $Q.reject = reject;
17152 $Q.resolve = resolve;
17160 function $$RAFProvider() { //rAF
17161 this.$get = ['$window', '$timeout', function($window, $timeout) {
17162 var requestAnimationFrame = $window.requestAnimationFrame ||
17163 $window.webkitRequestAnimationFrame;
17165 var cancelAnimationFrame = $window.cancelAnimationFrame ||
17166 $window.webkitCancelAnimationFrame ||
17167 $window.webkitCancelRequestAnimationFrame;
17169 var rafSupported = !!requestAnimationFrame;
17170 var raf = rafSupported
17172 var id = requestAnimationFrame(fn);
17173 return function() {
17174 cancelAnimationFrame(id);
17178 var timer = $timeout(fn, 16.66, false); // 1000 / 60 = 16.666
17179 return function() {
17180 $timeout.cancel(timer);
17184 raf.supported = rafSupported;
17193 * The design decisions behind the scope are heavily favored for speed and memory consumption.
17195 * The typical use of scope is to watch the expressions, which most of the time return the same
17196 * value as last time so we optimize the operation.
17198 * Closures construction is expensive in terms of speed as well as memory:
17199 * - No closures, instead use prototypical inheritance for API
17200 * - Internal state needs to be stored on scope directly, which means that private state is
17201 * exposed as $$____ properties
17203 * Loop operations are optimized by using while(count--) { ... }
17204 * - This means that in order to keep the same order of execution as addition we have to add
17205 * items to the array at the beginning (unshift) instead of at the end (push)
17207 * Child scopes are created and removed often
17208 * - Using an array would be slow since inserts in the middle are expensive; so we use linked lists
17210 * There are fewer watches than observers. This is why you don't want the observer to be implemented
17211 * in the same way as watch. Watch requires return of the initialization function which is expensive
17218 * @name $rootScopeProvider
17221 * Provider for the $rootScope service.
17226 * @name $rootScopeProvider#digestTtl
17229 * Sets the number of `$digest` iterations the scope should attempt to execute before giving up and
17230 * assuming that the model is unstable.
17232 * The current default is 10 iterations.
17234 * In complex applications it's possible that the dependencies between `$watch`s will result in
17235 * several digest iterations. However if an application needs more than the default 10 digest
17236 * iterations for its model to stabilize then you should investigate what is causing the model to
17237 * continuously change during the digest.
17239 * Increasing the TTL could have performance implications, so you should not change it without
17240 * proper justification.
17242 * @param {number} limit The number of digest iterations.
17253 * Every application has a single root {@link ng.$rootScope.Scope scope}.
17254 * All other scopes are descendant scopes of the root scope. Scopes provide separation
17255 * between the model and the view, via a mechanism for watching the model for changes.
17256 * They also provide event emission/broadcast and subscription facility. See the
17257 * {@link guide/scope developer guide on scopes}.
17259 function $RootScopeProvider() {
17261 var $rootScopeMinErr = minErr('$rootScope');
17262 var lastDirtyWatch = null;
17263 var applyAsyncId = null;
17265 this.digestTtl = function(value) {
17266 if (arguments.length) {
17272 function createChildScopeClass(parent) {
17273 function ChildScope() {
17274 this.$$watchers = this.$$nextSibling =
17275 this.$$childHead = this.$$childTail = null;
17276 this.$$listeners = {};
17277 this.$$listenerCount = {};
17278 this.$$watchersCount = 0;
17279 this.$id = nextUid();
17280 this.$$ChildScope = null;
17282 ChildScope.prototype = parent;
17286 this.$get = ['$exceptionHandler', '$parse', '$browser',
17287 function($exceptionHandler, $parse, $browser) {
17289 function destroyChildScope($event) {
17290 $event.currentScope.$$destroyed = true;
17293 function cleanUpScope($scope) {
17295 // Support: IE 9 only
17297 // There is a memory leak in IE9 if all child scopes are not disconnected
17298 // completely when a scope is destroyed. So this code will recurse up through
17299 // all this scopes children
17301 // See issue https://github.com/angular/angular.js/issues/10706
17302 if ($scope.$$childHead) {
17303 cleanUpScope($scope.$$childHead);
17305 if ($scope.$$nextSibling) {
17306 cleanUpScope($scope.$$nextSibling);
17310 // The code below works around IE9 and V8's memory leaks
17313 // - https://code.google.com/p/v8/issues/detail?id=2073#c26
17314 // - https://github.com/angular/angular.js/issues/6794#issuecomment-38648909
17315 // - https://github.com/angular/angular.js/issues/1313#issuecomment-10378451
17317 $scope.$parent = $scope.$$nextSibling = $scope.$$prevSibling = $scope.$$childHead =
17318 $scope.$$childTail = $scope.$root = $scope.$$watchers = null;
17323 * @name $rootScope.Scope
17326 * A root scope can be retrieved using the {@link ng.$rootScope $rootScope} key from the
17327 * {@link auto.$injector $injector}. Child scopes are created using the
17328 * {@link ng.$rootScope.Scope#$new $new()} method. (Most scopes are created automatically when
17329 * compiled HTML template is executed.) See also the {@link guide/scope Scopes guide} for
17330 * an in-depth introduction and usage examples.
17334 * A scope can inherit from a parent scope, as in this example:
17336 var parent = $rootScope;
17337 var child = parent.$new();
17339 parent.salutation = "Hello";
17340 expect(child.salutation).toEqual('Hello');
17342 child.salutation = "Welcome";
17343 expect(child.salutation).toEqual('Welcome');
17344 expect(parent.salutation).toEqual('Hello');
17347 * When interacting with `Scope` in tests, additional helper methods are available on the
17348 * instances of `Scope` type. See {@link ngMock.$rootScope.Scope ngMock Scope} for additional
17352 * @param {Object.<string, function()>=} providers Map of service factory which need to be
17353 * provided for the current scope. Defaults to {@link ng}.
17354 * @param {Object.<string, *>=} instanceCache Provides pre-instantiated services which should
17355 * append/override services provided by `providers`. This is handy
17356 * when unit-testing and having the need to override a default
17358 * @returns {Object} Newly created scope.
17362 this.$id = nextUid();
17363 this.$$phase = this.$parent = this.$$watchers =
17364 this.$$nextSibling = this.$$prevSibling =
17365 this.$$childHead = this.$$childTail = null;
17367 this.$$destroyed = false;
17368 this.$$listeners = {};
17369 this.$$listenerCount = {};
17370 this.$$watchersCount = 0;
17371 this.$$isolateBindings = null;
17376 * @name $rootScope.Scope#$id
17379 * Unique scope ID (monotonically increasing) useful for debugging.
17384 * @name $rootScope.Scope#$parent
17387 * Reference to the parent scope.
17392 * @name $rootScope.Scope#$root
17395 * Reference to the root scope.
17398 Scope.prototype = {
17399 constructor: Scope,
17402 * @name $rootScope.Scope#$new
17406 * Creates a new child {@link ng.$rootScope.Scope scope}.
17408 * The parent scope will propagate the {@link ng.$rootScope.Scope#$digest $digest()} event.
17409 * The scope can be removed from the scope hierarchy using {@link ng.$rootScope.Scope#$destroy $destroy()}.
17411 * {@link ng.$rootScope.Scope#$destroy $destroy()} must be called on a scope when it is
17412 * desired for the scope and its child scopes to be permanently detached from the parent and
17413 * thus stop participating in model change detection and listener notification by invoking.
17415 * @param {boolean} isolate If true, then the scope does not prototypically inherit from the
17416 * parent scope. The scope is isolated, as it can not see parent scope properties.
17417 * When creating widgets, it is useful for the widget to not accidentally read parent
17420 * @param {Scope} [parent=this] The {@link ng.$rootScope.Scope `Scope`} that will be the `$parent`
17421 * of the newly created scope. Defaults to `this` scope if not provided.
17422 * This is used when creating a transclude scope to correctly place it
17423 * in the scope hierarchy while maintaining the correct prototypical
17426 * @returns {Object} The newly created child scope.
17429 $new: function(isolate, parent) {
17432 parent = parent || this;
17435 child = new Scope();
17436 child.$root = this.$root;
17438 // Only create a child scope class if somebody asks for one,
17439 // but cache it to allow the VM to optimize lookups.
17440 if (!this.$$ChildScope) {
17441 this.$$ChildScope = createChildScopeClass(this);
17443 child = new this.$$ChildScope();
17445 child.$parent = parent;
17446 child.$$prevSibling = parent.$$childTail;
17447 if (parent.$$childHead) {
17448 parent.$$childTail.$$nextSibling = child;
17449 parent.$$childTail = child;
17451 parent.$$childHead = parent.$$childTail = child;
17454 // When the new scope is not isolated or we inherit from `this`, and
17455 // the parent scope is destroyed, the property `$$destroyed` is inherited
17456 // prototypically. In all other cases, this property needs to be set
17457 // when the parent scope is destroyed.
17458 // The listener needs to be added after the parent is set
17459 if (isolate || parent !== this) child.$on('$destroy', destroyChildScope);
17466 * @name $rootScope.Scope#$watch
17470 * Registers a `listener` callback to be executed whenever the `watchExpression` changes.
17472 * - The `watchExpression` is called on every call to {@link ng.$rootScope.Scope#$digest
17473 * $digest()} and should return the value that will be watched. (`watchExpression` should not change
17474 * its value when executed multiple times with the same input because it may be executed multiple
17475 * times by {@link ng.$rootScope.Scope#$digest $digest()}. That is, `watchExpression` should be
17476 * [idempotent](http://en.wikipedia.org/wiki/Idempotence).)
17477 * - The `listener` is called only when the value from the current `watchExpression` and the
17478 * previous call to `watchExpression` are not equal (with the exception of the initial run,
17479 * see below). Inequality is determined according to reference inequality,
17480 * [strict comparison](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators)
17481 * via the `!==` Javascript operator, unless `objectEquality == true`
17483 * - When `objectEquality == true`, inequality of the `watchExpression` is determined
17484 * according to the {@link angular.equals} function. To save the value of the object for
17485 * later comparison, the {@link angular.copy} function is used. This therefore means that
17486 * watching complex objects will have adverse memory and performance implications.
17487 * - This should not be used to watch for changes in objects that are
17488 * or contain [File](https://developer.mozilla.org/docs/Web/API/File) objects due to limitations with {@link angular.copy `angular.copy`}.
17489 * - The watch `listener` may change the model, which may trigger other `listener`s to fire.
17490 * This is achieved by rerunning the watchers until no changes are detected. The rerun
17491 * iteration limit is 10 to prevent an infinite loop deadlock.
17494 * If you want to be notified whenever {@link ng.$rootScope.Scope#$digest $digest} is called,
17495 * you can register a `watchExpression` function with no `listener`. (Be prepared for
17496 * multiple calls to your `watchExpression` because it will execute multiple times in a
17497 * single {@link ng.$rootScope.Scope#$digest $digest} cycle if a change is detected.)
17499 * After a watcher is registered with the scope, the `listener` fn is called asynchronously
17500 * (via {@link ng.$rootScope.Scope#$evalAsync $evalAsync}) to initialize the
17501 * watcher. In rare cases, this is undesirable because the listener is called when the result
17502 * of `watchExpression` didn't change. To detect this scenario within the `listener` fn, you
17503 * can compare the `newVal` and `oldVal`. If these two values are identical (`===`) then the
17504 * listener was called due to initialization.
17510 // let's assume that scope was dependency injected as the $rootScope
17511 var scope = $rootScope;
17512 scope.name = 'misko';
17515 expect(scope.counter).toEqual(0);
17516 scope.$watch('name', function(newValue, oldValue) {
17517 scope.counter = scope.counter + 1;
17519 expect(scope.counter).toEqual(0);
17522 // the listener is always called during the first $digest loop after it was registered
17523 expect(scope.counter).toEqual(1);
17526 // but now it will not be called unless the value changes
17527 expect(scope.counter).toEqual(1);
17529 scope.name = 'adam';
17531 expect(scope.counter).toEqual(2);
17535 // Using a function as a watchExpression
17537 scope.foodCounter = 0;
17538 expect(scope.foodCounter).toEqual(0);
17540 // This function returns the value being watched. It is called for each turn of the $digest loop
17541 function() { return food; },
17542 // This is the change listener, called when the value returned from the above function changes
17543 function(newValue, oldValue) {
17544 if ( newValue !== oldValue ) {
17545 // Only increment the counter if the value changed
17546 scope.foodCounter = scope.foodCounter + 1;
17550 // No digest has been run so the counter will be zero
17551 expect(scope.foodCounter).toEqual(0);
17553 // Run the digest but since food has not changed count will still be zero
17555 expect(scope.foodCounter).toEqual(0);
17557 // Update food and run digest. Now the counter will increment
17558 food = 'cheeseburger';
17560 expect(scope.foodCounter).toEqual(1);
17566 * @param {(function()|string)} watchExpression Expression that is evaluated on each
17567 * {@link ng.$rootScope.Scope#$digest $digest} cycle. A change in the return value triggers
17568 * a call to the `listener`.
17570 * - `string`: Evaluated as {@link guide/expression expression}
17571 * - `function(scope)`: called with current `scope` as a parameter.
17572 * @param {function(newVal, oldVal, scope)} listener Callback called whenever the value
17573 * of `watchExpression` changes.
17575 * - `newVal` contains the current value of the `watchExpression`
17576 * - `oldVal` contains the previous value of the `watchExpression`
17577 * - `scope` refers to the current scope
17578 * @param {boolean=} [objectEquality=false] Compare for object equality using {@link angular.equals} instead of
17579 * comparing for reference equality.
17580 * @returns {function()} Returns a deregistration function for this listener.
17582 $watch: function(watchExp, listener, objectEquality, prettyPrintExpression) {
17583 var get = $parse(watchExp);
17585 if (get.$$watchDelegate) {
17586 return get.$$watchDelegate(this, listener, objectEquality, get, watchExp);
17589 array = scope.$$watchers,
17592 last: initWatchVal,
17594 exp: prettyPrintExpression || watchExp,
17595 eq: !!objectEquality
17598 lastDirtyWatch = null;
17600 if (!isFunction(listener)) {
17605 array = scope.$$watchers = [];
17606 array.$$digestWatchIndex = -1;
17608 // we use unshift since we use a while loop in $digest for speed.
17609 // the while loop reads in reverse order.
17610 array.unshift(watcher);
17611 array.$$digestWatchIndex++;
17612 incrementWatchersCount(this, 1);
17614 return function deregisterWatch() {
17615 var index = arrayRemove(array, watcher);
17617 incrementWatchersCount(scope, -1);
17618 if (index < array.$$digestWatchIndex) {
17619 array.$$digestWatchIndex--;
17622 lastDirtyWatch = null;
17628 * @name $rootScope.Scope#$watchGroup
17632 * A variant of {@link ng.$rootScope.Scope#$watch $watch()} where it watches an array of `watchExpressions`.
17633 * If any one expression in the collection changes the `listener` is executed.
17635 * - The items in the `watchExpressions` array are observed via the standard `$watch` operation. Their return
17636 * values are examined for changes on every call to `$digest`.
17637 * - The `listener` is called whenever any expression in the `watchExpressions` array changes.
17639 * @param {Array.<string|Function(scope)>} watchExpressions Array of expressions that will be individually
17640 * watched using {@link ng.$rootScope.Scope#$watch $watch()}
17642 * @param {function(newValues, oldValues, scope)} listener Callback called whenever the return value of any
17643 * expression in `watchExpressions` changes
17644 * The `newValues` array contains the current values of the `watchExpressions`, with the indexes matching
17645 * those of `watchExpression`
17646 * and the `oldValues` array contains the previous values of the `watchExpressions`, with the indexes matching
17647 * those of `watchExpression`
17648 * The `scope` refers to the current scope.
17649 * @returns {function()} Returns a de-registration function for all listeners.
17651 $watchGroup: function(watchExpressions, listener) {
17652 var oldValues = new Array(watchExpressions.length);
17653 var newValues = new Array(watchExpressions.length);
17654 var deregisterFns = [];
17656 var changeReactionScheduled = false;
17657 var firstRun = true;
17659 if (!watchExpressions.length) {
17660 // No expressions means we call the listener ASAP
17661 var shouldCall = true;
17662 self.$evalAsync(function() {
17663 if (shouldCall) listener(newValues, newValues, self);
17665 return function deregisterWatchGroup() {
17666 shouldCall = false;
17670 if (watchExpressions.length === 1) {
17671 // Special case size of one
17672 return this.$watch(watchExpressions[0], function watchGroupAction(value, oldValue, scope) {
17673 newValues[0] = value;
17674 oldValues[0] = oldValue;
17675 listener(newValues, (value === oldValue) ? newValues : oldValues, scope);
17679 forEach(watchExpressions, function(expr, i) {
17680 var unwatchFn = self.$watch(expr, function watchGroupSubAction(value, oldValue) {
17681 newValues[i] = value;
17682 oldValues[i] = oldValue;
17683 if (!changeReactionScheduled) {
17684 changeReactionScheduled = true;
17685 self.$evalAsync(watchGroupAction);
17688 deregisterFns.push(unwatchFn);
17691 function watchGroupAction() {
17692 changeReactionScheduled = false;
17696 listener(newValues, newValues, self);
17698 listener(newValues, oldValues, self);
17702 return function deregisterWatchGroup() {
17703 while (deregisterFns.length) {
17704 deregisterFns.shift()();
17712 * @name $rootScope.Scope#$watchCollection
17716 * Shallow watches the properties of an object and fires whenever any of the properties change
17717 * (for arrays, this implies watching the array items; for object maps, this implies watching
17718 * the properties). If a change is detected, the `listener` callback is fired.
17720 * - The `obj` collection is observed via standard $watch operation and is examined on every
17721 * call to $digest() to see if any items have been added, removed, or moved.
17722 * - The `listener` is called whenever anything within the `obj` has changed. Examples include
17723 * adding, removing, and moving items belonging to an object or array.
17728 $scope.names = ['igor', 'matias', 'misko', 'james'];
17729 $scope.dataCount = 4;
17731 $scope.$watchCollection('names', function(newNames, oldNames) {
17732 $scope.dataCount = newNames.length;
17735 expect($scope.dataCount).toEqual(4);
17738 //still at 4 ... no changes
17739 expect($scope.dataCount).toEqual(4);
17741 $scope.names.pop();
17744 //now there's been a change
17745 expect($scope.dataCount).toEqual(3);
17749 * @param {string|function(scope)} obj Evaluated as {@link guide/expression expression}. The
17750 * expression value should evaluate to an object or an array which is observed on each
17751 * {@link ng.$rootScope.Scope#$digest $digest} cycle. Any shallow change within the
17752 * collection will trigger a call to the `listener`.
17754 * @param {function(newCollection, oldCollection, scope)} listener a callback function called
17755 * when a change is detected.
17756 * - The `newCollection` object is the newly modified data obtained from the `obj` expression
17757 * - The `oldCollection` object is a copy of the former collection data.
17758 * Due to performance considerations, the`oldCollection` value is computed only if the
17759 * `listener` function declares two or more arguments.
17760 * - The `scope` argument refers to the current scope.
17762 * @returns {function()} Returns a de-registration function for this listener. When the
17763 * de-registration function is executed, the internal watch operation is terminated.
17765 $watchCollection: function(obj, listener) {
17766 $watchCollectionInterceptor.$stateful = true;
17769 // the current value, updated on each dirty-check run
17771 // a shallow copy of the newValue from the last dirty-check run,
17772 // updated to match newValue during dirty-check run
17774 // a shallow copy of the newValue from when the last change happened
17776 // only track veryOldValue if the listener is asking for it
17777 var trackVeryOldValue = (listener.length > 1);
17778 var changeDetected = 0;
17779 var changeDetector = $parse(obj, $watchCollectionInterceptor);
17780 var internalArray = [];
17781 var internalObject = {};
17782 var initRun = true;
17785 function $watchCollectionInterceptor(_value) {
17787 var newLength, key, bothNaN, newItem, oldItem;
17789 // If the new value is undefined, then return undefined as the watch may be a one-time watch
17790 if (isUndefined(newValue)) return;
17792 if (!isObject(newValue)) { // if primitive
17793 if (oldValue !== newValue) {
17794 oldValue = newValue;
17797 } else if (isArrayLike(newValue)) {
17798 if (oldValue !== internalArray) {
17799 // we are transitioning from something which was not an array into array.
17800 oldValue = internalArray;
17801 oldLength = oldValue.length = 0;
17805 newLength = newValue.length;
17807 if (oldLength !== newLength) {
17808 // if lengths do not match we need to trigger change notification
17810 oldValue.length = oldLength = newLength;
17812 // copy the items to oldValue and look for changes.
17813 for (var i = 0; i < newLength; i++) {
17814 oldItem = oldValue[i];
17815 newItem = newValue[i];
17817 // eslint-disable-next-line no-self-compare
17818 bothNaN = (oldItem !== oldItem) && (newItem !== newItem);
17819 if (!bothNaN && (oldItem !== newItem)) {
17821 oldValue[i] = newItem;
17825 if (oldValue !== internalObject) {
17826 // we are transitioning from something which was not an object into object.
17827 oldValue = internalObject = {};
17831 // copy the items to oldValue and look for changes.
17833 for (key in newValue) {
17834 if (hasOwnProperty.call(newValue, key)) {
17836 newItem = newValue[key];
17837 oldItem = oldValue[key];
17839 if (key in oldValue) {
17840 // eslint-disable-next-line no-self-compare
17841 bothNaN = (oldItem !== oldItem) && (newItem !== newItem);
17842 if (!bothNaN && (oldItem !== newItem)) {
17844 oldValue[key] = newItem;
17848 oldValue[key] = newItem;
17853 if (oldLength > newLength) {
17854 // we used to have more keys, need to find them and destroy them.
17856 for (key in oldValue) {
17857 if (!hasOwnProperty.call(newValue, key)) {
17859 delete oldValue[key];
17864 return changeDetected;
17867 function $watchCollectionAction() {
17870 listener(newValue, newValue, self);
17872 listener(newValue, veryOldValue, self);
17875 // make a copy for the next time a collection is changed
17876 if (trackVeryOldValue) {
17877 if (!isObject(newValue)) {
17879 veryOldValue = newValue;
17880 } else if (isArrayLike(newValue)) {
17881 veryOldValue = new Array(newValue.length);
17882 for (var i = 0; i < newValue.length; i++) {
17883 veryOldValue[i] = newValue[i];
17885 } else { // if object
17887 for (var key in newValue) {
17888 if (hasOwnProperty.call(newValue, key)) {
17889 veryOldValue[key] = newValue[key];
17896 return this.$watch(changeDetector, $watchCollectionAction);
17901 * @name $rootScope.Scope#$digest
17905 * Processes all of the {@link ng.$rootScope.Scope#$watch watchers} of the current scope and
17906 * its children. Because a {@link ng.$rootScope.Scope#$watch watcher}'s listener can change
17907 * the model, the `$digest()` keeps calling the {@link ng.$rootScope.Scope#$watch watchers}
17908 * until no more listeners are firing. This means that it is possible to get into an infinite
17909 * loop. This function will throw `'Maximum iteration limit exceeded.'` if the number of
17910 * iterations exceeds 10.
17912 * Usually, you don't call `$digest()` directly in
17913 * {@link ng.directive:ngController controllers} or in
17914 * {@link ng.$compileProvider#directive directives}.
17915 * Instead, you should call {@link ng.$rootScope.Scope#$apply $apply()} (typically from within
17916 * a {@link ng.$compileProvider#directive directive}), which will force a `$digest()`.
17918 * If you want to be notified whenever `$digest()` is called,
17919 * you can register a `watchExpression` function with
17920 * {@link ng.$rootScope.Scope#$watch $watch()} with no `listener`.
17922 * In unit tests, you may need to call `$digest()` to simulate the scope life cycle.
17927 scope.name = 'misko';
17930 expect(scope.counter).toEqual(0);
17931 scope.$watch('name', function(newValue, oldValue) {
17932 scope.counter = scope.counter + 1;
17934 expect(scope.counter).toEqual(0);
17937 // the listener is always called during the first $digest loop after it was registered
17938 expect(scope.counter).toEqual(1);
17941 // but now it will not be called unless the value changes
17942 expect(scope.counter).toEqual(1);
17944 scope.name = 'adam';
17946 expect(scope.counter).toEqual(2);
17950 $digest: function() {
17951 var watch, value, last, fn, get,
17954 next, current, target = this,
17958 beginPhase('$digest');
17959 // Check for changes to browser url that happened in sync before the call to $digest
17960 $browser.$$checkUrlChange();
17962 if (this === $rootScope && applyAsyncId !== null) {
17963 // If this is the root scope, and $applyAsync has scheduled a deferred $apply(), then
17964 // cancel the scheduled $apply and flush the queue of expressions to be evaluated.
17965 $browser.defer.cancel(applyAsyncId);
17969 lastDirtyWatch = null;
17971 do { // "while dirty" loop
17975 // It's safe for asyncQueuePosition to be a local variable here because this loop can't
17976 // be reentered recursively. Calling $digest from a function passed to $evalAsync would
17977 // lead to a '$digest already in progress' error.
17978 for (var asyncQueuePosition = 0; asyncQueuePosition < asyncQueue.length; asyncQueuePosition++) {
17980 asyncTask = asyncQueue[asyncQueuePosition];
17982 fn(asyncTask.scope, asyncTask.locals);
17984 $exceptionHandler(e);
17986 lastDirtyWatch = null;
17988 asyncQueue.length = 0;
17990 traverseScopesLoop:
17991 do { // "traverse the scopes" loop
17992 if ((watchers = current.$$watchers)) {
17993 // process our watches
17994 watchers.$$digestWatchIndex = watchers.length;
17995 while (watchers.$$digestWatchIndex--) {
17997 watch = watchers[watchers.$$digestWatchIndex];
17998 // Most common watches are on primitives, in which case we can short
17999 // circuit it with === operator, only when === fails do we use .equals
18002 if ((value = get(current)) !== (last = watch.last) &&
18004 ? equals(value, last)
18005 : (isNumberNaN(value) && isNumberNaN(last)))) {
18007 lastDirtyWatch = watch;
18008 watch.last = watch.eq ? copy(value, null) : value;
18010 fn(value, ((last === initWatchVal) ? value : last), current);
18013 if (!watchLog[logIdx]) watchLog[logIdx] = [];
18014 watchLog[logIdx].push({
18015 msg: isFunction(watch.exp) ? 'fn: ' + (watch.exp.name || watch.exp.toString()) : watch.exp,
18020 } else if (watch === lastDirtyWatch) {
18021 // If the most recently dirty watcher is now clean, short circuit since the remaining watchers
18022 // have already been tested.
18024 break traverseScopesLoop;
18028 $exceptionHandler(e);
18033 // Insanity Warning: scope depth-first traversal
18034 // yes, this code is a bit crazy, but it works and we have tests to prove it!
18035 // this piece should be kept in sync with the traversal in $broadcast
18036 if (!(next = ((current.$$watchersCount && current.$$childHead) ||
18037 (current !== target && current.$$nextSibling)))) {
18038 while (current !== target && !(next = current.$$nextSibling)) {
18039 current = current.$parent;
18042 } while ((current = next));
18044 // `break traverseScopesLoop;` takes us to here
18046 if ((dirty || asyncQueue.length) && !(ttl--)) {
18048 throw $rootScopeMinErr('infdig',
18049 '{0} $digest() iterations reached. Aborting!\n' +
18050 'Watchers fired in the last 5 iterations: {1}',
18054 } while (dirty || asyncQueue.length);
18058 // postDigestQueuePosition isn't local here because this loop can be reentered recursively.
18059 while (postDigestQueuePosition < postDigestQueue.length) {
18061 postDigestQueue[postDigestQueuePosition++]();
18063 $exceptionHandler(e);
18066 postDigestQueue.length = postDigestQueuePosition = 0;
18068 // Check for changes to browser url that happened during the $digest
18069 // (for which no event is fired; e.g. via `history.pushState()`)
18070 $browser.$$checkUrlChange();
18076 * @name $rootScope.Scope#$destroy
18077 * @eventType broadcast on scope being destroyed
18080 * Broadcasted when a scope and its children are being destroyed.
18082 * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to
18083 * clean up DOM bindings before an element is removed from the DOM.
18088 * @name $rootScope.Scope#$destroy
18092 * Removes the current scope (and all of its children) from the parent scope. Removal implies
18093 * that calls to {@link ng.$rootScope.Scope#$digest $digest()} will no longer
18094 * propagate to the current scope and its children. Removal also implies that the current
18095 * scope is eligible for garbage collection.
18097 * The `$destroy()` is usually used by directives such as
18098 * {@link ng.directive:ngRepeat ngRepeat} for managing the
18099 * unrolling of the loop.
18101 * Just before a scope is destroyed, a `$destroy` event is broadcasted on this scope.
18102 * Application code can register a `$destroy` event handler that will give it a chance to
18103 * perform any necessary cleanup.
18105 * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to
18106 * clean up DOM bindings before an element is removed from the DOM.
18108 $destroy: function() {
18109 // We can't destroy a scope that has been already destroyed.
18110 if (this.$$destroyed) return;
18111 var parent = this.$parent;
18113 this.$broadcast('$destroy');
18114 this.$$destroyed = true;
18116 if (this === $rootScope) {
18117 //Remove handlers attached to window when $rootScope is removed
18118 $browser.$$applicationDestroyed();
18121 incrementWatchersCount(this, -this.$$watchersCount);
18122 for (var eventName in this.$$listenerCount) {
18123 decrementListenerCount(this, this.$$listenerCount[eventName], eventName);
18126 // sever all the references to parent scopes (after this cleanup, the current scope should
18127 // not be retained by any of our references and should be eligible for garbage collection)
18128 if (parent && parent.$$childHead === this) parent.$$childHead = this.$$nextSibling;
18129 if (parent && parent.$$childTail === this) parent.$$childTail = this.$$prevSibling;
18130 if (this.$$prevSibling) this.$$prevSibling.$$nextSibling = this.$$nextSibling;
18131 if (this.$$nextSibling) this.$$nextSibling.$$prevSibling = this.$$prevSibling;
18133 // Disable listeners, watchers and apply/digest methods
18134 this.$destroy = this.$digest = this.$apply = this.$evalAsync = this.$applyAsync = noop;
18135 this.$on = this.$watch = this.$watchGroup = function() { return noop; };
18136 this.$$listeners = {};
18138 // Disconnect the next sibling to prevent `cleanUpScope` destroying those too
18139 this.$$nextSibling = null;
18140 cleanUpScope(this);
18145 * @name $rootScope.Scope#$eval
18149 * Executes the `expression` on the current scope and returns the result. Any exceptions in
18150 * the expression are propagated (uncaught). This is useful when evaluating Angular
18155 var scope = ng.$rootScope.Scope();
18159 expect(scope.$eval('a+b')).toEqual(3);
18160 expect(scope.$eval(function(scope){ return scope.a + scope.b; })).toEqual(3);
18163 * @param {(string|function())=} expression An angular expression to be executed.
18165 * - `string`: execute using the rules as defined in {@link guide/expression expression}.
18166 * - `function(scope)`: execute the function with the current `scope` parameter.
18168 * @param {(object)=} locals Local variables object, useful for overriding values in scope.
18169 * @returns {*} The result of evaluating the expression.
18171 $eval: function(expr, locals) {
18172 return $parse(expr)(this, locals);
18177 * @name $rootScope.Scope#$evalAsync
18181 * Executes the expression on the current scope at a later point in time.
18183 * The `$evalAsync` makes no guarantees as to when the `expression` will be executed, only
18186 * - it will execute after the function that scheduled the evaluation (preferably before DOM
18188 * - at least one {@link ng.$rootScope.Scope#$digest $digest cycle} will be performed after
18189 * `expression` execution.
18191 * Any exceptions from the execution of the expression are forwarded to the
18192 * {@link ng.$exceptionHandler $exceptionHandler} service.
18194 * __Note:__ if this function is called outside of a `$digest` cycle, a new `$digest` cycle
18195 * will be scheduled. However, it is encouraged to always call code that changes the model
18196 * from within an `$apply` call. That includes code evaluated via `$evalAsync`.
18198 * @param {(string|function())=} expression An angular expression to be executed.
18200 * - `string`: execute using the rules as defined in {@link guide/expression expression}.
18201 * - `function(scope)`: execute the function with the current `scope` parameter.
18203 * @param {(object)=} locals Local variables object, useful for overriding values in scope.
18205 $evalAsync: function(expr, locals) {
18206 // if we are outside of an $digest loop and this is the first time we are scheduling async
18207 // task also schedule async auto-flush
18208 if (!$rootScope.$$phase && !asyncQueue.length) {
18209 $browser.defer(function() {
18210 if (asyncQueue.length) {
18211 $rootScope.$digest();
18216 asyncQueue.push({scope: this, fn: $parse(expr), locals: locals});
18219 $$postDigest: function(fn) {
18220 postDigestQueue.push(fn);
18225 * @name $rootScope.Scope#$apply
18229 * `$apply()` is used to execute an expression in angular from outside of the angular
18230 * framework. (For example from browser DOM events, setTimeout, XHR or third party libraries).
18231 * Because we are calling into the angular framework we need to perform proper scope life
18232 * cycle of {@link ng.$exceptionHandler exception handling},
18233 * {@link ng.$rootScope.Scope#$digest executing watches}.
18237 * # Pseudo-Code of `$apply()`
18239 function $apply(expr) {
18241 return $eval(expr);
18243 $exceptionHandler(e);
18251 * Scope's `$apply()` method transitions through the following stages:
18253 * 1. The {@link guide/expression expression} is executed using the
18254 * {@link ng.$rootScope.Scope#$eval $eval()} method.
18255 * 2. Any exceptions from the execution of the expression are forwarded to the
18256 * {@link ng.$exceptionHandler $exceptionHandler} service.
18257 * 3. The {@link ng.$rootScope.Scope#$watch watch} listeners are fired immediately after the
18258 * expression was executed using the {@link ng.$rootScope.Scope#$digest $digest()} method.
18261 * @param {(string|function())=} exp An angular expression to be executed.
18263 * - `string`: execute using the rules as defined in {@link guide/expression expression}.
18264 * - `function(scope)`: execute the function with current `scope` parameter.
18266 * @returns {*} The result of evaluating the expression.
18268 $apply: function(expr) {
18270 beginPhase('$apply');
18272 return this.$eval(expr);
18277 $exceptionHandler(e);
18280 $rootScope.$digest();
18282 $exceptionHandler(e);
18283 // eslint-disable-next-line no-unsafe-finally
18291 * @name $rootScope.Scope#$applyAsync
18295 * Schedule the invocation of $apply to occur at a later time. The actual time difference
18296 * varies across browsers, but is typically around ~10 milliseconds.
18298 * This can be used to queue up multiple expressions which need to be evaluated in the same
18301 * @param {(string|function())=} exp An angular expression to be executed.
18303 * - `string`: execute using the rules as defined in {@link guide/expression expression}.
18304 * - `function(scope)`: execute the function with current `scope` parameter.
18306 $applyAsync: function(expr) {
18309 applyAsyncQueue.push($applyAsyncExpression);
18311 expr = $parse(expr);
18312 scheduleApplyAsync();
18314 function $applyAsyncExpression() {
18321 * @name $rootScope.Scope#$on
18325 * Listens on events of a given type. See {@link ng.$rootScope.Scope#$emit $emit} for
18326 * discussion of event life cycle.
18328 * The event listener function format is: `function(event, args...)`. The `event` object
18329 * passed into the listener has the following attributes:
18331 * - `targetScope` - `{Scope}`: the scope on which the event was `$emit`-ed or
18333 * - `currentScope` - `{Scope}`: the scope that is currently handling the event. Once the
18334 * event propagates through the scope hierarchy, this property is set to null.
18335 * - `name` - `{string}`: name of the event.
18336 * - `stopPropagation` - `{function=}`: calling `stopPropagation` function will cancel
18337 * further event propagation (available only for events that were `$emit`-ed).
18338 * - `preventDefault` - `{function}`: calling `preventDefault` sets `defaultPrevented` flag
18340 * - `defaultPrevented` - `{boolean}`: true if `preventDefault` was called.
18342 * @param {string} name Event name to listen on.
18343 * @param {function(event, ...args)} listener Function to call when the event is emitted.
18344 * @returns {function()} Returns a deregistration function for this listener.
18346 $on: function(name, listener) {
18347 var namedListeners = this.$$listeners[name];
18348 if (!namedListeners) {
18349 this.$$listeners[name] = namedListeners = [];
18351 namedListeners.push(listener);
18353 var current = this;
18355 if (!current.$$listenerCount[name]) {
18356 current.$$listenerCount[name] = 0;
18358 current.$$listenerCount[name]++;
18359 } while ((current = current.$parent));
18362 return function() {
18363 var indexOfListener = namedListeners.indexOf(listener);
18364 if (indexOfListener !== -1) {
18365 namedListeners[indexOfListener] = null;
18366 decrementListenerCount(self, 1, name);
18374 * @name $rootScope.Scope#$emit
18378 * Dispatches an event `name` upwards through the scope hierarchy notifying the
18379 * registered {@link ng.$rootScope.Scope#$on} listeners.
18381 * The event life cycle starts at the scope on which `$emit` was called. All
18382 * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get
18383 * notified. Afterwards, the event traverses upwards toward the root scope and calls all
18384 * registered listeners along the way. The event will stop propagating if one of the listeners
18387 * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
18388 * onto the {@link ng.$exceptionHandler $exceptionHandler} service.
18390 * @param {string} name Event name to emit.
18391 * @param {...*} args Optional one or more arguments which will be passed onto the event listeners.
18392 * @return {Object} Event object (see {@link ng.$rootScope.Scope#$on}).
18394 $emit: function(name, args) {
18398 stopPropagation = false,
18401 targetScope: scope,
18402 stopPropagation: function() {stopPropagation = true;},
18403 preventDefault: function() {
18404 event.defaultPrevented = true;
18406 defaultPrevented: false
18408 listenerArgs = concat([event], arguments, 1),
18412 namedListeners = scope.$$listeners[name] || empty;
18413 event.currentScope = scope;
18414 for (i = 0, length = namedListeners.length; i < length; i++) {
18416 // if listeners were deregistered, defragment the array
18417 if (!namedListeners[i]) {
18418 namedListeners.splice(i, 1);
18424 //allow all listeners attached to the current scope to run
18425 namedListeners[i].apply(null, listenerArgs);
18427 $exceptionHandler(e);
18430 //if any listener on the current scope stops propagation, prevent bubbling
18431 if (stopPropagation) {
18432 event.currentScope = null;
18436 scope = scope.$parent;
18439 event.currentScope = null;
18447 * @name $rootScope.Scope#$broadcast
18451 * Dispatches an event `name` downwards to all child scopes (and their children) notifying the
18452 * registered {@link ng.$rootScope.Scope#$on} listeners.
18454 * The event life cycle starts at the scope on which `$broadcast` was called. All
18455 * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get
18456 * notified. Afterwards, the event propagates to all direct and indirect scopes of the current
18457 * scope and calls all registered listeners along the way. The event cannot be canceled.
18459 * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
18460 * onto the {@link ng.$exceptionHandler $exceptionHandler} service.
18462 * @param {string} name Event name to broadcast.
18463 * @param {...*} args Optional one or more arguments which will be passed onto the event listeners.
18464 * @return {Object} Event object, see {@link ng.$rootScope.Scope#$on}
18466 $broadcast: function(name, args) {
18472 targetScope: target,
18473 preventDefault: function() {
18474 event.defaultPrevented = true;
18476 defaultPrevented: false
18479 if (!target.$$listenerCount[name]) return event;
18481 var listenerArgs = concat([event], arguments, 1),
18482 listeners, i, length;
18484 //down while you can, then up and next sibling or up and next sibling until back at root
18485 while ((current = next)) {
18486 event.currentScope = current;
18487 listeners = current.$$listeners[name] || [];
18488 for (i = 0, length = listeners.length; i < length; i++) {
18489 // if listeners were deregistered, defragment the array
18490 if (!listeners[i]) {
18491 listeners.splice(i, 1);
18498 listeners[i].apply(null, listenerArgs);
18500 $exceptionHandler(e);
18504 // Insanity Warning: scope depth-first traversal
18505 // yes, this code is a bit crazy, but it works and we have tests to prove it!
18506 // this piece should be kept in sync with the traversal in $digest
18507 // (though it differs due to having the extra check for $$listenerCount)
18508 if (!(next = ((current.$$listenerCount[name] && current.$$childHead) ||
18509 (current !== target && current.$$nextSibling)))) {
18510 while (current !== target && !(next = current.$$nextSibling)) {
18511 current = current.$parent;
18516 event.currentScope = null;
18521 var $rootScope = new Scope();
18523 //The internal queues. Expose them on the $rootScope for debugging/testing purposes.
18524 var asyncQueue = $rootScope.$$asyncQueue = [];
18525 var postDigestQueue = $rootScope.$$postDigestQueue = [];
18526 var applyAsyncQueue = $rootScope.$$applyAsyncQueue = [];
18528 var postDigestQueuePosition = 0;
18533 function beginPhase(phase) {
18534 if ($rootScope.$$phase) {
18535 throw $rootScopeMinErr('inprog', '{0} already in progress', $rootScope.$$phase);
18538 $rootScope.$$phase = phase;
18541 function clearPhase() {
18542 $rootScope.$$phase = null;
18545 function incrementWatchersCount(current, count) {
18547 current.$$watchersCount += count;
18548 } while ((current = current.$parent));
18551 function decrementListenerCount(current, count, name) {
18553 current.$$listenerCount[name] -= count;
18555 if (current.$$listenerCount[name] === 0) {
18556 delete current.$$listenerCount[name];
18558 } while ((current = current.$parent));
18562 * function used as an initial value for watchers.
18563 * because it's unique we can easily tell it apart from other values
18565 function initWatchVal() {}
18567 function flushApplyAsync() {
18568 while (applyAsyncQueue.length) {
18570 applyAsyncQueue.shift()();
18572 $exceptionHandler(e);
18575 applyAsyncId = null;
18578 function scheduleApplyAsync() {
18579 if (applyAsyncId === null) {
18580 applyAsyncId = $browser.defer(function() {
18581 $rootScope.$apply(flushApplyAsync);
18590 * @name $rootElement
18593 * The root element of Angular application. This is either the element where {@link
18594 * ng.directive:ngApp ngApp} was declared or the element passed into
18595 * {@link angular.bootstrap}. The element represents the root element of application. It is also the
18596 * location where the application's {@link auto.$injector $injector} service gets
18597 * published, and can be retrieved using `$rootElement.injector()`.
18601 // the implementation is in angular.bootstrap
18606 * Private service to sanitize uris for links and images. Used by $compile and $sanitize.
18608 function $$SanitizeUriProvider() {
18609 var aHrefSanitizationWhitelist = /^\s*(https?|ftp|mailto|tel|file):/,
18610 imgSrcSanitizationWhitelist = /^\s*((https?|ftp|file|blob):|data:image\/)/;
18614 * Retrieves or overrides the default regular expression that is used for whitelisting of safe
18615 * urls during a[href] sanitization.
18617 * The sanitization is a security measure aimed at prevent XSS attacks via html links.
18619 * Any url about to be assigned to a[href] via data-binding is first normalized and turned into
18620 * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist`
18621 * regular expression. If a match is found, the original url is written into the dom. Otherwise,
18622 * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
18624 * @param {RegExp=} regexp New regexp to whitelist urls with.
18625 * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
18626 * chaining otherwise.
18628 this.aHrefSanitizationWhitelist = function(regexp) {
18629 if (isDefined(regexp)) {
18630 aHrefSanitizationWhitelist = regexp;
18633 return aHrefSanitizationWhitelist;
18639 * Retrieves or overrides the default regular expression that is used for whitelisting of safe
18640 * urls during img[src] sanitization.
18642 * The sanitization is a security measure aimed at prevent XSS attacks via html links.
18644 * Any url about to be assigned to img[src] via data-binding is first normalized and turned into
18645 * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist`
18646 * regular expression. If a match is found, the original url is written into the dom. Otherwise,
18647 * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
18649 * @param {RegExp=} regexp New regexp to whitelist urls with.
18650 * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
18651 * chaining otherwise.
18653 this.imgSrcSanitizationWhitelist = function(regexp) {
18654 if (isDefined(regexp)) {
18655 imgSrcSanitizationWhitelist = regexp;
18658 return imgSrcSanitizationWhitelist;
18661 this.$get = function() {
18662 return function sanitizeUri(uri, isImage) {
18663 var regex = isImage ? imgSrcSanitizationWhitelist : aHrefSanitizationWhitelist;
18665 normalizedVal = urlResolve(uri).href;
18666 if (normalizedVal !== '' && !normalizedVal.match(regex)) {
18667 return 'unsafe:' + normalizedVal;
18674 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
18675 * Any commits to this file should be reviewed with security in mind. *
18676 * Changes to this file can potentially create security vulnerabilities. *
18677 * An approval from 2 Core members with history of modifying *
18678 * this file is required. *
18680 * Does the change somehow allow for arbitrary javascript to be executed? *
18681 * Or allows for someone to change the prototype of built-in objects? *
18682 * Or gives undesired access to variables likes document or window? *
18683 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
18685 /* exported $SceProvider, $SceDelegateProvider */
18687 var $sceMinErr = minErr('$sce');
18689 var SCE_CONTEXTS = {
18693 // RESOURCE_URL is a subtype of URL used in contexts where a privileged resource is sourced from a
18694 // url. (e.g. ng-include, script src, templateUrl)
18695 RESOURCE_URL: 'resourceUrl',
18699 // Helper functions follow.
18701 var UNDERSCORE_LOWERCASE_REGEXP = /_([a-z])/g;
18703 function snakeToCamel(name) {
18705 .replace(UNDERSCORE_LOWERCASE_REGEXP, fnCamelCaseReplace);
18708 function adjustMatcher(matcher) {
18709 if (matcher === 'self') {
18711 } else if (isString(matcher)) {
18712 // Strings match exactly except for 2 wildcards - '*' and '**'.
18713 // '*' matches any character except those from the set ':/.?&'.
18714 // '**' matches any character (like .* in a RegExp).
18715 // More than 2 *'s raises an error as it's ill defined.
18716 if (matcher.indexOf('***') > -1) {
18717 throw $sceMinErr('iwcard',
18718 'Illegal sequence *** in string matcher. String: {0}', matcher);
18720 matcher = escapeForRegexp(matcher).
18721 replace(/\\\*\\\*/g, '.*').
18722 replace(/\\\*/g, '[^:/.?&;]*');
18723 return new RegExp('^' + matcher + '$');
18724 } else if (isRegExp(matcher)) {
18725 // The only other type of matcher allowed is a Regexp.
18726 // Match entire URL / disallow partial matches.
18727 // Flags are reset (i.e. no global, ignoreCase or multiline)
18728 return new RegExp('^' + matcher.source + '$');
18730 throw $sceMinErr('imatcher',
18731 'Matchers may only be "self", string patterns or RegExp objects');
18736 function adjustMatchers(matchers) {
18737 var adjustedMatchers = [];
18738 if (isDefined(matchers)) {
18739 forEach(matchers, function(matcher) {
18740 adjustedMatchers.push(adjustMatcher(matcher));
18743 return adjustedMatchers;
18749 * @name $sceDelegate
18754 * `$sceDelegate` is a service that is used by the `$sce` service to provide {@link ng.$sce Strict
18755 * Contextual Escaping (SCE)} services to AngularJS.
18757 * Typically, you would configure or override the {@link ng.$sceDelegate $sceDelegate} instead of
18758 * the `$sce` service to customize the way Strict Contextual Escaping works in AngularJS. This is
18759 * because, while the `$sce` provides numerous shorthand methods, etc., you really only need to
18760 * override 3 core functions (`trustAs`, `getTrusted` and `valueOf`) to replace the way things
18761 * work because `$sce` delegates to `$sceDelegate` for these operations.
18763 * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} to configure this service.
18765 * The default instance of `$sceDelegate` should work out of the box with little pain. While you
18766 * can override it completely to change the behavior of `$sce`, the common case would
18767 * involve configuring the {@link ng.$sceDelegateProvider $sceDelegateProvider} instead by setting
18768 * your own whitelists and blacklists for trusting URLs used for loading AngularJS resources such as
18769 * templates. Refer {@link ng.$sceDelegateProvider#resourceUrlWhitelist
18770 * $sceDelegateProvider.resourceUrlWhitelist} and {@link
18771 * ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist}
18776 * @name $sceDelegateProvider
18781 * The `$sceDelegateProvider` provider allows developers to configure the {@link ng.$sceDelegate
18782 * $sceDelegate} service. This allows one to get/set the whitelists and blacklists used to ensure
18783 * that the URLs used for sourcing Angular templates are safe. Refer {@link
18784 * ng.$sceDelegateProvider#resourceUrlWhitelist $sceDelegateProvider.resourceUrlWhitelist} and
18785 * {@link ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist}
18787 * For the general details about this service in Angular, read the main page for {@link ng.$sce
18788 * Strict Contextual Escaping (SCE)}.
18790 * **Example**: Consider the following case. <a name="example"></a>
18792 * - your app is hosted at url `http://myapp.example.com/`
18793 * - but some of your templates are hosted on other domains you control such as
18794 * `http://srv01.assets.example.com/`, `http://srv02.assets.example.com/`, etc.
18795 * - and you have an open redirect at `http://myapp.example.com/clickThru?...`.
18797 * Here is what a secure configuration for this scenario might look like:
18800 * angular.module('myApp', []).config(function($sceDelegateProvider) {
18801 * $sceDelegateProvider.resourceUrlWhitelist([
18802 * // Allow same origin resource loads.
18804 * // Allow loading from our assets domain. Notice the difference between * and **.
18805 * 'http://srv*.assets.example.com/**'
18808 * // The blacklist overrides the whitelist so the open redirect here is blocked.
18809 * $sceDelegateProvider.resourceUrlBlacklist([
18810 * 'http://myapp.example.com/clickThru**'
18816 function $SceDelegateProvider() {
18817 this.SCE_CONTEXTS = SCE_CONTEXTS;
18819 // Resource URLs can also be trusted by policy.
18820 var resourceUrlWhitelist = ['self'],
18821 resourceUrlBlacklist = [];
18825 * @name $sceDelegateProvider#resourceUrlWhitelist
18828 * @param {Array=} whitelist When provided, replaces the resourceUrlWhitelist with the value
18829 * provided. This must be an array or null. A snapshot of this array is used so further
18830 * changes to the array are ignored.
18832 * Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
18833 * allowed in this array.
18835 * <div class="alert alert-warning">
18836 * **Note:** an empty whitelist array will block all URLs!
18839 * @return {Array} the currently set whitelist array.
18841 * The **default value** when no whitelist has been explicitly set is `['self']` allowing only
18842 * same origin resource requests.
18845 * Sets/Gets the whitelist of trusted resource URLs.
18847 this.resourceUrlWhitelist = function(value) {
18848 if (arguments.length) {
18849 resourceUrlWhitelist = adjustMatchers(value);
18851 return resourceUrlWhitelist;
18856 * @name $sceDelegateProvider#resourceUrlBlacklist
18859 * @param {Array=} blacklist When provided, replaces the resourceUrlBlacklist with the value
18860 * provided. This must be an array or null. A snapshot of this array is used so further
18861 * changes to the array are ignored.
18863 * Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
18864 * allowed in this array.
18866 * The typical usage for the blacklist is to **block
18867 * [open redirects](http://cwe.mitre.org/data/definitions/601.html)** served by your domain as
18868 * these would otherwise be trusted but actually return content from the redirected domain.
18870 * Finally, **the blacklist overrides the whitelist** and has the final say.
18872 * @return {Array} the currently set blacklist array.
18874 * The **default value** when no whitelist has been explicitly set is the empty array (i.e. there
18875 * is no blacklist.)
18878 * Sets/Gets the blacklist of trusted resource URLs.
18881 this.resourceUrlBlacklist = function(value) {
18882 if (arguments.length) {
18883 resourceUrlBlacklist = adjustMatchers(value);
18885 return resourceUrlBlacklist;
18888 this.$get = ['$injector', function($injector) {
18890 var htmlSanitizer = function htmlSanitizer(html) {
18891 throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.');
18894 if ($injector.has('$sanitize')) {
18895 htmlSanitizer = $injector.get('$sanitize');
18899 function matchUrl(matcher, parsedUrl) {
18900 if (matcher === 'self') {
18901 return urlIsSameOrigin(parsedUrl);
18903 // definitely a regex. See adjustMatchers()
18904 return !!matcher.exec(parsedUrl.href);
18908 function isResourceUrlAllowedByPolicy(url) {
18909 var parsedUrl = urlResolve(url.toString());
18910 var i, n, allowed = false;
18911 // Ensure that at least one item from the whitelist allows this url.
18912 for (i = 0, n = resourceUrlWhitelist.length; i < n; i++) {
18913 if (matchUrl(resourceUrlWhitelist[i], parsedUrl)) {
18919 // Ensure that no item from the blacklist blocked this url.
18920 for (i = 0, n = resourceUrlBlacklist.length; i < n; i++) {
18921 if (matchUrl(resourceUrlBlacklist[i], parsedUrl)) {
18930 function generateHolderType(Base) {
18931 var holderType = function TrustedValueHolderType(trustedValue) {
18932 this.$$unwrapTrustedValue = function() {
18933 return trustedValue;
18937 holderType.prototype = new Base();
18939 holderType.prototype.valueOf = function sceValueOf() {
18940 return this.$$unwrapTrustedValue();
18942 holderType.prototype.toString = function sceToString() {
18943 return this.$$unwrapTrustedValue().toString();
18948 var trustedValueHolderBase = generateHolderType(),
18951 byType[SCE_CONTEXTS.HTML] = generateHolderType(trustedValueHolderBase);
18952 byType[SCE_CONTEXTS.CSS] = generateHolderType(trustedValueHolderBase);
18953 byType[SCE_CONTEXTS.URL] = generateHolderType(trustedValueHolderBase);
18954 byType[SCE_CONTEXTS.JS] = generateHolderType(trustedValueHolderBase);
18955 byType[SCE_CONTEXTS.RESOURCE_URL] = generateHolderType(byType[SCE_CONTEXTS.URL]);
18959 * @name $sceDelegate#trustAs
18962 * Returns an object that is trusted by angular for use in specified strict
18963 * contextual escaping contexts (such as ng-bind-html, ng-include, any src
18964 * attribute interpolation, any dom event binding attribute interpolation
18965 * such as for onclick, etc.) that uses the provided value.
18966 * See {@link ng.$sce $sce} for enabling strict contextual escaping.
18968 * @param {string} type The kind of context in which this value is safe for use. e.g. url,
18969 * resourceUrl, html, js and css.
18970 * @param {*} value The value that that should be considered trusted/safe.
18971 * @returns {*} A value that can be used to stand in for the provided `value` in places
18972 * where Angular expects a $sce.trustAs() return value.
18974 function trustAs(type, trustedValue) {
18975 var Constructor = (byType.hasOwnProperty(type) ? byType[type] : null);
18976 if (!Constructor) {
18977 throw $sceMinErr('icontext',
18978 'Attempted to trust a value in invalid context. Context: {0}; Value: {1}',
18979 type, trustedValue);
18981 if (trustedValue === null || isUndefined(trustedValue) || trustedValue === '') {
18982 return trustedValue;
18984 // All the current contexts in SCE_CONTEXTS happen to be strings. In order to avoid trusting
18985 // mutable objects, we ensure here that the value passed in is actually a string.
18986 if (typeof trustedValue !== 'string') {
18987 throw $sceMinErr('itype',
18988 'Attempted to trust a non-string value in a content requiring a string: Context: {0}',
18991 return new Constructor(trustedValue);
18996 * @name $sceDelegate#valueOf
18999 * If the passed parameter had been returned by a prior call to {@link ng.$sceDelegate#trustAs
19000 * `$sceDelegate.trustAs`}, returns the value that had been passed to {@link
19001 * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}.
19003 * If the passed parameter is not a value that had been returned by {@link
19004 * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}, returns it as-is.
19006 * @param {*} value The result of a prior {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}
19007 * call or anything else.
19008 * @returns {*} The `value` that was originally provided to {@link ng.$sceDelegate#trustAs
19009 * `$sceDelegate.trustAs`} if `value` is the result of such a call. Otherwise, returns
19010 * `value` unchanged.
19012 function valueOf(maybeTrusted) {
19013 if (maybeTrusted instanceof trustedValueHolderBase) {
19014 return maybeTrusted.$$unwrapTrustedValue();
19016 return maybeTrusted;
19022 * @name $sceDelegate#getTrusted
19025 * Takes the result of a {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`} call and
19026 * returns the originally supplied value if the queried context type is a supertype of the
19027 * created type. If this condition isn't satisfied, throws an exception.
19029 * <div class="alert alert-danger">
19030 * Disabling auto-escaping is extremely dangerous, it usually creates a Cross Site Scripting
19031 * (XSS) vulnerability in your application.
19034 * @param {string} type The kind of context in which this value is to be used.
19035 * @param {*} maybeTrusted The result of a prior {@link ng.$sceDelegate#trustAs
19036 * `$sceDelegate.trustAs`} call.
19037 * @returns {*} The value the was originally provided to {@link ng.$sceDelegate#trustAs
19038 * `$sceDelegate.trustAs`} if valid in this context. Otherwise, throws an exception.
19040 function getTrusted(type, maybeTrusted) {
19041 if (maybeTrusted === null || isUndefined(maybeTrusted) || maybeTrusted === '') {
19042 return maybeTrusted;
19044 var constructor = (byType.hasOwnProperty(type) ? byType[type] : null);
19045 if (constructor && maybeTrusted instanceof constructor) {
19046 return maybeTrusted.$$unwrapTrustedValue();
19048 // If we get here, then we may only take one of two actions.
19049 // 1. sanitize the value for the requested type, or
19050 // 2. throw an exception.
19051 if (type === SCE_CONTEXTS.RESOURCE_URL) {
19052 if (isResourceUrlAllowedByPolicy(maybeTrusted)) {
19053 return maybeTrusted;
19055 throw $sceMinErr('insecurl',
19056 'Blocked loading resource from url not allowed by $sceDelegate policy. URL: {0}',
19057 maybeTrusted.toString());
19059 } else if (type === SCE_CONTEXTS.HTML) {
19060 return htmlSanitizer(maybeTrusted);
19062 throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.');
19065 return { trustAs: trustAs,
19066 getTrusted: getTrusted,
19067 valueOf: valueOf };
19074 * @name $sceProvider
19079 * The $sceProvider provider allows developers to configure the {@link ng.$sce $sce} service.
19080 * - enable/disable Strict Contextual Escaping (SCE) in a module
19081 * - override the default implementation with a custom delegate
19083 * Read more about {@link ng.$sce Strict Contextual Escaping (SCE)}.
19093 * `$sce` is a service that provides Strict Contextual Escaping services to AngularJS.
19095 * # Strict Contextual Escaping
19097 * Strict Contextual Escaping (SCE) is a mode in which AngularJS requires bindings in certain
19098 * contexts to result in a value that is marked as safe to use for that context. One example of
19099 * such a context is binding arbitrary html controlled by the user via `ng-bind-html`. We refer
19100 * to these contexts as privileged or SCE contexts.
19102 * As of version 1.2, Angular ships with SCE enabled by default.
19104 * Note: When enabled (the default), IE<11 in quirks mode is not supported. In this mode, IE<11 allow
19105 * one to execute arbitrary javascript by the use of the expression() syntax. Refer
19106 * <http://blogs.msdn.com/b/ie/archive/2008/10/16/ending-expressions.aspx> to learn more about them.
19107 * You can ensure your document is in standards mode and not quirks mode by adding `<!doctype html>`
19108 * to the top of your HTML document.
19110 * SCE assists in writing code in a way that (a) is secure by default and (b) makes auditing for
19111 * security vulnerabilities such as XSS, clickjacking, etc. a lot easier.
19113 * Here's an example of a binding in a privileged context:
19116 * <input ng-model="userHtml" aria-label="User input">
19117 * <div ng-bind-html="userHtml"></div>
19120 * Notice that `ng-bind-html` is bound to `userHtml` controlled by the user. With SCE
19121 * disabled, this application allows the user to render arbitrary HTML into the DIV.
19122 * In a more realistic example, one may be rendering user comments, blog articles, etc. via
19123 * bindings. (HTML is just one example of a context where rendering user controlled input creates
19124 * security vulnerabilities.)
19126 * For the case of HTML, you might use a library, either on the client side, or on the server side,
19127 * to sanitize unsafe HTML before binding to the value and rendering it in the document.
19129 * How would you ensure that every place that used these types of bindings was bound to a value that
19130 * was sanitized by your library (or returned as safe for rendering by your server?) How can you
19131 * ensure that you didn't accidentally delete the line that sanitized the value, or renamed some
19132 * properties/fields and forgot to update the binding to the sanitized value?
19134 * To be secure by default, you want to ensure that any such bindings are disallowed unless you can
19135 * determine that something explicitly says it's safe to use a value for binding in that
19136 * context. You can then audit your code (a simple grep would do) to ensure that this is only done
19137 * for those values that you can easily tell are safe - because they were received from your server,
19138 * sanitized by your library, etc. You can organize your codebase to help with this - perhaps
19139 * allowing only the files in a specific directory to do this. Ensuring that the internal API
19140 * exposed by that code doesn't markup arbitrary values as safe then becomes a more manageable task.
19142 * In the case of AngularJS' SCE service, one uses {@link ng.$sce#trustAs $sce.trustAs}
19143 * (and shorthand methods such as {@link ng.$sce#trustAsHtml $sce.trustAsHtml}, etc.) to
19144 * obtain values that will be accepted by SCE / privileged contexts.
19147 * ## How does it work?
19149 * In privileged contexts, directives and code will bind to the result of {@link ng.$sce#getTrusted
19150 * $sce.getTrusted(context, value)} rather than to the value directly. Directives use {@link
19151 * ng.$sce#parseAs $sce.parseAs} rather than `$parse` to watch attribute bindings, which performs the
19152 * {@link ng.$sce#getTrusted $sce.getTrusted} behind the scenes on non-constant literals.
19154 * As an example, {@link ng.directive:ngBindHtml ngBindHtml} uses {@link
19155 * ng.$sce#parseAsHtml $sce.parseAsHtml(binding expression)}. Here's the actual code (slightly
19159 * var ngBindHtmlDirective = ['$sce', function($sce) {
19160 * return function(scope, element, attr) {
19161 * scope.$watch($sce.parseAsHtml(attr.ngBindHtml), function(value) {
19162 * element.html(value || '');
19168 * ## Impact on loading templates
19170 * This applies both to the {@link ng.directive:ngInclude `ng-include`} directive as well as
19171 * `templateUrl`'s specified by {@link guide/directive directives}.
19173 * By default, Angular only loads templates from the same domain and protocol as the application
19174 * document. This is done by calling {@link ng.$sce#getTrustedResourceUrl
19175 * $sce.getTrustedResourceUrl} on the template URL. To load templates from other domains and/or
19176 * protocols, you may either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist
19177 * them} or {@link ng.$sce#trustAsResourceUrl wrap it} into a trusted value.
19181 * [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest)
19182 * and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/)
19183 * policy apply in addition to this and may further restrict whether the template is successfully
19184 * loaded. This means that without the right CORS policy, loading templates from a different domain
19185 * won't work on all browsers. Also, loading templates from `file://` URL does not work on some
19188 * ## This feels like too much overhead
19190 * It's important to remember that SCE only applies to interpolation expressions.
19192 * If your expressions are constant literals, they're automatically trusted and you don't need to
19193 * call `$sce.trustAs` on them (remember to include the `ngSanitize` module) (e.g.
19194 * `<div ng-bind-html="'<b>implicitly trusted</b>'"></div>`) just works.
19196 * Additionally, `a[href]` and `img[src]` automatically sanitize their URLs and do not pass them
19197 * through {@link ng.$sce#getTrusted $sce.getTrusted}. SCE doesn't play a role here.
19199 * The included {@link ng.$sceDelegate $sceDelegate} comes with sane defaults to allow you to load
19200 * templates in `ng-include` from your application's domain without having to even know about SCE.
19201 * It blocks loading templates from other domains or loading templates over http from an https
19202 * served document. You can change these by setting your own custom {@link
19203 * ng.$sceDelegateProvider#resourceUrlWhitelist whitelists} and {@link
19204 * ng.$sceDelegateProvider#resourceUrlBlacklist blacklists} for matching such URLs.
19206 * This significantly reduces the overhead. It is far easier to pay the small overhead and have an
19207 * application that's secure and can be audited to verify that with much more ease than bolting
19208 * security onto an application later.
19210 * <a name="contexts"></a>
19211 * ## What trusted context types are supported?
19213 * | Context | Notes |
19214 * |---------------------|----------------|
19215 * | `$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. |
19216 * | `$sce.CSS` | For CSS that's safe to source into the application. Currently unused. Feel free to use it in your own directives. |
19217 * | `$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. |
19218 * | `$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`, `VIDEO`, `AUDIO`, `SOURCE`, and `TRACK` (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. |
19219 * | `$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. |
19221 * ## Format of items in {@link ng.$sceDelegateProvider#resourceUrlWhitelist resourceUrlWhitelist}/{@link ng.$sceDelegateProvider#resourceUrlBlacklist Blacklist} <a name="resourceUrlPatternItem"></a>
19223 * Each element in these arrays must be one of the following:
19226 * - The special **string**, `'self'`, can be used to match against all URLs of the **same
19227 * domain** as the application document using the **same protocol**.
19228 * - **String** (except the special value `'self'`)
19229 * - The string is matched against the full *normalized / absolute URL* of the resource
19230 * being tested (substring matches are not good enough.)
19231 * - There are exactly **two wildcard sequences** - `*` and `**`. All other characters
19232 * match themselves.
19233 * - `*`: matches zero or more occurrences of any character other than one of the following 6
19234 * characters: '`:`', '`/`', '`.`', '`?`', '`&`' and '`;`'. It's a useful wildcard for use
19236 * - `**`: matches zero or more occurrences of *any* character. As such, it's not
19237 * appropriate for use in a scheme, domain, etc. as it would match too much. (e.g.
19238 * http://**.example.com/ would match http://evil.com/?ignore=.example.com/ and that might
19239 * not have been the intention.) Its usage at the very end of the path is ok. (e.g.
19240 * http://foo.example.com/templates/**).
19241 * - **RegExp** (*see caveat below*)
19242 * - *Caveat*: While regular expressions are powerful and offer great flexibility, their syntax
19243 * (and all the inevitable escaping) makes them *harder to maintain*. It's easy to
19244 * accidentally introduce a bug when one updates a complex expression (imho, all regexes should
19245 * have good test coverage). For instance, the use of `.` in the regex is correct only in a
19246 * small number of cases. A `.` character in the regex used when matching the scheme or a
19247 * subdomain could be matched against a `:` or literal `.` that was likely not intended. It
19248 * is highly recommended to use the string patterns and only fall back to regular expressions
19249 * as a last resort.
19250 * - The regular expression must be an instance of RegExp (i.e. not a string.) It is
19251 * matched against the **entire** *normalized / absolute URL* of the resource being tested
19252 * (even when the RegExp did not have the `^` and `$` codes.) In addition, any flags
19253 * present on the RegExp (such as multiline, global, ignoreCase) are ignored.
19254 * - If you are generating your JavaScript from some other templating engine (not
19255 * recommended, e.g. in issue [#4006](https://github.com/angular/angular.js/issues/4006)),
19256 * remember to escape your regular expression (and be aware that you might need more than
19257 * one level of escaping depending on your templating engine and the way you interpolated
19258 * the value.) Do make use of your platform's escaping mechanism as it might be good
19259 * enough before coding your own. E.g. Ruby has
19260 * [Regexp.escape(str)](http://www.ruby-doc.org/core-2.0.0/Regexp.html#method-c-escape)
19261 * and Python has [re.escape](http://docs.python.org/library/re.html#re.escape).
19262 * Javascript lacks a similar built in function for escaping. Take a look at Google
19263 * Closure library's [goog.string.regExpEscape(s)](
19264 * http://docs.closure-library.googlecode.com/git/closure_goog_string_string.js.source.html#line962).
19266 * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} for an example.
19268 * ## Show me an example using SCE.
19270 * <example module="mySceApp" deps="angular-sanitize.js" name="sce-service">
19271 * <file name="index.html">
19272 * <div ng-controller="AppController as myCtrl">
19273 * <i ng-bind-html="myCtrl.explicitlyTrustedHtml" id="explicitlyTrustedHtml"></i><br><br>
19274 * <b>User comments</b><br>
19275 * By default, HTML that isn't explicitly trusted (e.g. Alice's comment) is sanitized when
19276 * $sanitize is available. If $sanitize isn't available, this results in an error instead of an
19278 * <div class="well">
19279 * <div ng-repeat="userComment in myCtrl.userComments">
19280 * <b>{{userComment.name}}</b>:
19281 * <span ng-bind-html="userComment.htmlComment" class="htmlComment"></span>
19288 * <file name="script.js">
19289 * angular.module('mySceApp', ['ngSanitize'])
19290 * .controller('AppController', ['$http', '$templateCache', '$sce',
19291 * function AppController($http, $templateCache, $sce) {
19293 * $http.get('test_data.json', {cache: $templateCache}).then(function(response) {
19294 * self.userComments = response.data;
19296 * self.explicitlyTrustedHtml = $sce.trustAsHtml(
19297 * '<span onmouseover="this.textContent="Explicitly trusted HTML bypasses ' +
19298 * 'sanitization."">Hover over this text.</span>');
19302 * <file name="test_data.json">
19304 * { "name": "Alice",
19306 * "<span onmouseover='this.textContent=\"PWN3D!\"'>Is <i>anyone</i> reading this?</span>"
19309 * "htmlComment": "<i>Yes!</i> Am I the only other one?"
19314 * <file name="protractor.js" type="protractor">
19315 * describe('SCE doc demo', function() {
19316 * it('should sanitize untrusted values', function() {
19317 * expect(element.all(by.css('.htmlComment')).first().getAttribute('innerHTML'))
19318 * .toBe('<span>Is <i>anyone</i> reading this?</span>');
19321 * it('should NOT sanitize explicitly trusted values', function() {
19322 * expect(element(by.id('explicitlyTrustedHtml')).getAttribute('innerHTML')).toBe(
19323 * '<span onmouseover="this.textContent="Explicitly trusted HTML bypasses ' +
19324 * 'sanitization."">Hover over this text.</span>');
19332 * ## Can I disable SCE completely?
19334 * Yes, you can. However, this is strongly discouraged. SCE gives you a lot of security benefits
19335 * for little coding overhead. It will be much harder to take an SCE disabled application and
19336 * either secure it on your own or enable SCE at a later stage. It might make sense to disable SCE
19337 * for cases where you have a lot of existing code that was written before SCE was introduced and
19338 * you're migrating them a module at a time.
19340 * That said, here's how you can completely disable SCE:
19343 * angular.module('myAppWithSceDisabledmyApp', []).config(function($sceProvider) {
19344 * // Completely disable SCE. For demonstration purposes only!
19345 * // Do not use in new projects.
19346 * $sceProvider.enabled(false);
19352 function $SceProvider() {
19353 var enabled = true;
19357 * @name $sceProvider#enabled
19360 * @param {boolean=} value If provided, then enables/disables SCE.
19361 * @return {boolean} true if SCE is enabled, false otherwise.
19364 * Enables/disables SCE and returns the current value.
19366 this.enabled = function(value) {
19367 if (arguments.length) {
19374 /* Design notes on the default implementation for SCE.
19376 * The API contract for the SCE delegate
19377 * -------------------------------------
19378 * The SCE delegate object must provide the following 3 methods:
19380 * - trustAs(contextEnum, value)
19381 * This method is used to tell the SCE service that the provided value is OK to use in the
19382 * contexts specified by contextEnum. It must return an object that will be accepted by
19383 * getTrusted() for a compatible contextEnum and return this value.
19386 * For values that were not produced by trustAs(), return them as is. For values that were
19387 * produced by trustAs(), return the corresponding input value to trustAs. Basically, if
19388 * trustAs is wrapping the given values into some type, this operation unwraps it when given
19391 * - getTrusted(contextEnum, value)
19392 * This function should return the a value that is safe to use in the context specified by
19393 * contextEnum or throw and exception otherwise.
19395 * NOTE: This contract deliberately does NOT state that values returned by trustAs() must be
19396 * opaque or wrapped in some holder object. That happens to be an implementation detail. For
19397 * instance, an implementation could maintain a registry of all trusted objects by context. In
19398 * such a case, trustAs() would return the same object that was passed in. getTrusted() would
19399 * return the same object passed in if it was found in the registry under a compatible context or
19400 * throw an exception otherwise. An implementation might only wrap values some of the time based
19401 * on some criteria. getTrusted() might return a value and not throw an exception for special
19402 * constants or objects even if not wrapped. All such implementations fulfill this contract.
19405 * A note on the inheritance model for SCE contexts
19406 * ------------------------------------------------
19407 * I've used inheritance and made RESOURCE_URL wrapped types a subtype of URL wrapped types. This
19408 * is purely an implementation details.
19410 * The contract is simply this:
19412 * getTrusted($sce.RESOURCE_URL, value) succeeding implies that getTrusted($sce.URL, value)
19413 * will also succeed.
19415 * Inheritance happens to capture this in a natural way. In some future, we
19416 * may not use inheritance anymore. That is OK because no code outside of
19417 * sce.js and sceSpecs.js would need to be aware of this detail.
19420 this.$get = ['$parse', '$sceDelegate', function(
19421 $parse, $sceDelegate) {
19422 // Support: IE 9-11 only
19423 // Prereq: Ensure that we're not running in IE<11 quirks mode. In that mode, IE < 11 allow
19424 // the "expression(javascript expression)" syntax which is insecure.
19425 if (enabled && msie < 8) {
19426 throw $sceMinErr('iequirks',
19427 'Strict Contextual Escaping does not support Internet Explorer version < 11 in quirks ' +
19428 'mode. You can fix this by adding the text <!doctype html> to the top of your HTML ' +
19429 'document. See http://docs.angularjs.org/api/ng.$sce for more information.');
19432 var sce = shallowCopy(SCE_CONTEXTS);
19436 * @name $sce#isEnabled
19439 * @return {Boolean} true if SCE is enabled, false otherwise. If you want to set the value, you
19440 * have to do it at module config time on {@link ng.$sceProvider $sceProvider}.
19443 * Returns a boolean indicating if SCE is enabled.
19445 sce.isEnabled = function() {
19448 sce.trustAs = $sceDelegate.trustAs;
19449 sce.getTrusted = $sceDelegate.getTrusted;
19450 sce.valueOf = $sceDelegate.valueOf;
19453 sce.trustAs = sce.getTrusted = function(type, value) { return value; };
19454 sce.valueOf = identity;
19459 * @name $sce#parseAs
19462 * Converts Angular {@link guide/expression expression} into a function. This is like {@link
19463 * ng.$parse $parse} and is identical when the expression is a literal constant. Otherwise, it
19464 * wraps the expression in a call to {@link ng.$sce#getTrusted $sce.getTrusted(*type*,
19467 * @param {string} type The kind of SCE context in which this result will be used.
19468 * @param {string} expression String expression to compile.
19469 * @returns {function(context, locals)} a function which represents the compiled expression:
19471 * * `context` – `{object}` – an object against which any expressions embedded in the strings
19472 * are evaluated against (typically a scope object).
19473 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
19476 sce.parseAs = function sceParseAs(type, expr) {
19477 var parsed = $parse(expr);
19478 if (parsed.literal && parsed.constant) {
19481 return $parse(expr, function(value) {
19482 return sce.getTrusted(type, value);
19489 * @name $sce#trustAs
19492 * Delegates to {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}. As such,
19493 * returns an object that is trusted by angular for use in specified strict contextual
19494 * escaping contexts (such as ng-bind-html, ng-include, any src attribute
19495 * interpolation, any dom event binding attribute interpolation such as for onclick, etc.)
19496 * that uses the provided value. See * {@link ng.$sce $sce} for enabling strict contextual
19499 * @param {string} type The kind of context in which this value is safe for use. e.g. url,
19500 * resourceUrl, html, js and css.
19501 * @param {*} value The value that that should be considered trusted/safe.
19502 * @returns {*} A value that can be used to stand in for the provided `value` in places
19503 * where Angular expects a $sce.trustAs() return value.
19508 * @name $sce#trustAsHtml
19511 * Shorthand method. `$sce.trustAsHtml(value)` →
19512 * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.HTML, value)`}
19514 * @param {*} value The value to trustAs.
19515 * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedHtml
19516 * $sce.getTrustedHtml(value)} to obtain the original value. (privileged directives
19517 * only accept expressions that are either literal constants or are the
19518 * return value of {@link ng.$sce#trustAs $sce.trustAs}.)
19523 * @name $sce#trustAsUrl
19526 * Shorthand method. `$sce.trustAsUrl(value)` →
19527 * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.URL, value)`}
19529 * @param {*} value The value to trustAs.
19530 * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedUrl
19531 * $sce.getTrustedUrl(value)} to obtain the original value. (privileged directives
19532 * only accept expressions that are either literal constants or are the
19533 * return value of {@link ng.$sce#trustAs $sce.trustAs}.)
19538 * @name $sce#trustAsResourceUrl
19541 * Shorthand method. `$sce.trustAsResourceUrl(value)` →
19542 * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.RESOURCE_URL, value)`}
19544 * @param {*} value The value to trustAs.
19545 * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedResourceUrl
19546 * $sce.getTrustedResourceUrl(value)} to obtain the original value. (privileged directives
19547 * only accept expressions that are either literal constants or are the return
19548 * value of {@link ng.$sce#trustAs $sce.trustAs}.)
19553 * @name $sce#trustAsJs
19556 * Shorthand method. `$sce.trustAsJs(value)` →
19557 * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.JS, value)`}
19559 * @param {*} value The value to trustAs.
19560 * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedJs
19561 * $sce.getTrustedJs(value)} to obtain the original value. (privileged directives
19562 * only accept expressions that are either literal constants or are the
19563 * return value of {@link ng.$sce#trustAs $sce.trustAs}.)
19568 * @name $sce#getTrusted
19571 * Delegates to {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted`}. As such,
19572 * takes the result of a {@link ng.$sce#trustAs `$sce.trustAs`}() call and returns the
19573 * originally supplied value if the queried context type is a supertype of the created type.
19574 * If this condition isn't satisfied, throws an exception.
19576 * @param {string} type The kind of context in which this value is to be used.
19577 * @param {*} maybeTrusted The result of a prior {@link ng.$sce#trustAs `$sce.trustAs`}
19579 * @returns {*} The value the was originally provided to
19580 * {@link ng.$sce#trustAs `$sce.trustAs`} if valid in this context.
19581 * Otherwise, throws an exception.
19586 * @name $sce#getTrustedHtml
19589 * Shorthand method. `$sce.getTrustedHtml(value)` →
19590 * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.HTML, value)`}
19592 * @param {*} value The value to pass to `$sce.getTrusted`.
19593 * @returns {*} The return value of `$sce.getTrusted($sce.HTML, value)`
19598 * @name $sce#getTrustedCss
19601 * Shorthand method. `$sce.getTrustedCss(value)` →
19602 * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.CSS, value)`}
19604 * @param {*} value The value to pass to `$sce.getTrusted`.
19605 * @returns {*} The return value of `$sce.getTrusted($sce.CSS, value)`
19610 * @name $sce#getTrustedUrl
19613 * Shorthand method. `$sce.getTrustedUrl(value)` →
19614 * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.URL, value)`}
19616 * @param {*} value The value to pass to `$sce.getTrusted`.
19617 * @returns {*} The return value of `$sce.getTrusted($sce.URL, value)`
19622 * @name $sce#getTrustedResourceUrl
19625 * Shorthand method. `$sce.getTrustedResourceUrl(value)` →
19626 * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.RESOURCE_URL, value)`}
19628 * @param {*} value The value to pass to `$sceDelegate.getTrusted`.
19629 * @returns {*} The return value of `$sce.getTrusted($sce.RESOURCE_URL, value)`
19634 * @name $sce#getTrustedJs
19637 * Shorthand method. `$sce.getTrustedJs(value)` →
19638 * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.JS, value)`}
19640 * @param {*} value The value to pass to `$sce.getTrusted`.
19641 * @returns {*} The return value of `$sce.getTrusted($sce.JS, value)`
19646 * @name $sce#parseAsHtml
19649 * Shorthand method. `$sce.parseAsHtml(expression string)` →
19650 * {@link ng.$sce#parseAs `$sce.parseAs($sce.HTML, value)`}
19652 * @param {string} expression String expression to compile.
19653 * @returns {function(context, locals)} a function which represents the compiled expression:
19655 * * `context` – `{object}` – an object against which any expressions embedded in the strings
19656 * are evaluated against (typically a scope object).
19657 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
19663 * @name $sce#parseAsCss
19666 * Shorthand method. `$sce.parseAsCss(value)` →
19667 * {@link ng.$sce#parseAs `$sce.parseAs($sce.CSS, value)`}
19669 * @param {string} expression String expression to compile.
19670 * @returns {function(context, locals)} a function which represents the compiled expression:
19672 * * `context` – `{object}` – an object against which any expressions embedded in the strings
19673 * are evaluated against (typically a scope object).
19674 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
19680 * @name $sce#parseAsUrl
19683 * Shorthand method. `$sce.parseAsUrl(value)` →
19684 * {@link ng.$sce#parseAs `$sce.parseAs($sce.URL, value)`}
19686 * @param {string} expression String expression to compile.
19687 * @returns {function(context, locals)} a function which represents the compiled expression:
19689 * * `context` – `{object}` – an object against which any expressions embedded in the strings
19690 * are evaluated against (typically a scope object).
19691 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
19697 * @name $sce#parseAsResourceUrl
19700 * Shorthand method. `$sce.parseAsResourceUrl(value)` →
19701 * {@link ng.$sce#parseAs `$sce.parseAs($sce.RESOURCE_URL, value)`}
19703 * @param {string} expression String expression to compile.
19704 * @returns {function(context, locals)} a function which represents the compiled expression:
19706 * * `context` – `{object}` – an object against which any expressions embedded in the strings
19707 * are evaluated against (typically a scope object).
19708 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
19714 * @name $sce#parseAsJs
19717 * Shorthand method. `$sce.parseAsJs(value)` →
19718 * {@link ng.$sce#parseAs `$sce.parseAs($sce.JS, value)`}
19720 * @param {string} expression String expression to compile.
19721 * @returns {function(context, locals)} a function which represents the compiled expression:
19723 * * `context` – `{object}` – an object against which any expressions embedded in the strings
19724 * are evaluated against (typically a scope object).
19725 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
19729 // Shorthand delegations.
19730 var parse = sce.parseAs,
19731 getTrusted = sce.getTrusted,
19732 trustAs = sce.trustAs;
19734 forEach(SCE_CONTEXTS, function(enumValue, name) {
19735 var lName = lowercase(name);
19736 sce[snakeToCamel('parse_as_' + lName)] = function(expr) {
19737 return parse(enumValue, expr);
19739 sce[snakeToCamel('get_trusted_' + lName)] = function(value) {
19740 return getTrusted(enumValue, value);
19742 sce[snakeToCamel('trust_as_' + lName)] = function(value) {
19743 return trustAs(enumValue, value);
19751 /* exported $SnifferProvider */
19754 * !!! This is an undocumented "private" service !!!
19757 * @requires $window
19758 * @requires $document
19761 * @property {boolean} history Does the browser support html5 history api ?
19762 * @property {boolean} transitions Does the browser support CSS transition events ?
19763 * @property {boolean} animations Does the browser support CSS animation events ?
19766 * This is very simple implementation of testing browser's features.
19768 function $SnifferProvider() {
19769 this.$get = ['$window', '$document', function($window, $document) {
19770 var eventSupport = {},
19771 // Chrome Packaged Apps are not allowed to access `history.pushState`.
19772 // If not sandboxed, they can be detected by the presence of `chrome.app.runtime`
19773 // (see https://developer.chrome.com/apps/api_index). If sandboxed, they can be detected by
19774 // the presence of an extension runtime ID and the absence of other Chrome runtime APIs
19775 // (see https://developer.chrome.com/apps/manifest/sandbox).
19776 // (NW.js apps have access to Chrome APIs, but do support `history`.)
19777 isNw = $window.nw && $window.nw.process,
19778 isChromePackagedApp =
19781 ($window.chrome.app && $window.chrome.app.runtime ||
19782 !$window.chrome.app && $window.chrome.runtime && $window.chrome.runtime.id),
19783 hasHistoryPushState = !isChromePackagedApp && $window.history && $window.history.pushState,
19785 toInt((/android (\d+)/.exec(lowercase(($window.navigator || {}).userAgent)) || [])[1]),
19786 boxee = /Boxee/i.test(($window.navigator || {}).userAgent),
19787 document = $document[0] || {},
19788 bodyStyle = document.body && document.body.style,
19789 transitions = false,
19790 animations = false;
19793 // Support: Android <5, Blackberry Browser 10, default Chrome in Android 4.4.x
19794 // Mentioned browsers need a -webkit- prefix for transitions & animations.
19795 transitions = !!('transition' in bodyStyle || 'webkitTransition' in bodyStyle);
19796 animations = !!('animation' in bodyStyle || 'webkitAnimation' in bodyStyle);
19801 // Android has history.pushState, but it does not update location correctly
19802 // so let's not use the history API at all.
19803 // http://code.google.com/p/android/issues/detail?id=17471
19804 // https://github.com/angular/angular.js/issues/904
19806 // older webkit browser (533.9) on Boxee box has exactly the same problem as Android has
19807 // so let's not use the history API also
19808 // We are purposefully using `!(android < 4)` to cover the case when `android` is undefined
19809 history: !!(hasHistoryPushState && !(android < 4) && !boxee),
19810 hasEvent: function(event) {
19811 // Support: IE 9-11 only
19812 // IE9 implements 'input' event it's so fubared that we rather pretend that it doesn't have
19813 // it. In particular the event is not fired when backspace or delete key are pressed or
19814 // when cut operation is performed.
19815 // IE10+ implements 'input' event but it erroneously fires under various situations,
19816 // e.g. when placeholder changes, or a form is focused.
19817 if (event === 'input' && msie) return false;
19819 if (isUndefined(eventSupport[event])) {
19820 var divElm = document.createElement('div');
19821 eventSupport[event] = 'on' + event in divElm;
19824 return eventSupport[event];
19827 transitions: transitions,
19828 animations: animations,
19834 var $templateRequestMinErr = minErr('$compile');
19838 * @name $templateRequestProvider
19842 * Used to configure the options passed to the {@link $http} service when making a template request.
19844 * For example, it can be used for specifying the "Accept" header that is sent to the server, when
19845 * requesting a template.
19847 function $TemplateRequestProvider() {
19853 * @name $templateRequestProvider#httpOptions
19855 * The options to be passed to the {@link $http} service when making the request.
19856 * You can use this to override options such as the "Accept" header for template requests.
19858 * The {@link $templateRequest} will set the `cache` and the `transformResponse` properties of the
19859 * options if not overridden here.
19861 * @param {string=} value new value for the {@link $http} options.
19862 * @returns {string|self} Returns the {@link $http} options when used as getter and self if used as setter.
19864 this.httpOptions = function(val) {
19869 return httpOptions;
19874 * @name $templateRequest
19877 * The `$templateRequest` service runs security checks then downloads the provided template using
19878 * `$http` and, upon success, stores the contents inside of `$templateCache`. If the HTTP request
19879 * fails or the response data of the HTTP request is empty, a `$compile` error will be thrown (the
19880 * exception can be thwarted by setting the 2nd parameter of the function to true). Note that the
19881 * contents of `$templateCache` are trusted, so the call to `$sce.getTrustedUrl(tpl)` is omitted
19882 * when `tpl` is of type string and `$templateCache` has the matching entry.
19884 * If you want to pass custom options to the `$http` service, such as setting the Accept header you
19885 * can configure this via {@link $templateRequestProvider#httpOptions}.
19887 * @param {string|TrustedResourceUrl} tpl The HTTP request template URL
19888 * @param {boolean=} ignoreRequestError Whether or not to ignore the exception when the request fails or the template is empty
19890 * @return {Promise} a promise for the HTTP response data of the given URL.
19892 * @property {number} totalPendingRequests total amount of pending template requests being downloaded.
19894 this.$get = ['$exceptionHandler', '$templateCache', '$http', '$q', '$sce',
19895 function($exceptionHandler, $templateCache, $http, $q, $sce) {
19897 function handleRequestFn(tpl, ignoreRequestError) {
19898 handleRequestFn.totalPendingRequests++;
19900 // We consider the template cache holds only trusted templates, so
19901 // there's no need to go through whitelisting again for keys that already
19902 // are included in there. This also makes Angular accept any script
19903 // directive, no matter its name. However, we still need to unwrap trusted
19905 if (!isString(tpl) || isUndefined($templateCache.get(tpl))) {
19906 tpl = $sce.getTrustedResourceUrl(tpl);
19909 var transformResponse = $http.defaults && $http.defaults.transformResponse;
19911 if (isArray(transformResponse)) {
19912 transformResponse = transformResponse.filter(function(transformer) {
19913 return transformer !== defaultHttpResponseTransform;
19915 } else if (transformResponse === defaultHttpResponseTransform) {
19916 transformResponse = null;
19919 return $http.get(tpl, extend({
19920 cache: $templateCache,
19921 transformResponse: transformResponse
19923 .finally(function() {
19924 handleRequestFn.totalPendingRequests--;
19926 .then(function(response) {
19927 $templateCache.put(tpl, response.data);
19928 return response.data;
19931 function handleError(resp) {
19932 if (!ignoreRequestError) {
19933 resp = $templateRequestMinErr('tpload',
19934 'Failed to load template: {0} (HTTP status: {1} {2})',
19935 tpl, resp.status, resp.statusText);
19937 $exceptionHandler(resp);
19940 return $q.reject(resp);
19944 handleRequestFn.totalPendingRequests = 0;
19946 return handleRequestFn;
19952 function $$TestabilityProvider() {
19953 this.$get = ['$rootScope', '$browser', '$location',
19954 function($rootScope, $browser, $location) {
19957 * @name $testability
19960 * The private $$testability service provides a collection of methods for use when debugging
19961 * or by automated test and debugging tools.
19963 var testability = {};
19966 * @name $$testability#findBindings
19969 * Returns an array of elements that are bound (via ng-bind or {{}})
19970 * to expressions matching the input.
19972 * @param {Element} element The element root to search from.
19973 * @param {string} expression The binding expression to match.
19974 * @param {boolean} opt_exactMatch If true, only returns exact matches
19975 * for the expression. Filters and whitespace are ignored.
19977 testability.findBindings = function(element, expression, opt_exactMatch) {
19978 var bindings = element.getElementsByClassName('ng-binding');
19980 forEach(bindings, function(binding) {
19981 var dataBinding = angular.element(binding).data('$binding');
19983 forEach(dataBinding, function(bindingName) {
19984 if (opt_exactMatch) {
19985 var matcher = new RegExp('(^|\\s)' + escapeForRegexp(expression) + '(\\s|\\||$)');
19986 if (matcher.test(bindingName)) {
19987 matches.push(binding);
19990 if (bindingName.indexOf(expression) !== -1) {
19991 matches.push(binding);
20001 * @name $$testability#findModels
20004 * Returns an array of elements that are two-way found via ng-model to
20005 * expressions matching the input.
20007 * @param {Element} element The element root to search from.
20008 * @param {string} expression The model expression to match.
20009 * @param {boolean} opt_exactMatch If true, only returns exact matches
20010 * for the expression.
20012 testability.findModels = function(element, expression, opt_exactMatch) {
20013 var prefixes = ['ng-', 'data-ng-', 'ng\\:'];
20014 for (var p = 0; p < prefixes.length; ++p) {
20015 var attributeEquals = opt_exactMatch ? '=' : '*=';
20016 var selector = '[' + prefixes[p] + 'model' + attributeEquals + '"' + expression + '"]';
20017 var elements = element.querySelectorAll(selector);
20018 if (elements.length) {
20025 * @name $$testability#getLocation
20028 * Shortcut for getting the location in a browser agnostic way. Returns
20029 * the path, search, and hash. (e.g. /path?a=b#hash)
20031 testability.getLocation = function() {
20032 return $location.url();
20036 * @name $$testability#setLocation
20039 * Shortcut for navigating to a location without doing a full page reload.
20041 * @param {string} url The location url (path, search and hash,
20042 * e.g. /path?a=b#hash) to go to.
20044 testability.setLocation = function(url) {
20045 if (url !== $location.url()) {
20046 $location.url(url);
20047 $rootScope.$digest();
20052 * @name $$testability#whenStable
20055 * Calls the callback when $timeout and $http requests are completed.
20057 * @param {function} callback
20059 testability.whenStable = function(callback) {
20060 $browser.notifyWhenNoOutstandingRequests(callback);
20063 return testability;
20068 function $TimeoutProvider() {
20069 this.$get = ['$rootScope', '$browser', '$q', '$$q', '$exceptionHandler',
20070 function($rootScope, $browser, $q, $$q, $exceptionHandler) {
20072 var deferreds = {};
20080 * Angular's wrapper for `window.setTimeout`. The `fn` function is wrapped into a try/catch
20081 * block and delegates any exceptions to
20082 * {@link ng.$exceptionHandler $exceptionHandler} service.
20084 * The return value of calling `$timeout` is a promise, which will be resolved when
20085 * the delay has passed and the timeout function, if provided, is executed.
20087 * To cancel a timeout request, call `$timeout.cancel(promise)`.
20089 * In tests you can use {@link ngMock.$timeout `$timeout.flush()`} to
20090 * synchronously flush the queue of deferred functions.
20092 * If you only want a promise that will be resolved after some specified delay
20093 * then you can call `$timeout` without the `fn` function.
20095 * @param {function()=} fn A function, whose execution should be delayed.
20096 * @param {number=} [delay=0] Delay in milliseconds.
20097 * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
20098 * will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
20099 * @param {...*=} Pass additional parameters to the executed function.
20100 * @returns {Promise} Promise that will be resolved when the timeout is reached. The promise
20101 * will be resolved with the return value of the `fn` function.
20104 function timeout(fn, delay, invokeApply) {
20105 if (!isFunction(fn)) {
20106 invokeApply = delay;
20111 var args = sliceArgs(arguments, 3),
20112 skipApply = (isDefined(invokeApply) && !invokeApply),
20113 deferred = (skipApply ? $$q : $q).defer(),
20114 promise = deferred.promise,
20117 timeoutId = $browser.defer(function() {
20119 deferred.resolve(fn.apply(null, args));
20121 deferred.reject(e);
20122 $exceptionHandler(e);
20124 delete deferreds[promise.$$timeoutId];
20127 if (!skipApply) $rootScope.$apply();
20130 promise.$$timeoutId = timeoutId;
20131 deferreds[timeoutId] = deferred;
20139 * @name $timeout#cancel
20142 * Cancels a task associated with the `promise`. As a result of this, the promise will be
20143 * resolved with a rejection.
20145 * @param {Promise=} promise Promise returned by the `$timeout` function.
20146 * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully
20149 timeout.cancel = function(promise) {
20150 if (promise && promise.$$timeoutId in deferreds) {
20151 // Timeout cancels should not report an unhandled promise.
20152 deferreds[promise.$$timeoutId].promise.catch(noop);
20153 deferreds[promise.$$timeoutId].reject('canceled');
20154 delete deferreds[promise.$$timeoutId];
20155 return $browser.defer.cancel(promise.$$timeoutId);
20164 // NOTE: The usage of window and document instead of $window and $document here is
20165 // deliberate. This service depends on the specific behavior of anchor nodes created by the
20166 // browser (resolving and parsing URLs) that is unlikely to be provided by mock objects and
20167 // cause us to break tests. In addition, when the browser resolves a URL for XHR, it
20168 // doesn't know about mocked locations and resolves URLs to the real document - which is
20169 // exactly the behavior needed here. There is little value is mocking these out for this
20171 var urlParsingNode = window.document.createElement('a');
20172 var originUrl = urlResolve(window.location.href);
20177 * Implementation Notes for non-IE browsers
20178 * ----------------------------------------
20179 * Assigning a URL to the href property of an anchor DOM node, even one attached to the DOM,
20180 * results both in the normalizing and parsing of the URL. Normalizing means that a relative
20181 * URL will be resolved into an absolute URL in the context of the application document.
20182 * Parsing means that the anchor node's host, hostname, protocol, port, pathname and related
20183 * properties are all populated to reflect the normalized URL. This approach has wide
20184 * compatibility - Safari 1+, Mozilla 1+ etc. See
20185 * http://www.aptana.com/reference/html/api/HTMLAnchorElement.html
20187 * Implementation Notes for IE
20188 * ---------------------------
20189 * IE <= 10 normalizes the URL when assigned to the anchor node similar to the other
20190 * browsers. However, the parsed components will not be set if the URL assigned did not specify
20191 * them. (e.g. if you assign a.href = "foo", then a.protocol, a.host, etc. will be empty.) We
20192 * work around that by performing the parsing in a 2nd step by taking a previously normalized
20193 * URL (e.g. by assigning to a.href) and assigning it a.href again. This correctly populates the
20194 * properties such as protocol, hostname, port, etc.
20197 * http://developer.mozilla.org/en-US/docs/Web/API/HTMLAnchorElement
20198 * http://www.aptana.com/reference/html/api/HTMLAnchorElement.html
20199 * http://url.spec.whatwg.org/#urlutils
20200 * https://github.com/angular/angular.js/pull/2902
20201 * http://james.padolsey.com/javascript/parsing-urls-with-the-dom/
20204 * @param {string} url The URL to be parsed.
20205 * @description Normalizes and parses a URL.
20206 * @returns {object} Returns the normalized URL as a dictionary.
20208 * | member name | Description |
20209 * |---------------|----------------|
20210 * | href | A normalized version of the provided URL if it was not an absolute URL |
20211 * | protocol | The protocol including the trailing colon |
20212 * | host | The host and port (if the port is non-default) of the normalizedUrl |
20213 * | search | The search params, minus the question mark |
20214 * | hash | The hash string, minus the hash symbol
20215 * | hostname | The hostname
20216 * | port | The port, without ":"
20217 * | pathname | The pathname, beginning with "/"
20220 function urlResolve(url) {
20223 // Support: IE 9-11 only
20225 // Normalize before parse. Refer Implementation Notes on why this is
20226 // done in two steps on IE.
20227 urlParsingNode.setAttribute('href', href);
20228 href = urlParsingNode.href;
20231 urlParsingNode.setAttribute('href', href);
20233 // urlParsingNode provides the UrlUtils interface - http://url.spec.whatwg.org/#urlutils
20235 href: urlParsingNode.href,
20236 protocol: urlParsingNode.protocol ? urlParsingNode.protocol.replace(/:$/, '') : '',
20237 host: urlParsingNode.host,
20238 search: urlParsingNode.search ? urlParsingNode.search.replace(/^\?/, '') : '',
20239 hash: urlParsingNode.hash ? urlParsingNode.hash.replace(/^#/, '') : '',
20240 hostname: urlParsingNode.hostname,
20241 port: urlParsingNode.port,
20242 pathname: (urlParsingNode.pathname.charAt(0) === '/')
20243 ? urlParsingNode.pathname
20244 : '/' + urlParsingNode.pathname
20249 * Parse a request URL and determine whether this is a same-origin request as the application document.
20251 * @param {string|object} requestUrl The url of the request as a string that will be resolved
20252 * or a parsed URL object.
20253 * @returns {boolean} Whether the request is for the same origin as the application document.
20255 function urlIsSameOrigin(requestUrl) {
20256 var parsed = (isString(requestUrl)) ? urlResolve(requestUrl) : requestUrl;
20257 return (parsed.protocol === originUrl.protocol &&
20258 parsed.host === originUrl.host);
20267 * A reference to the browser's `window` object. While `window`
20268 * is globally available in JavaScript, it causes testability problems, because
20269 * it is a global variable. In angular we always refer to it through the
20270 * `$window` service, so it may be overridden, removed or mocked for testing.
20272 * Expressions, like the one defined for the `ngClick` directive in the example
20273 * below, are evaluated with respect to the current scope. Therefore, there is
20274 * no risk of inadvertently coding in a dependency on a global value in such an
20278 <example module="windowExample" name="window-service">
20279 <file name="index.html">
20281 angular.module('windowExample', [])
20282 .controller('ExampleController', ['$scope', '$window', function($scope, $window) {
20283 $scope.greeting = 'Hello, World!';
20284 $scope.doGreeting = function(greeting) {
20285 $window.alert(greeting);
20289 <div ng-controller="ExampleController">
20290 <input type="text" ng-model="greeting" aria-label="greeting" />
20291 <button ng-click="doGreeting(greeting)">ALERT</button>
20294 <file name="protractor.js" type="protractor">
20295 it('should display the greeting in the input box', function() {
20296 element(by.model('greeting')).sendKeys('Hello, E2E Tests');
20297 // If we click the button it will block the test runner
20298 // element(':button').click();
20303 function $WindowProvider() {
20304 this.$get = valueFn(window);
20308 * @name $$cookieReader
20309 * @requires $document
20312 * This is a private service for reading cookies used by $http and ngCookies
20314 * @return {Object} a key/value map of the current cookies
20316 function $$CookieReader($document) {
20317 var rawDocument = $document[0] || {};
20318 var lastCookies = {};
20319 var lastCookieString = '';
20321 function safeGetCookie(rawDocument) {
20323 return rawDocument.cookie || '';
20329 function safeDecodeURIComponent(str) {
20331 return decodeURIComponent(str);
20337 return function() {
20338 var cookieArray, cookie, i, index, name;
20339 var currentCookieString = safeGetCookie(rawDocument);
20341 if (currentCookieString !== lastCookieString) {
20342 lastCookieString = currentCookieString;
20343 cookieArray = lastCookieString.split('; ');
20346 for (i = 0; i < cookieArray.length; i++) {
20347 cookie = cookieArray[i];
20348 index = cookie.indexOf('=');
20349 if (index > 0) { //ignore nameless cookies
20350 name = safeDecodeURIComponent(cookie.substring(0, index));
20351 // the first value that is seen for a cookie is the most
20352 // specific one. values for the same cookie name that
20353 // follow are for less specific paths.
20354 if (isUndefined(lastCookies[name])) {
20355 lastCookies[name] = safeDecodeURIComponent(cookie.substring(index + 1));
20360 return lastCookies;
20364 $$CookieReader.$inject = ['$document'];
20367 function $$CookieReaderProvider() {
20368 this.$get = $$CookieReader;
20371 /* global currencyFilter: true,
20373 filterFilter: true,
20375 limitToFilter: true,
20376 lowercaseFilter: true,
20377 numberFilter: true,
20378 orderByFilter: true,
20379 uppercaseFilter: true,
20384 * @name $filterProvider
20387 * Filters are just functions which transform input to an output. However filters need to be
20388 * Dependency Injected. To achieve this a filter definition consists of a factory function which is
20389 * annotated with dependencies and is responsible for creating a filter function.
20391 * <div class="alert alert-warning">
20392 * **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`.
20393 * Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace
20394 * your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
20395 * (`myapp_subsection_filterx`).
20399 * // Filter registration
20400 * function MyModule($provide, $filterProvider) {
20401 * // create a service to demonstrate injection (not always needed)
20402 * $provide.value('greet', function(name){
20403 * return 'Hello ' + name + '!';
20406 * // register a filter factory which uses the
20407 * // greet service to demonstrate DI.
20408 * $filterProvider.register('greet', function(greet){
20409 * // return the filter function which uses the greet service
20410 * // to generate salutation
20411 * return function(text) {
20412 * // filters need to be forgiving so check input validity
20413 * return text && greet(text) || text;
20419 * The filter function is registered with the `$injector` under the filter name suffix with
20423 * it('should be the same instance', inject(
20424 * function($filterProvider) {
20425 * $filterProvider.register('reverse', function(){
20429 * function($filter, reverseFilter) {
20430 * expect($filter('reverse')).toBe(reverseFilter);
20435 * For more information about how angular filters work, and how to create your own filters, see
20436 * {@link guide/filter Filters} in the Angular Developer Guide.
20444 * Filters are used for formatting data displayed to the user.
20446 * They can be used in view templates, controllers or services.Angular comes
20447 * with a collection of [built-in filters](api/ng/filter), but it is easy to
20448 * define your own as well.
20450 * The general syntax in templates is as follows:
20453 * {{ expression [| filter_name[:parameter_value] ... ] }}
20456 * @param {String} name Name of the filter function to retrieve
20457 * @return {Function} the filter function
20459 <example name="$filter" module="filterExample">
20460 <file name="index.html">
20461 <div ng-controller="MainCtrl">
20462 <h3>{{ originalText }}</h3>
20463 <h3>{{ filteredText }}</h3>
20467 <file name="script.js">
20468 angular.module('filterExample', [])
20469 .controller('MainCtrl', function($scope, $filter) {
20470 $scope.originalText = 'hello';
20471 $scope.filteredText = $filter('uppercase')($scope.originalText);
20476 $FilterProvider.$inject = ['$provide'];
20478 function $FilterProvider($provide) {
20479 var suffix = 'Filter';
20483 * @name $filterProvider#register
20484 * @param {string|Object} name Name of the filter function, or an object map of filters where
20485 * the keys are the filter names and the values are the filter factories.
20487 * <div class="alert alert-warning">
20488 * **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`.
20489 * Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace
20490 * your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
20491 * (`myapp_subsection_filterx`).
20493 * @param {Function} factory If the first argument was a string, a factory function for the filter to be registered.
20494 * @returns {Object} Registered filter instance, or if a map of filters was provided then a map
20495 * of the registered filter instances.
20497 function register(name, factory) {
20498 if (isObject(name)) {
20500 forEach(name, function(filter, key) {
20501 filters[key] = register(key, filter);
20505 return $provide.factory(name + suffix, factory);
20508 this.register = register;
20510 this.$get = ['$injector', function($injector) {
20511 return function(name) {
20512 return $injector.get(name + suffix);
20516 ////////////////////////////////////////
20519 currencyFilter: false,
20521 filterFilter: false,
20523 limitToFilter: false,
20524 lowercaseFilter: false,
20525 numberFilter: false,
20526 orderByFilter: false,
20527 uppercaseFilter: false
20530 register('currency', currencyFilter);
20531 register('date', dateFilter);
20532 register('filter', filterFilter);
20533 register('json', jsonFilter);
20534 register('limitTo', limitToFilter);
20535 register('lowercase', lowercaseFilter);
20536 register('number', numberFilter);
20537 register('orderBy', orderByFilter);
20538 register('uppercase', uppercaseFilter);
20547 * Selects a subset of items from `array` and returns it as a new array.
20549 * @param {Array} array The source array.
20550 * <div class="alert alert-info">
20551 * **Note**: If the array contains objects that reference themselves, filtering is not possible.
20553 * @param {string|Object|function()} expression The predicate to be used for selecting items from
20558 * - `string`: The string is used for matching against the contents of the `array`. All strings or
20559 * objects with string properties in `array` that match this string will be returned. This also
20560 * applies to nested object properties.
20561 * The predicate can be negated by prefixing the string with `!`.
20563 * - `Object`: A pattern object can be used to filter specific properties on objects contained
20564 * by `array`. For example `{name:"M", phone:"1"}` predicate will return an array of items
20565 * which have property `name` containing "M" and property `phone` containing "1". A special
20566 * property name (`$` by default) can be used (e.g. as in `{$: "text"}`) to accept a match
20567 * against any property of the object or its nested object properties. That's equivalent to the
20568 * simple substring match with a `string` as described above. The special property name can be
20569 * overwritten, using the `anyPropertyKey` parameter.
20570 * The predicate can be negated by prefixing the string with `!`.
20571 * For example `{name: "!M"}` predicate will return an array of items which have property `name`
20572 * not containing "M".
20574 * Note that a named property will match properties on the same level only, while the special
20575 * `$` property will match properties on the same level or deeper. E.g. an array item like
20576 * `{name: {first: 'John', last: 'Doe'}}` will **not** be matched by `{name: 'John'}`, but
20577 * **will** be matched by `{$: 'John'}`.
20579 * - `function(value, index, array)`: A predicate function can be used to write arbitrary filters.
20580 * The function is called for each element of the array, with the element, its index, and
20581 * the entire array itself as arguments.
20583 * The final result is an array of those elements that the predicate returned true for.
20585 * @param {function(actual, expected)|true|false} [comparator] Comparator which is used in
20586 * determining if the expected value (from the filter expression) and actual value (from
20587 * the object in the array) should be considered a match.
20591 * - `function(actual, expected)`:
20592 * The function will be given the object value and the predicate value to compare and
20593 * should return true if both values should be considered equal.
20595 * - `true`: A shorthand for `function(actual, expected) { return angular.equals(actual, expected)}`.
20596 * This is essentially strict comparison of expected and actual.
20598 * - `false`: A short hand for a function which will look for a substring match in a case
20599 * insensitive way. Primitive values are converted to strings. Objects are not compared against
20600 * primitives, unless they have a custom `toString` method (e.g. `Date` objects).
20603 * Defaults to `false`.
20605 * @param {string} [anyPropertyKey] The special property name that matches against any property.
20609 <example name="filter-filter">
20610 <file name="index.html">
20611 <div ng-init="friends = [{name:'John', phone:'555-1276'},
20612 {name:'Mary', phone:'800-BIG-MARY'},
20613 {name:'Mike', phone:'555-4321'},
20614 {name:'Adam', phone:'555-5678'},
20615 {name:'Julie', phone:'555-8765'},
20616 {name:'Juliette', phone:'555-5678'}]"></div>
20618 <label>Search: <input ng-model="searchText"></label>
20619 <table id="searchTextResults">
20620 <tr><th>Name</th><th>Phone</th></tr>
20621 <tr ng-repeat="friend in friends | filter:searchText">
20622 <td>{{friend.name}}</td>
20623 <td>{{friend.phone}}</td>
20627 <label>Any: <input ng-model="search.$"></label> <br>
20628 <label>Name only <input ng-model="search.name"></label><br>
20629 <label>Phone only <input ng-model="search.phone"></label><br>
20630 <label>Equality <input type="checkbox" ng-model="strict"></label><br>
20631 <table id="searchObjResults">
20632 <tr><th>Name</th><th>Phone</th></tr>
20633 <tr ng-repeat="friendObj in friends | filter:search:strict">
20634 <td>{{friendObj.name}}</td>
20635 <td>{{friendObj.phone}}</td>
20639 <file name="protractor.js" type="protractor">
20640 var expectFriendNames = function(expectedNames, key) {
20641 element.all(by.repeater(key + ' in friends').column(key + '.name')).then(function(arr) {
20642 arr.forEach(function(wd, i) {
20643 expect(wd.getText()).toMatch(expectedNames[i]);
20648 it('should search across all fields when filtering with a string', function() {
20649 var searchText = element(by.model('searchText'));
20650 searchText.clear();
20651 searchText.sendKeys('m');
20652 expectFriendNames(['Mary', 'Mike', 'Adam'], 'friend');
20654 searchText.clear();
20655 searchText.sendKeys('76');
20656 expectFriendNames(['John', 'Julie'], 'friend');
20659 it('should search in specific fields when filtering with a predicate object', function() {
20660 var searchAny = element(by.model('search.$'));
20662 searchAny.sendKeys('i');
20663 expectFriendNames(['Mary', 'Mike', 'Julie', 'Juliette'], 'friendObj');
20665 it('should use a equal comparison when comparator is true', function() {
20666 var searchName = element(by.model('search.name'));
20667 var strict = element(by.model('strict'));
20668 searchName.clear();
20669 searchName.sendKeys('Julie');
20671 expectFriendNames(['Julie'], 'friendObj');
20677 function filterFilter() {
20678 return function(array, expression, comparator, anyPropertyKey) {
20679 if (!isArrayLike(array)) {
20680 if (array == null) {
20683 throw minErr('filter')('notarray', 'Expected array but received: {0}', array);
20687 anyPropertyKey = anyPropertyKey || '$';
20688 var expressionType = getTypeForFilter(expression);
20690 var matchAgainstAnyProp;
20692 switch (expressionType) {
20694 predicateFn = expression;
20700 matchAgainstAnyProp = true;
20703 predicateFn = createPredicateFn(expression, comparator, anyPropertyKey, matchAgainstAnyProp);
20709 return Array.prototype.filter.call(array, predicateFn);
20713 // Helper functions for `filterFilter`
20714 function createPredicateFn(expression, comparator, anyPropertyKey, matchAgainstAnyProp) {
20715 var shouldMatchPrimitives = isObject(expression) && (anyPropertyKey in expression);
20718 if (comparator === true) {
20719 comparator = equals;
20720 } else if (!isFunction(comparator)) {
20721 comparator = function(actual, expected) {
20722 if (isUndefined(actual)) {
20723 // No substring matching against `undefined`
20726 if ((actual === null) || (expected === null)) {
20727 // No substring matching against `null`; only match against `null`
20728 return actual === expected;
20730 if (isObject(expected) || (isObject(actual) && !hasCustomToString(actual))) {
20731 // Should not compare primitives against objects, unless they have custom `toString` method
20735 actual = lowercase('' + actual);
20736 expected = lowercase('' + expected);
20737 return actual.indexOf(expected) !== -1;
20741 predicateFn = function(item) {
20742 if (shouldMatchPrimitives && !isObject(item)) {
20743 return deepCompare(item, expression[anyPropertyKey], comparator, anyPropertyKey, false);
20745 return deepCompare(item, expression, comparator, anyPropertyKey, matchAgainstAnyProp);
20748 return predicateFn;
20751 function deepCompare(actual, expected, comparator, anyPropertyKey, matchAgainstAnyProp, dontMatchWholeObject) {
20752 var actualType = getTypeForFilter(actual);
20753 var expectedType = getTypeForFilter(expected);
20755 if ((expectedType === 'string') && (expected.charAt(0) === '!')) {
20756 return !deepCompare(actual, expected.substring(1), comparator, anyPropertyKey, matchAgainstAnyProp);
20757 } else if (isArray(actual)) {
20758 // In case `actual` is an array, consider it a match
20759 // if ANY of it's items matches `expected`
20760 return actual.some(function(item) {
20761 return deepCompare(item, expected, comparator, anyPropertyKey, matchAgainstAnyProp);
20765 switch (actualType) {
20768 if (matchAgainstAnyProp) {
20769 for (key in actual) {
20770 // Under certain, rare, circumstances, key may not be a string and `charAt` will be undefined
20771 // See: https://github.com/angular/angular.js/issues/15644
20772 if (key.charAt && (key.charAt(0) !== '$') &&
20773 deepCompare(actual[key], expected, comparator, anyPropertyKey, true)) {
20777 return dontMatchWholeObject ? false : deepCompare(actual, expected, comparator, anyPropertyKey, false);
20778 } else if (expectedType === 'object') {
20779 for (key in expected) {
20780 var expectedVal = expected[key];
20781 if (isFunction(expectedVal) || isUndefined(expectedVal)) {
20785 var matchAnyProperty = key === anyPropertyKey;
20786 var actualVal = matchAnyProperty ? actual : actual[key];
20787 if (!deepCompare(actualVal, expectedVal, comparator, anyPropertyKey, matchAnyProperty, matchAnyProperty)) {
20793 return comparator(actual, expected);
20798 return comparator(actual, expected);
20802 // Used for easily differentiating between `null` and actual `object`
20803 function getTypeForFilter(val) {
20804 return (val === null) ? 'null' : typeof val;
20807 var MAX_DIGITS = 22;
20808 var DECIMAL_SEP = '.';
20809 var ZERO_CHAR = '0';
20817 * Formats a number as a currency (ie $1,234.56). When no currency symbol is provided, default
20818 * symbol for current locale is used.
20820 * @param {number} amount Input to filter.
20821 * @param {string=} symbol Currency symbol or identifier to be displayed.
20822 * @param {number=} fractionSize Number of decimal places to round the amount to, defaults to default max fraction size for current locale
20823 * @returns {string} Formatted number.
20827 <example module="currencyExample" name="currency-filter">
20828 <file name="index.html">
20830 angular.module('currencyExample', [])
20831 .controller('ExampleController', ['$scope', function($scope) {
20832 $scope.amount = 1234.56;
20835 <div ng-controller="ExampleController">
20836 <input type="number" ng-model="amount" aria-label="amount"> <br>
20837 default currency symbol ($): <span id="currency-default">{{amount | currency}}</span><br>
20838 custom currency identifier (USD$): <span id="currency-custom">{{amount | currency:"USD$"}}</span><br>
20839 no fractions (0): <span id="currency-no-fractions">{{amount | currency:"USD$":0}}</span>
20842 <file name="protractor.js" type="protractor">
20843 it('should init with 1234.56', function() {
20844 expect(element(by.id('currency-default')).getText()).toBe('$1,234.56');
20845 expect(element(by.id('currency-custom')).getText()).toBe('USD$1,234.56');
20846 expect(element(by.id('currency-no-fractions')).getText()).toBe('USD$1,235');
20848 it('should update', function() {
20849 if (browser.params.browser === 'safari') {
20850 // Safari does not understand the minus key. See
20851 // https://github.com/angular/protractor/issues/481
20854 element(by.model('amount')).clear();
20855 element(by.model('amount')).sendKeys('-1234');
20856 expect(element(by.id('currency-default')).getText()).toBe('-$1,234.00');
20857 expect(element(by.id('currency-custom')).getText()).toBe('-USD$1,234.00');
20858 expect(element(by.id('currency-no-fractions')).getText()).toBe('-USD$1,234');
20863 currencyFilter.$inject = ['$locale'];
20864 function currencyFilter($locale) {
20865 var formats = $locale.NUMBER_FORMATS;
20866 return function(amount, currencySymbol, fractionSize) {
20867 if (isUndefined(currencySymbol)) {
20868 currencySymbol = formats.CURRENCY_SYM;
20871 if (isUndefined(fractionSize)) {
20872 fractionSize = formats.PATTERNS[1].maxFrac;
20875 // if null or undefined pass it through
20876 return (amount == null)
20878 : formatNumber(amount, formats.PATTERNS[1], formats.GROUP_SEP, formats.DECIMAL_SEP, fractionSize).
20879 replace(/\u00A4/g, currencySymbol);
20889 * Formats a number as text.
20891 * If the input is null or undefined, it will just be returned.
20892 * If the input is infinite (Infinity or -Infinity), the Infinity symbol '∞' or '-∞' is returned, respectively.
20893 * If the input is not a number an empty string is returned.
20896 * @param {number|string} number Number to format.
20897 * @param {(number|string)=} fractionSize Number of decimal places to round the number to.
20898 * If this is not provided then the fraction size is computed from the current locale's number
20899 * formatting pattern. In the case of the default locale, it will be 3.
20900 * @returns {string} Number rounded to `fractionSize` appropriately formatted based on the current
20901 * locale (e.g., in the en_US locale it will have "." as the decimal separator and
20902 * include "," group separators after each third digit).
20905 <example module="numberFilterExample" name="number-filter">
20906 <file name="index.html">
20908 angular.module('numberFilterExample', [])
20909 .controller('ExampleController', ['$scope', function($scope) {
20910 $scope.val = 1234.56789;
20913 <div ng-controller="ExampleController">
20914 <label>Enter number: <input ng-model='val'></label><br>
20915 Default formatting: <span id='number-default'>{{val | number}}</span><br>
20916 No fractions: <span>{{val | number:0}}</span><br>
20917 Negative number: <span>{{-val | number:4}}</span>
20920 <file name="protractor.js" type="protractor">
20921 it('should format numbers', function() {
20922 expect(element(by.id('number-default')).getText()).toBe('1,234.568');
20923 expect(element(by.binding('val | number:0')).getText()).toBe('1,235');
20924 expect(element(by.binding('-val | number:4')).getText()).toBe('-1,234.5679');
20927 it('should update', function() {
20928 element(by.model('val')).clear();
20929 element(by.model('val')).sendKeys('3374.333');
20930 expect(element(by.id('number-default')).getText()).toBe('3,374.333');
20931 expect(element(by.binding('val | number:0')).getText()).toBe('3,374');
20932 expect(element(by.binding('-val | number:4')).getText()).toBe('-3,374.3330');
20937 numberFilter.$inject = ['$locale'];
20938 function numberFilter($locale) {
20939 var formats = $locale.NUMBER_FORMATS;
20940 return function(number, fractionSize) {
20942 // if null or undefined pass it through
20943 return (number == null)
20945 : formatNumber(number, formats.PATTERNS[0], formats.GROUP_SEP, formats.DECIMAL_SEP,
20951 * Parse a number (as a string) into three components that can be used
20952 * for formatting the number.
20954 * (Significant bits of this parse algorithm came from https://github.com/MikeMcl/big.js/)
20956 * @param {string} numStr The number to parse
20957 * @return {object} An object describing this number, containing the following keys:
20958 * - d : an array of digits containing leading zeros as necessary
20959 * - i : the number of the digits in `d` that are to the left of the decimal point
20960 * - e : the exponent for numbers that would need more than `MAX_DIGITS` digits in `d`
20963 function parse(numStr) {
20964 var exponent = 0, digits, numberOfIntegerDigits;
20968 if ((numberOfIntegerDigits = numStr.indexOf(DECIMAL_SEP)) > -1) {
20969 numStr = numStr.replace(DECIMAL_SEP, '');
20972 // Exponential form?
20973 if ((i = numStr.search(/e/i)) > 0) {
20974 // Work out the exponent.
20975 if (numberOfIntegerDigits < 0) numberOfIntegerDigits = i;
20976 numberOfIntegerDigits += +numStr.slice(i + 1);
20977 numStr = numStr.substring(0, i);
20978 } else if (numberOfIntegerDigits < 0) {
20979 // There was no decimal point or exponent so it is an integer.
20980 numberOfIntegerDigits = numStr.length;
20983 // Count the number of leading zeros.
20984 for (i = 0; numStr.charAt(i) === ZERO_CHAR; i++) { /* empty */ }
20986 if (i === (zeros = numStr.length)) {
20987 // The digits are all zero.
20989 numberOfIntegerDigits = 1;
20991 // Count the number of trailing zeros
20993 while (numStr.charAt(zeros) === ZERO_CHAR) zeros--;
20995 // Trailing zeros are insignificant so ignore them
20996 numberOfIntegerDigits -= i;
20998 // Convert string to array of digits without leading/trailing zeros.
20999 for (j = 0; i <= zeros; i++, j++) {
21000 digits[j] = +numStr.charAt(i);
21004 // If the number overflows the maximum allowed digits then use an exponent.
21005 if (numberOfIntegerDigits > MAX_DIGITS) {
21006 digits = digits.splice(0, MAX_DIGITS - 1);
21007 exponent = numberOfIntegerDigits - 1;
21008 numberOfIntegerDigits = 1;
21011 return { d: digits, e: exponent, i: numberOfIntegerDigits };
21015 * Round the parsed number to the specified number of decimal places
21016 * This function changed the parsedNumber in-place
21018 function roundNumber(parsedNumber, fractionSize, minFrac, maxFrac) {
21019 var digits = parsedNumber.d;
21020 var fractionLen = digits.length - parsedNumber.i;
21022 // determine fractionSize if it is not specified; `+fractionSize` converts it to a number
21023 fractionSize = (isUndefined(fractionSize)) ? Math.min(Math.max(minFrac, fractionLen), maxFrac) : +fractionSize;
21025 // The index of the digit to where rounding is to occur
21026 var roundAt = fractionSize + parsedNumber.i;
21027 var digit = digits[roundAt];
21030 // Drop fractional digits beyond `roundAt`
21031 digits.splice(Math.max(parsedNumber.i, roundAt));
21033 // Set non-fractional digits beyond `roundAt` to 0
21034 for (var j = roundAt; j < digits.length; j++) {
21038 // We rounded to zero so reset the parsedNumber
21039 fractionLen = Math.max(0, fractionLen);
21040 parsedNumber.i = 1;
21041 digits.length = Math.max(1, roundAt = fractionSize + 1);
21043 for (var i = 1; i < roundAt; i++) digits[i] = 0;
21047 if (roundAt - 1 < 0) {
21048 for (var k = 0; k > roundAt; k--) {
21055 digits[roundAt - 1]++;
21059 // Pad out with zeros to get the required fraction length
21060 for (; fractionLen < Math.max(0, fractionSize); fractionLen++) digits.push(0);
21063 // Do any carrying, e.g. a digit was rounded up to 10
21064 var carry = digits.reduceRight(function(carry, d, i, digits) {
21066 digits[i] = d % 10;
21067 return Math.floor(d / 10);
21070 digits.unshift(carry);
21076 * Format a number into a string
21077 * @param {number} number The number to format
21079 * minFrac, // the minimum number of digits required in the fraction part of the number
21080 * maxFrac, // the maximum number of digits required in the fraction part of the number
21081 * gSize, // number of digits in each group of separated digits
21082 * lgSize, // number of digits in the last group of digits before the decimal separator
21083 * negPre, // the string to go in front of a negative number (e.g. `-` or `(`))
21084 * posPre, // the string to go in front of a positive number
21085 * negSuf, // the string to go after a negative number (e.g. `)`)
21086 * posSuf // the string to go after a positive number
21088 * @param {string} groupSep The string to separate groups of number (e.g. `,`)
21089 * @param {string} decimalSep The string to act as the decimal separator (e.g. `.`)
21090 * @param {[type]} fractionSize The size of the fractional part of the number
21091 * @return {string} The number formatted as a string
21093 function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) {
21095 if (!(isString(number) || isNumber(number)) || isNaN(number)) return '';
21097 var isInfinity = !isFinite(number);
21098 var isZero = false;
21099 var numStr = Math.abs(number) + '',
21100 formattedText = '',
21104 formattedText = '\u221e';
21106 parsedNumber = parse(numStr);
21108 roundNumber(parsedNumber, fractionSize, pattern.minFrac, pattern.maxFrac);
21110 var digits = parsedNumber.d;
21111 var integerLen = parsedNumber.i;
21112 var exponent = parsedNumber.e;
21114 isZero = digits.reduce(function(isZero, d) { return isZero && !d; }, true);
21116 // pad zeros for small numbers
21117 while (integerLen < 0) {
21122 // extract decimals digits
21123 if (integerLen > 0) {
21124 decimals = digits.splice(integerLen, digits.length);
21130 // format the integer digits with grouping separators
21132 if (digits.length >= pattern.lgSize) {
21133 groups.unshift(digits.splice(-pattern.lgSize, digits.length).join(''));
21135 while (digits.length > pattern.gSize) {
21136 groups.unshift(digits.splice(-pattern.gSize, digits.length).join(''));
21138 if (digits.length) {
21139 groups.unshift(digits.join(''));
21141 formattedText = groups.join(groupSep);
21143 // append the decimal digits
21144 if (decimals.length) {
21145 formattedText += decimalSep + decimals.join('');
21149 formattedText += 'e+' + exponent;
21152 if (number < 0 && !isZero) {
21153 return pattern.negPre + formattedText + pattern.negSuf;
21155 return pattern.posPre + formattedText + pattern.posSuf;
21159 function padNumber(num, digits, trim, negWrap) {
21161 if (num < 0 || (negWrap && num <= 0)) {
21170 while (num.length < digits) num = ZERO_CHAR + num;
21172 num = num.substr(num.length - digits);
21178 function dateGetter(name, size, offset, trim, negWrap) {
21179 offset = offset || 0;
21180 return function(date) {
21181 var value = date['get' + name]();
21182 if (offset > 0 || value > -offset) {
21185 if (value === 0 && offset === -12) value = 12;
21186 return padNumber(value, size, trim, negWrap);
21190 function dateStrGetter(name, shortForm, standAlone) {
21191 return function(date, formats) {
21192 var value = date['get' + name]();
21193 var propPrefix = (standAlone ? 'STANDALONE' : '') + (shortForm ? 'SHORT' : '');
21194 var get = uppercase(propPrefix + name);
21196 return formats[get][value];
21200 function timeZoneGetter(date, formats, offset) {
21201 var zone = -1 * offset;
21202 var paddedZone = (zone >= 0) ? '+' : '';
21204 paddedZone += padNumber(Math[zone > 0 ? 'floor' : 'ceil'](zone / 60), 2) +
21205 padNumber(Math.abs(zone % 60), 2);
21210 function getFirstThursdayOfYear(year) {
21211 // 0 = index of January
21212 var dayOfWeekOnFirst = (new Date(year, 0, 1)).getDay();
21213 // 4 = index of Thursday (+1 to account for 1st = 5)
21214 // 11 = index of *next* Thursday (+1 account for 1st = 12)
21215 return new Date(year, 0, ((dayOfWeekOnFirst <= 4) ? 5 : 12) - dayOfWeekOnFirst);
21218 function getThursdayThisWeek(datetime) {
21219 return new Date(datetime.getFullYear(), datetime.getMonth(),
21220 // 4 = index of Thursday
21221 datetime.getDate() + (4 - datetime.getDay()));
21224 function weekGetter(size) {
21225 return function(date) {
21226 var firstThurs = getFirstThursdayOfYear(date.getFullYear()),
21227 thisThurs = getThursdayThisWeek(date);
21229 var diff = +thisThurs - +firstThurs,
21230 result = 1 + Math.round(diff / 6.048e8); // 6.048e8 ms per week
21232 return padNumber(result, size);
21236 function ampmGetter(date, formats) {
21237 return date.getHours() < 12 ? formats.AMPMS[0] : formats.AMPMS[1];
21240 function eraGetter(date, formats) {
21241 return date.getFullYear() <= 0 ? formats.ERAS[0] : formats.ERAS[1];
21244 function longEraGetter(date, formats) {
21245 return date.getFullYear() <= 0 ? formats.ERANAMES[0] : formats.ERANAMES[1];
21248 var DATE_FORMATS = {
21249 yyyy: dateGetter('FullYear', 4, 0, false, true),
21250 yy: dateGetter('FullYear', 2, 0, true, true),
21251 y: dateGetter('FullYear', 1, 0, false, true),
21252 MMMM: dateStrGetter('Month'),
21253 MMM: dateStrGetter('Month', true),
21254 MM: dateGetter('Month', 2, 1),
21255 M: dateGetter('Month', 1, 1),
21256 LLLL: dateStrGetter('Month', false, true),
21257 dd: dateGetter('Date', 2),
21258 d: dateGetter('Date', 1),
21259 HH: dateGetter('Hours', 2),
21260 H: dateGetter('Hours', 1),
21261 hh: dateGetter('Hours', 2, -12),
21262 h: dateGetter('Hours', 1, -12),
21263 mm: dateGetter('Minutes', 2),
21264 m: dateGetter('Minutes', 1),
21265 ss: dateGetter('Seconds', 2),
21266 s: dateGetter('Seconds', 1),
21267 // while ISO 8601 requires fractions to be prefixed with `.` or `,`
21268 // we can be just safely rely on using `sss` since we currently don't support single or two digit fractions
21269 sss: dateGetter('Milliseconds', 3),
21270 EEEE: dateStrGetter('Day'),
21271 EEE: dateStrGetter('Day', true),
21279 GGGG: longEraGetter
21282 var DATE_FORMATS_SPLIT = /((?:[^yMLdHhmsaZEwG']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|L+|d+|H+|h+|m+|s+|a|Z|G+|w+))(.*)/,
21283 NUMBER_STRING = /^-?\d+$/;
21291 * Formats `date` to a string based on the requested `format`.
21293 * `format` string can be composed of the following elements:
21295 * * `'yyyy'`: 4 digit representation of year (e.g. AD 1 => 0001, AD 2010 => 2010)
21296 * * `'yy'`: 2 digit representation of year, padded (00-99). (e.g. AD 2001 => 01, AD 2010 => 10)
21297 * * `'y'`: 1 digit representation of year, e.g. (AD 1 => 1, AD 199 => 199)
21298 * * `'MMMM'`: Month in year (January-December)
21299 * * `'MMM'`: Month in year (Jan-Dec)
21300 * * `'MM'`: Month in year, padded (01-12)
21301 * * `'M'`: Month in year (1-12)
21302 * * `'LLLL'`: Stand-alone month in year (January-December)
21303 * * `'dd'`: Day in month, padded (01-31)
21304 * * `'d'`: Day in month (1-31)
21305 * * `'EEEE'`: Day in Week,(Sunday-Saturday)
21306 * * `'EEE'`: Day in Week, (Sun-Sat)
21307 * * `'HH'`: Hour in day, padded (00-23)
21308 * * `'H'`: Hour in day (0-23)
21309 * * `'hh'`: Hour in AM/PM, padded (01-12)
21310 * * `'h'`: Hour in AM/PM, (1-12)
21311 * * `'mm'`: Minute in hour, padded (00-59)
21312 * * `'m'`: Minute in hour (0-59)
21313 * * `'ss'`: Second in minute, padded (00-59)
21314 * * `'s'`: Second in minute (0-59)
21315 * * `'sss'`: Millisecond in second, padded (000-999)
21316 * * `'a'`: AM/PM marker
21317 * * `'Z'`: 4 digit (+sign) representation of the timezone offset (-1200-+1200)
21318 * * `'ww'`: Week of year, padded (00-53). Week 01 is the week with the first Thursday of the year
21319 * * `'w'`: Week of year (0-53). Week 1 is the week with the first Thursday of the year
21320 * * `'G'`, `'GG'`, `'GGG'`: The abbreviated form of the era string (e.g. 'AD')
21321 * * `'GGGG'`: The long form of the era string (e.g. 'Anno Domini')
21323 * `format` string can also be one of the following predefined
21324 * {@link guide/i18n localizable formats}:
21326 * * `'medium'`: equivalent to `'MMM d, y h:mm:ss a'` for en_US locale
21327 * (e.g. Sep 3, 2010 12:05:08 PM)
21328 * * `'short'`: equivalent to `'M/d/yy h:mm a'` for en_US locale (e.g. 9/3/10 12:05 PM)
21329 * * `'fullDate'`: equivalent to `'EEEE, MMMM d, y'` for en_US locale
21330 * (e.g. Friday, September 3, 2010)
21331 * * `'longDate'`: equivalent to `'MMMM d, y'` for en_US locale (e.g. September 3, 2010)
21332 * * `'mediumDate'`: equivalent to `'MMM d, y'` for en_US locale (e.g. Sep 3, 2010)
21333 * * `'shortDate'`: equivalent to `'M/d/yy'` for en_US locale (e.g. 9/3/10)
21334 * * `'mediumTime'`: equivalent to `'h:mm:ss a'` for en_US locale (e.g. 12:05:08 PM)
21335 * * `'shortTime'`: equivalent to `'h:mm a'` for en_US locale (e.g. 12:05 PM)
21337 * `format` string can contain literal values. These need to be escaped by surrounding with single quotes (e.g.
21338 * `"h 'in the morning'"`). In order to output a single quote, escape it - i.e., two single quotes in a sequence
21339 * (e.g. `"h 'o''clock'"`).
21341 * @param {(Date|number|string)} date Date to format either as Date object, milliseconds (string or
21342 * number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.sssZ and its
21343 * shorter versions like yyyy-MM-ddTHH:mmZ, yyyy-MM-dd or yyyyMMddTHHmmssZ). If no timezone is
21344 * specified in the string input, the time is considered to be in the local timezone.
21345 * @param {string=} format Formatting rules (see Description). If not specified,
21346 * `mediumDate` is used.
21347 * @param {string=} timezone Timezone to be used for formatting. It understands UTC/GMT and the
21348 * continental US time zone abbreviations, but for general use, use a time zone offset, for
21349 * example, `'+0430'` (4 hours, 30 minutes east of the Greenwich meridian)
21350 * If not specified, the timezone of the browser will be used.
21351 * @returns {string} Formatted string or the input if input is not recognized as date/millis.
21354 <example name="filter-date">
21355 <file name="index.html">
21356 <span ng-non-bindable>{{1288323623006 | date:'medium'}}</span>:
21357 <span>{{1288323623006 | date:'medium'}}</span><br>
21358 <span ng-non-bindable>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span>:
21359 <span>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span><br>
21360 <span ng-non-bindable>{{1288323623006 | date:'MM/dd/yyyy @ h:mma'}}</span>:
21361 <span>{{'1288323623006' | date:'MM/dd/yyyy @ h:mma'}}</span><br>
21362 <span ng-non-bindable>{{1288323623006 | date:"MM/dd/yyyy 'at' h:mma"}}</span>:
21363 <span>{{'1288323623006' | date:"MM/dd/yyyy 'at' h:mma"}}</span><br>
21365 <file name="protractor.js" type="protractor">
21366 it('should format date', function() {
21367 expect(element(by.binding("1288323623006 | date:'medium'")).getText()).
21368 toMatch(/Oct 2\d, 2010 \d{1,2}:\d{2}:\d{2} (AM|PM)/);
21369 expect(element(by.binding("1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'")).getText()).
21370 toMatch(/2010-10-2\d \d{2}:\d{2}:\d{2} (-|\+)?\d{4}/);
21371 expect(element(by.binding("'1288323623006' | date:'MM/dd/yyyy @ h:mma'")).getText()).
21372 toMatch(/10\/2\d\/2010 @ \d{1,2}:\d{2}(AM|PM)/);
21373 expect(element(by.binding("'1288323623006' | date:\"MM/dd/yyyy 'at' h:mma\"")).getText()).
21374 toMatch(/10\/2\d\/2010 at \d{1,2}:\d{2}(AM|PM)/);
21379 dateFilter.$inject = ['$locale'];
21380 function dateFilter($locale) {
21383 var R_ISO8601_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/;
21384 // 1 2 3 4 5 6 7 8 9 10 11
21385 function jsonStringToDate(string) {
21387 if ((match = string.match(R_ISO8601_STR))) {
21388 var date = new Date(0),
21391 dateSetter = match[8] ? date.setUTCFullYear : date.setFullYear,
21392 timeSetter = match[8] ? date.setUTCHours : date.setHours;
21395 tzHour = toInt(match[9] + match[10]);
21396 tzMin = toInt(match[9] + match[11]);
21398 dateSetter.call(date, toInt(match[1]), toInt(match[2]) - 1, toInt(match[3]));
21399 var h = toInt(match[4] || 0) - tzHour;
21400 var m = toInt(match[5] || 0) - tzMin;
21401 var s = toInt(match[6] || 0);
21402 var ms = Math.round(parseFloat('0.' + (match[7] || 0)) * 1000);
21403 timeSetter.call(date, h, m, s, ms);
21410 return function(date, format, timezone) {
21415 format = format || 'mediumDate';
21416 format = $locale.DATETIME_FORMATS[format] || format;
21417 if (isString(date)) {
21418 date = NUMBER_STRING.test(date) ? toInt(date) : jsonStringToDate(date);
21421 if (isNumber(date)) {
21422 date = new Date(date);
21425 if (!isDate(date) || !isFinite(date.getTime())) {
21430 match = DATE_FORMATS_SPLIT.exec(format);
21432 parts = concat(parts, match, 1);
21433 format = parts.pop();
21435 parts.push(format);
21440 var dateTimezoneOffset = date.getTimezoneOffset();
21442 dateTimezoneOffset = timezoneToOffset(timezone, dateTimezoneOffset);
21443 date = convertTimezoneToLocal(date, timezone, true);
21445 forEach(parts, function(value) {
21446 fn = DATE_FORMATS[value];
21447 text += fn ? fn(date, $locale.DATETIME_FORMATS, dateTimezoneOffset)
21448 : value === '\'\'' ? '\'' : value.replace(/(^'|'$)/g, '').replace(/''/g, '\'');
21462 * Allows you to convert a JavaScript object into JSON string.
21464 * This filter is mostly useful for debugging. When using the double curly {{value}} notation
21465 * the binding is automatically converted to JSON.
21467 * @param {*} object Any JavaScript object (including arrays and primitive types) to filter.
21468 * @param {number=} spacing The number of spaces to use per indentation, defaults to 2.
21469 * @returns {string} JSON string.
21473 <example name="filter-json">
21474 <file name="index.html">
21475 <pre id="default-spacing">{{ {'name':'value'} | json }}</pre>
21476 <pre id="custom-spacing">{{ {'name':'value'} | json:4 }}</pre>
21478 <file name="protractor.js" type="protractor">
21479 it('should jsonify filtered objects', function() {
21480 expect(element(by.id('default-spacing')).getText()).toMatch(/\{\n {2}"name": ?"value"\n}/);
21481 expect(element(by.id('custom-spacing')).getText()).toMatch(/\{\n {4}"name": ?"value"\n}/);
21487 function jsonFilter() {
21488 return function(object, spacing) {
21489 if (isUndefined(spacing)) {
21492 return toJson(object, spacing);
21502 * Converts string to lowercase.
21503 * @see angular.lowercase
21505 var lowercaseFilter = valueFn(lowercase);
21513 * Converts string to uppercase.
21514 * @see angular.uppercase
21516 var uppercaseFilter = valueFn(uppercase);
21524 * Creates a new array or string containing only a specified number of elements. The elements are
21525 * taken from either the beginning or the end of the source array, string or number, as specified by
21526 * the value and sign (positive or negative) of `limit`. Other array-like objects are also supported
21527 * (e.g. array subclasses, NodeLists, jqLite/jQuery collections etc). If a number is used as input,
21528 * it is converted to a string.
21530 * @param {Array|ArrayLike|string|number} input - Array/array-like, string or number to be limited.
21531 * @param {string|number} limit - The length of the returned array or string. If the `limit` number
21532 * is positive, `limit` number of items from the beginning of the source array/string are copied.
21533 * If the number is negative, `limit` number of items from the end of the source array/string
21534 * are copied. The `limit` will be trimmed if it exceeds `array.length`. If `limit` is undefined,
21535 * the input will be returned unchanged.
21536 * @param {(string|number)=} begin - Index at which to begin limitation. As a negative index,
21537 * `begin` indicates an offset from the end of `input`. Defaults to `0`.
21538 * @returns {Array|string} A new sub-array or substring of length `limit` or less if the input had
21539 * less than `limit` elements.
21542 <example module="limitToExample" name="limit-to-filter">
21543 <file name="index.html">
21545 angular.module('limitToExample', [])
21546 .controller('ExampleController', ['$scope', function($scope) {
21547 $scope.numbers = [1,2,3,4,5,6,7,8,9];
21548 $scope.letters = "abcdefghi";
21549 $scope.longNumber = 2345432342;
21550 $scope.numLimit = 3;
21551 $scope.letterLimit = 3;
21552 $scope.longNumberLimit = 3;
21555 <div ng-controller="ExampleController">
21557 Limit {{numbers}} to:
21558 <input type="number" step="1" ng-model="numLimit">
21560 <p>Output numbers: {{ numbers | limitTo:numLimit }}</p>
21562 Limit {{letters}} to:
21563 <input type="number" step="1" ng-model="letterLimit">
21565 <p>Output letters: {{ letters | limitTo:letterLimit }}</p>
21567 Limit {{longNumber}} to:
21568 <input type="number" step="1" ng-model="longNumberLimit">
21570 <p>Output long number: {{ longNumber | limitTo:longNumberLimit }}</p>
21573 <file name="protractor.js" type="protractor">
21574 var numLimitInput = element(by.model('numLimit'));
21575 var letterLimitInput = element(by.model('letterLimit'));
21576 var longNumberLimitInput = element(by.model('longNumberLimit'));
21577 var limitedNumbers = element(by.binding('numbers | limitTo:numLimit'));
21578 var limitedLetters = element(by.binding('letters | limitTo:letterLimit'));
21579 var limitedLongNumber = element(by.binding('longNumber | limitTo:longNumberLimit'));
21581 it('should limit the number array to first three items', function() {
21582 expect(numLimitInput.getAttribute('value')).toBe('3');
21583 expect(letterLimitInput.getAttribute('value')).toBe('3');
21584 expect(longNumberLimitInput.getAttribute('value')).toBe('3');
21585 expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3]');
21586 expect(limitedLetters.getText()).toEqual('Output letters: abc');
21587 expect(limitedLongNumber.getText()).toEqual('Output long number: 234');
21590 // There is a bug in safari and protractor that doesn't like the minus key
21591 // it('should update the output when -3 is entered', function() {
21592 // numLimitInput.clear();
21593 // numLimitInput.sendKeys('-3');
21594 // letterLimitInput.clear();
21595 // letterLimitInput.sendKeys('-3');
21596 // longNumberLimitInput.clear();
21597 // longNumberLimitInput.sendKeys('-3');
21598 // expect(limitedNumbers.getText()).toEqual('Output numbers: [7,8,9]');
21599 // expect(limitedLetters.getText()).toEqual('Output letters: ghi');
21600 // expect(limitedLongNumber.getText()).toEqual('Output long number: 342');
21603 it('should not exceed the maximum size of input array', function() {
21604 numLimitInput.clear();
21605 numLimitInput.sendKeys('100');
21606 letterLimitInput.clear();
21607 letterLimitInput.sendKeys('100');
21608 longNumberLimitInput.clear();
21609 longNumberLimitInput.sendKeys('100');
21610 expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3,4,5,6,7,8,9]');
21611 expect(limitedLetters.getText()).toEqual('Output letters: abcdefghi');
21612 expect(limitedLongNumber.getText()).toEqual('Output long number: 2345432342');
21617 function limitToFilter() {
21618 return function(input, limit, begin) {
21619 if (Math.abs(Number(limit)) === Infinity) {
21620 limit = Number(limit);
21622 limit = toInt(limit);
21624 if (isNumberNaN(limit)) return input;
21626 if (isNumber(input)) input = input.toString();
21627 if (!isArrayLike(input)) return input;
21629 begin = (!begin || isNaN(begin)) ? 0 : toInt(begin);
21630 begin = (begin < 0) ? Math.max(0, input.length + begin) : begin;
21633 return sliceFn(input, begin, begin + limit);
21636 return sliceFn(input, limit, input.length);
21638 return sliceFn(input, Math.max(0, begin + limit), begin);
21644 function sliceFn(input, begin, end) {
21645 if (isString(input)) return input.slice(begin, end);
21647 return slice.call(input, begin, end);
21656 * Returns an array containing the items from the specified `collection`, ordered by a `comparator`
21657 * function based on the values computed using the `expression` predicate.
21659 * For example, `[{id: 'foo'}, {id: 'bar'}] | orderBy:'id'` would result in
21660 * `[{id: 'bar'}, {id: 'foo'}]`.
21662 * The `collection` can be an Array or array-like object (e.g. NodeList, jQuery object, TypedArray,
21665 * The `expression` can be a single predicate, or a list of predicates each serving as a tie-breaker
21666 * for the preceding one. The `expression` is evaluated against each item and the output is used
21667 * for comparing with other items.
21669 * You can change the sorting order by setting `reverse` to `true`. By default, items are sorted in
21672 * The comparison is done using the `comparator` function. If none is specified, a default, built-in
21673 * comparator is used (see below for details - in a nutshell, it compares numbers numerically and
21674 * strings alphabetically).
21676 * ### Under the hood
21678 * Ordering the specified `collection` happens in two phases:
21680 * 1. All items are passed through the predicate (or predicates), and the returned values are saved
21681 * along with their type (`string`, `number` etc). For example, an item `{label: 'foo'}`, passed
21682 * through a predicate that extracts the value of the `label` property, would be transformed to:
21690 * 2. The comparator function is used to sort the items, based on the derived values, types and
21693 * If you use a custom comparator, it will be called with pairs of objects of the form
21694 * `{value: ..., type: '...', index: ...}` and is expected to return `0` if the objects are equal
21695 * (as far as the comparator is concerned), `-1` if the 1st one should be ranked higher than the
21696 * second, or `1` otherwise.
21698 * In order to ensure that the sorting will be deterministic across platforms, if none of the
21699 * specified predicates can distinguish between two items, `orderBy` will automatically introduce a
21700 * dummy predicate that returns the item's index as `value`.
21701 * (If you are using a custom comparator, make sure it can handle this predicate as well.)
21703 * Finally, in an attempt to simplify things, if a predicate returns an object as the extracted
21704 * value for an item, `orderBy` will try to convert that object to a primitive value, before passing
21705 * it to the comparator. The following rules govern the conversion:
21707 * 1. If the object has a `valueOf()` method that returns a primitive, its return value will be
21708 * used instead.<br />
21709 * (If the object has a `valueOf()` method that returns another object, then the returned object
21710 * will be used in subsequent steps.)
21711 * 2. If the object has a custom `toString()` method (i.e. not the one inherited from `Object`) that
21712 * returns a primitive, its return value will be used instead.<br />
21713 * (If the object has a `toString()` method that returns another object, then the returned object
21714 * will be used in subsequent steps.)
21715 * 3. No conversion; the object itself is used.
21717 * ### The default comparator
21719 * The default, built-in comparator should be sufficient for most usecases. In short, it compares
21720 * numbers numerically, strings alphabetically (and case-insensitively), for objects falls back to
21721 * using their index in the original collection, and sorts values of different types by type.
21723 * More specifically, it follows these steps to determine the relative order of items:
21725 * 1. If the compared values are of different types, compare the types themselves alphabetically.
21726 * 2. If both values are of type `string`, compare them alphabetically in a case- and
21727 * locale-insensitive way.
21728 * 3. If both values are objects, compare their indices instead.
21729 * 4. Otherwise, return:
21730 * - `0`, if the values are equal (by strict equality comparison, i.e. using `===`).
21731 * - `-1`, if the 1st value is "less than" the 2nd value (compared using the `<` operator).
21732 * - `1`, otherwise.
21734 * **Note:** If you notice numbers not being sorted as expected, make sure they are actually being
21735 * saved as numbers and not strings.
21736 * **Note:** For the purpose of sorting, `null` values are treated as the string `'null'` (i.e.
21737 * `type: 'string'`, `value: 'null'`). This may cause unexpected sort order relative to
21740 * @param {Array|ArrayLike} collection - The collection (array or array-like object) to sort.
21741 * @param {(Function|string|Array.<Function|string>)=} expression - A predicate (or list of
21742 * predicates) to be used by the comparator to determine the order of elements.
21746 * - `Function`: A getter function. This function will be called with each item as argument and
21747 * the return value will be used for sorting.
21748 * - `string`: An Angular expression. This expression will be evaluated against each item and the
21749 * result will be used for sorting. For example, use `'label'` to sort by a property called
21750 * `label` or `'label.substring(0, 3)'` to sort by the first 3 characters of the `label`
21752 * (The result of a constant expression is interpreted as a property name to be used for
21753 * comparison. For example, use `'"special name"'` (note the extra pair of quotes) to sort by a
21754 * property called `special name`.)<br />
21755 * An expression can be optionally prefixed with `+` or `-` to control the sorting direction,
21756 * ascending or descending. For example, `'+label'` or `'-label'`. If no property is provided,
21757 * (e.g. `'+'` or `'-'`), the collection element itself is used in comparisons.
21758 * - `Array`: An array of function and/or string predicates. If a predicate cannot determine the
21759 * relative order of two items, the next predicate is used as a tie-breaker.
21761 * **Note:** If the predicate is missing or empty then it defaults to `'+'`.
21763 * @param {boolean=} reverse - If `true`, reverse the sorting order.
21764 * @param {(Function)=} comparator - The comparator function used to determine the relative order of
21765 * value pairs. If omitted, the built-in comparator will be used.
21767 * @returns {Array} - The sorted array.
21771 * ### Ordering a table with `ngRepeat`
21773 * The example below demonstrates a simple {@link ngRepeat ngRepeat}, where the data is sorted by
21774 * age in descending order (expression is set to `'-age'`). The `comparator` is not set, which means
21775 * it defaults to the built-in comparator.
21777 <example name="orderBy-static" module="orderByExample1">
21778 <file name="index.html">
21779 <div ng-controller="ExampleController">
21780 <table class="friends">
21783 <th>Phone Number</th>
21786 <tr ng-repeat="friend in friends | orderBy:'-age'">
21787 <td>{{friend.name}}</td>
21788 <td>{{friend.phone}}</td>
21789 <td>{{friend.age}}</td>
21794 <file name="script.js">
21795 angular.module('orderByExample1', [])
21796 .controller('ExampleController', ['$scope', function($scope) {
21798 {name: 'John', phone: '555-1212', age: 10},
21799 {name: 'Mary', phone: '555-9876', age: 19},
21800 {name: 'Mike', phone: '555-4321', age: 21},
21801 {name: 'Adam', phone: '555-5678', age: 35},
21802 {name: 'Julie', phone: '555-8765', age: 29}
21806 <file name="style.css">
21808 border-collapse: collapse;
21812 border-bottom: 1px solid;
21814 .friends td, .friends th {
21815 border-left: 1px solid;
21818 .friends td:first-child, .friends th:first-child {
21822 <file name="protractor.js" type="protractor">
21823 // Element locators
21824 var names = element.all(by.repeater('friends').column('friend.name'));
21826 it('should sort friends by age in reverse order', function() {
21827 expect(names.get(0).getText()).toBe('Adam');
21828 expect(names.get(1).getText()).toBe('Julie');
21829 expect(names.get(2).getText()).toBe('Mike');
21830 expect(names.get(3).getText()).toBe('Mary');
21831 expect(names.get(4).getText()).toBe('John');
21838 * ### Changing parameters dynamically
21840 * All parameters can be changed dynamically. The next example shows how you can make the columns of
21841 * a table sortable, by binding the `expression` and `reverse` parameters to scope properties.
21843 <example name="orderBy-dynamic" module="orderByExample2">
21844 <file name="index.html">
21845 <div ng-controller="ExampleController">
21846 <pre>Sort by = {{propertyName}}; reverse = {{reverse}}</pre>
21848 <button ng-click="propertyName = null; reverse = false">Set to unsorted</button>
21850 <table class="friends">
21853 <button ng-click="sortBy('name')">Name</button>
21854 <span class="sortorder" ng-show="propertyName === 'name'" ng-class="{reverse: reverse}"></span>
21857 <button ng-click="sortBy('phone')">Phone Number</button>
21858 <span class="sortorder" ng-show="propertyName === 'phone'" ng-class="{reverse: reverse}"></span>
21861 <button ng-click="sortBy('age')">Age</button>
21862 <span class="sortorder" ng-show="propertyName === 'age'" ng-class="{reverse: reverse}"></span>
21865 <tr ng-repeat="friend in friends | orderBy:propertyName:reverse">
21866 <td>{{friend.name}}</td>
21867 <td>{{friend.phone}}</td>
21868 <td>{{friend.age}}</td>
21873 <file name="script.js">
21874 angular.module('orderByExample2', [])
21875 .controller('ExampleController', ['$scope', function($scope) {
21877 {name: 'John', phone: '555-1212', age: 10},
21878 {name: 'Mary', phone: '555-9876', age: 19},
21879 {name: 'Mike', phone: '555-4321', age: 21},
21880 {name: 'Adam', phone: '555-5678', age: 35},
21881 {name: 'Julie', phone: '555-8765', age: 29}
21884 $scope.propertyName = 'age';
21885 $scope.reverse = true;
21886 $scope.friends = friends;
21888 $scope.sortBy = function(propertyName) {
21889 $scope.reverse = ($scope.propertyName === propertyName) ? !$scope.reverse : false;
21890 $scope.propertyName = propertyName;
21894 <file name="style.css">
21896 border-collapse: collapse;
21900 border-bottom: 1px solid;
21902 .friends td, .friends th {
21903 border-left: 1px solid;
21906 .friends td:first-child, .friends th:first-child {
21911 content: '\25b2'; // BLACK UP-POINTING TRIANGLE
21913 .sortorder.reverse:after {
21914 content: '\25bc'; // BLACK DOWN-POINTING TRIANGLE
21917 <file name="protractor.js" type="protractor">
21918 // Element locators
21919 var unsortButton = element(by.partialButtonText('unsorted'));
21920 var nameHeader = element(by.partialButtonText('Name'));
21921 var phoneHeader = element(by.partialButtonText('Phone'));
21922 var ageHeader = element(by.partialButtonText('Age'));
21923 var firstName = element(by.repeater('friends').column('friend.name').row(0));
21924 var lastName = element(by.repeater('friends').column('friend.name').row(4));
21926 it('should sort friends by some property, when clicking on the column header', function() {
21927 expect(firstName.getText()).toBe('Adam');
21928 expect(lastName.getText()).toBe('John');
21930 phoneHeader.click();
21931 expect(firstName.getText()).toBe('John');
21932 expect(lastName.getText()).toBe('Mary');
21934 nameHeader.click();
21935 expect(firstName.getText()).toBe('Adam');
21936 expect(lastName.getText()).toBe('Mike');
21939 expect(firstName.getText()).toBe('John');
21940 expect(lastName.getText()).toBe('Adam');
21943 it('should sort friends in reverse order, when clicking on the same column', function() {
21944 expect(firstName.getText()).toBe('Adam');
21945 expect(lastName.getText()).toBe('John');
21948 expect(firstName.getText()).toBe('John');
21949 expect(lastName.getText()).toBe('Adam');
21952 expect(firstName.getText()).toBe('Adam');
21953 expect(lastName.getText()).toBe('John');
21956 it('should restore the original order, when clicking "Set to unsorted"', function() {
21957 expect(firstName.getText()).toBe('Adam');
21958 expect(lastName.getText()).toBe('John');
21960 unsortButton.click();
21961 expect(firstName.getText()).toBe('John');
21962 expect(lastName.getText()).toBe('Julie');
21969 * ### Using `orderBy` inside a controller
21971 * It is also possible to call the `orderBy` filter manually, by injecting `orderByFilter`, and
21972 * calling it with the desired parameters. (Alternatively, you could inject the `$filter` factory
21973 * and retrieve the `orderBy` filter with `$filter('orderBy')`.)
21975 <example name="orderBy-call-manually" module="orderByExample3">
21976 <file name="index.html">
21977 <div ng-controller="ExampleController">
21978 <pre>Sort by = {{propertyName}}; reverse = {{reverse}}</pre>
21980 <button ng-click="sortBy(null)">Set to unsorted</button>
21982 <table class="friends">
21985 <button ng-click="sortBy('name')">Name</button>
21986 <span class="sortorder" ng-show="propertyName === 'name'" ng-class="{reverse: reverse}"></span>
21989 <button ng-click="sortBy('phone')">Phone Number</button>
21990 <span class="sortorder" ng-show="propertyName === 'phone'" ng-class="{reverse: reverse}"></span>
21993 <button ng-click="sortBy('age')">Age</button>
21994 <span class="sortorder" ng-show="propertyName === 'age'" ng-class="{reverse: reverse}"></span>
21997 <tr ng-repeat="friend in friends">
21998 <td>{{friend.name}}</td>
21999 <td>{{friend.phone}}</td>
22000 <td>{{friend.age}}</td>
22005 <file name="script.js">
22006 angular.module('orderByExample3', [])
22007 .controller('ExampleController', ['$scope', 'orderByFilter', function($scope, orderBy) {
22009 {name: 'John', phone: '555-1212', age: 10},
22010 {name: 'Mary', phone: '555-9876', age: 19},
22011 {name: 'Mike', phone: '555-4321', age: 21},
22012 {name: 'Adam', phone: '555-5678', age: 35},
22013 {name: 'Julie', phone: '555-8765', age: 29}
22016 $scope.propertyName = 'age';
22017 $scope.reverse = true;
22018 $scope.friends = orderBy(friends, $scope.propertyName, $scope.reverse);
22020 $scope.sortBy = function(propertyName) {
22021 $scope.reverse = (propertyName !== null && $scope.propertyName === propertyName)
22022 ? !$scope.reverse : false;
22023 $scope.propertyName = propertyName;
22024 $scope.friends = orderBy(friends, $scope.propertyName, $scope.reverse);
22028 <file name="style.css">
22030 border-collapse: collapse;
22034 border-bottom: 1px solid;
22036 .friends td, .friends th {
22037 border-left: 1px solid;
22040 .friends td:first-child, .friends th:first-child {
22045 content: '\25b2'; // BLACK UP-POINTING TRIANGLE
22047 .sortorder.reverse:after {
22048 content: '\25bc'; // BLACK DOWN-POINTING TRIANGLE
22051 <file name="protractor.js" type="protractor">
22052 // Element locators
22053 var unsortButton = element(by.partialButtonText('unsorted'));
22054 var nameHeader = element(by.partialButtonText('Name'));
22055 var phoneHeader = element(by.partialButtonText('Phone'));
22056 var ageHeader = element(by.partialButtonText('Age'));
22057 var firstName = element(by.repeater('friends').column('friend.name').row(0));
22058 var lastName = element(by.repeater('friends').column('friend.name').row(4));
22060 it('should sort friends by some property, when clicking on the column header', function() {
22061 expect(firstName.getText()).toBe('Adam');
22062 expect(lastName.getText()).toBe('John');
22064 phoneHeader.click();
22065 expect(firstName.getText()).toBe('John');
22066 expect(lastName.getText()).toBe('Mary');
22068 nameHeader.click();
22069 expect(firstName.getText()).toBe('Adam');
22070 expect(lastName.getText()).toBe('Mike');
22073 expect(firstName.getText()).toBe('John');
22074 expect(lastName.getText()).toBe('Adam');
22077 it('should sort friends in reverse order, when clicking on the same column', function() {
22078 expect(firstName.getText()).toBe('Adam');
22079 expect(lastName.getText()).toBe('John');
22082 expect(firstName.getText()).toBe('John');
22083 expect(lastName.getText()).toBe('Adam');
22086 expect(firstName.getText()).toBe('Adam');
22087 expect(lastName.getText()).toBe('John');
22090 it('should restore the original order, when clicking "Set to unsorted"', function() {
22091 expect(firstName.getText()).toBe('Adam');
22092 expect(lastName.getText()).toBe('John');
22094 unsortButton.click();
22095 expect(firstName.getText()).toBe('John');
22096 expect(lastName.getText()).toBe('Julie');
22103 * ### Using a custom comparator
22105 * If you have very specific requirements about the way items are sorted, you can pass your own
22106 * comparator function. For example, you might need to compare some strings in a locale-sensitive
22107 * way. (When specifying a custom comparator, you also need to pass a value for the `reverse`
22108 * argument - passing `false` retains the default sorting order, i.e. ascending.)
22110 <example name="orderBy-custom-comparator" module="orderByExample4">
22111 <file name="index.html">
22112 <div ng-controller="ExampleController">
22113 <div class="friends-container custom-comparator">
22114 <h3>Locale-sensitive Comparator</h3>
22115 <table class="friends">
22118 <th>Favorite Letter</th>
22120 <tr ng-repeat="friend in friends | orderBy:'favoriteLetter':false:localeSensitiveComparator">
22121 <td>{{friend.name}}</td>
22122 <td>{{friend.favoriteLetter}}</td>
22126 <div class="friends-container default-comparator">
22127 <h3>Default Comparator</h3>
22128 <table class="friends">
22131 <th>Favorite Letter</th>
22133 <tr ng-repeat="friend in friends | orderBy:'favoriteLetter'">
22134 <td>{{friend.name}}</td>
22135 <td>{{friend.favoriteLetter}}</td>
22141 <file name="script.js">
22142 angular.module('orderByExample4', [])
22143 .controller('ExampleController', ['$scope', function($scope) {
22145 {name: 'John', favoriteLetter: 'Ä'},
22146 {name: 'Mary', favoriteLetter: 'Ü'},
22147 {name: 'Mike', favoriteLetter: 'Ö'},
22148 {name: 'Adam', favoriteLetter: 'H'},
22149 {name: 'Julie', favoriteLetter: 'Z'}
22152 $scope.localeSensitiveComparator = function(v1, v2) {
22153 // If we don't get strings, just compare by index
22154 if (v1.type !== 'string' || v2.type !== 'string') {
22155 return (v1.index < v2.index) ? -1 : 1;
22158 // Compare strings alphabetically, taking locale into account
22159 return v1.value.localeCompare(v2.value);
22163 <file name="style.css">
22164 .friends-container {
22165 display: inline-block;
22170 border-collapse: collapse;
22174 border-bottom: 1px solid;
22176 .friends td, .friends th {
22177 border-left: 1px solid;
22180 .friends td:first-child, .friends th:first-child {
22184 <file name="protractor.js" type="protractor">
22185 // Element locators
22186 var container = element(by.css('.custom-comparator'));
22187 var names = container.all(by.repeater('friends').column('friend.name'));
22189 it('should sort friends by favorite letter (in correct alphabetical order)', function() {
22190 expect(names.get(0).getText()).toBe('John');
22191 expect(names.get(1).getText()).toBe('Adam');
22192 expect(names.get(2).getText()).toBe('Mike');
22193 expect(names.get(3).getText()).toBe('Mary');
22194 expect(names.get(4).getText()).toBe('Julie');
22200 orderByFilter.$inject = ['$parse'];
22201 function orderByFilter($parse) {
22202 return function(array, sortPredicate, reverseOrder, compareFn) {
22204 if (array == null) return array;
22205 if (!isArrayLike(array)) {
22206 throw minErr('orderBy')('notarray', 'Expected array but received: {0}', array);
22209 if (!isArray(sortPredicate)) { sortPredicate = [sortPredicate]; }
22210 if (sortPredicate.length === 0) { sortPredicate = ['+']; }
22212 var predicates = processPredicates(sortPredicate);
22214 var descending = reverseOrder ? -1 : 1;
22216 // Define the `compare()` function. Use a default comparator if none is specified.
22217 var compare = isFunction(compareFn) ? compareFn : defaultCompare;
22219 // The next three lines are a version of a Swartzian Transform idiom from Perl
22220 // (sometimes called the Decorate-Sort-Undecorate idiom)
22221 // See https://en.wikipedia.org/wiki/Schwartzian_transform
22222 var compareValues = Array.prototype.map.call(array, getComparisonObject);
22223 compareValues.sort(doComparison);
22224 array = compareValues.map(function(item) { return item.value; });
22228 function getComparisonObject(value, index) {
22229 // NOTE: We are adding an extra `tieBreaker` value based on the element's index.
22230 // This will be used to keep the sort stable when none of the input predicates can
22231 // distinguish between two elements.
22234 tieBreaker: {value: index, type: 'number', index: index},
22235 predicateValues: predicates.map(function(predicate) {
22236 return getPredicateValue(predicate.get(value), index);
22241 function doComparison(v1, v2) {
22242 for (var i = 0, ii = predicates.length; i < ii; i++) {
22243 var result = compare(v1.predicateValues[i], v2.predicateValues[i]);
22245 return result * predicates[i].descending * descending;
22249 return compare(v1.tieBreaker, v2.tieBreaker) * descending;
22253 function processPredicates(sortPredicates) {
22254 return sortPredicates.map(function(predicate) {
22255 var descending = 1, get = identity;
22257 if (isFunction(predicate)) {
22259 } else if (isString(predicate)) {
22260 if ((predicate.charAt(0) === '+' || predicate.charAt(0) === '-')) {
22261 descending = predicate.charAt(0) === '-' ? -1 : 1;
22262 predicate = predicate.substring(1);
22264 if (predicate !== '') {
22265 get = $parse(predicate);
22266 if (get.constant) {
22268 get = function(value) { return value[key]; };
22272 return {get: get, descending: descending};
22276 function isPrimitive(value) {
22277 switch (typeof value) {
22278 case 'number': /* falls through */
22279 case 'boolean': /* falls through */
22287 function objectValue(value) {
22288 // If `valueOf` is a valid function use that
22289 if (isFunction(value.valueOf)) {
22290 value = value.valueOf();
22291 if (isPrimitive(value)) return value;
22293 // If `toString` is a valid function and not the one from `Object.prototype` use that
22294 if (hasCustomToString(value)) {
22295 value = value.toString();
22296 if (isPrimitive(value)) return value;
22302 function getPredicateValue(value, index) {
22303 var type = typeof value;
22304 if (value === null) {
22307 } else if (type === 'object') {
22308 value = objectValue(value);
22310 return {value: value, type: type, index: index};
22313 function defaultCompare(v1, v2) {
22315 var type1 = v1.type;
22316 var type2 = v2.type;
22318 if (type1 === type2) {
22319 var value1 = v1.value;
22320 var value2 = v2.value;
22322 if (type1 === 'string') {
22323 // Compare strings case-insensitively
22324 value1 = value1.toLowerCase();
22325 value2 = value2.toLowerCase();
22326 } else if (type1 === 'object') {
22327 // For basic objects, use the position of the object
22328 // in the collection instead of the value
22329 if (isObject(value1)) value1 = v1.index;
22330 if (isObject(value2)) value2 = v2.index;
22333 if (value1 !== value2) {
22334 result = value1 < value2 ? -1 : 1;
22337 result = type1 < type2 ? -1 : 1;
22344 function ngDirective(directive) {
22345 if (isFunction(directive)) {
22350 directive.restrict = directive.restrict || 'AC';
22351 return valueFn(directive);
22360 * Modifies the default behavior of the html a tag so that the default action is prevented when
22361 * the href attribute is empty.
22363 * For dynamically creating `href` attributes for a tags, see the {@link ng.ngHref `ngHref`} directive.
22365 var htmlAnchorDirective = valueFn({
22367 compile: function(element, attr) {
22368 if (!attr.href && !attr.xlinkHref) {
22369 return function(scope, element) {
22370 // If the linked element is not an anchor tag anymore, do nothing
22371 if (element[0].nodeName.toLowerCase() !== 'a') return;
22373 // SVGAElement does not use the href attribute, but rather the 'xlinkHref' attribute.
22374 var href = toString.call(element.prop('href')) === '[object SVGAnimatedString]' ?
22375 'xlink:href' : 'href';
22376 element.on('click', function(event) {
22377 // if we have no href url, then don't navigate anywhere.
22378 if (!element.attr(href)) {
22379 event.preventDefault();
22394 * Using Angular markup like `{{hash}}` in an href attribute will
22395 * make the link go to the wrong URL if the user clicks it before
22396 * Angular has a chance to replace the `{{hash}}` markup with its
22397 * value. Until Angular replaces the markup the link will be broken
22398 * and will most likely return a 404 error. The `ngHref` directive
22399 * solves this problem.
22401 * The wrong way to write it:
22403 * <a href="http://www.gravatar.com/avatar/{{hash}}">link1</a>
22406 * The correct way to write it:
22408 * <a ng-href="http://www.gravatar.com/avatar/{{hash}}">link1</a>
22412 * @param {template} ngHref any string which can contain `{{}}` markup.
22415 * This example shows various combinations of `href`, `ng-href` and `ng-click` attributes
22416 * in links and their different behaviors:
22417 <example name="ng-href">
22418 <file name="index.html">
22419 <input ng-model="value" /><br />
22420 <a id="link-1" href ng-click="value = 1">link 1</a> (link, don't reload)<br />
22421 <a id="link-2" href="" ng-click="value = 2">link 2</a> (link, don't reload)<br />
22422 <a id="link-3" ng-href="/{{'123'}}">link 3</a> (link, reload!)<br />
22423 <a id="link-4" href="" name="xx" ng-click="value = 4">anchor</a> (link, don't reload)<br />
22424 <a id="link-5" name="xxx" ng-click="value = 5">anchor</a> (no link)<br />
22425 <a id="link-6" ng-href="{{value}}">link</a> (link, change location)
22427 <file name="protractor.js" type="protractor">
22428 it('should execute ng-click but not reload when href without value', function() {
22429 element(by.id('link-1')).click();
22430 expect(element(by.model('value')).getAttribute('value')).toEqual('1');
22431 expect(element(by.id('link-1')).getAttribute('href')).toBe('');
22434 it('should execute ng-click but not reload when href empty string', function() {
22435 element(by.id('link-2')).click();
22436 expect(element(by.model('value')).getAttribute('value')).toEqual('2');
22437 expect(element(by.id('link-2')).getAttribute('href')).toBe('');
22440 it('should execute ng-click and change url when ng-href specified', function() {
22441 expect(element(by.id('link-3')).getAttribute('href')).toMatch(/\/123$/);
22443 element(by.id('link-3')).click();
22445 // At this point, we navigate away from an Angular page, so we need
22446 // to use browser.driver to get the base webdriver.
22448 browser.wait(function() {
22449 return browser.driver.getCurrentUrl().then(function(url) {
22450 return url.match(/\/123$/);
22452 }, 5000, 'page should navigate to /123');
22455 it('should execute ng-click but not reload when href empty string and name specified', function() {
22456 element(by.id('link-4')).click();
22457 expect(element(by.model('value')).getAttribute('value')).toEqual('4');
22458 expect(element(by.id('link-4')).getAttribute('href')).toBe('');
22461 it('should execute ng-click but not reload when no href but name specified', function() {
22462 element(by.id('link-5')).click();
22463 expect(element(by.model('value')).getAttribute('value')).toEqual('5');
22464 expect(element(by.id('link-5')).getAttribute('href')).toBe(null);
22467 it('should only change url when only ng-href', function() {
22468 element(by.model('value')).clear();
22469 element(by.model('value')).sendKeys('6');
22470 expect(element(by.id('link-6')).getAttribute('href')).toMatch(/\/6$/);
22472 element(by.id('link-6')).click();
22474 // At this point, we navigate away from an Angular page, so we need
22475 // to use browser.driver to get the base webdriver.
22476 browser.wait(function() {
22477 return browser.driver.getCurrentUrl().then(function(url) {
22478 return url.match(/\/6$/);
22480 }, 5000, 'page should navigate to /6');
22493 * Using Angular markup like `{{hash}}` in a `src` attribute doesn't
22494 * work right: The browser will fetch from the URL with the literal
22495 * text `{{hash}}` until Angular replaces the expression inside
22496 * `{{hash}}`. The `ngSrc` directive solves this problem.
22498 * The buggy way to write it:
22500 * <img src="http://www.gravatar.com/avatar/{{hash}}" alt="Description"/>
22503 * The correct way to write it:
22505 * <img ng-src="http://www.gravatar.com/avatar/{{hash}}" alt="Description" />
22509 * @param {template} ngSrc any string which can contain `{{}}` markup.
22519 * Using Angular markup like `{{hash}}` in a `srcset` attribute doesn't
22520 * work right: The browser will fetch from the URL with the literal
22521 * text `{{hash}}` until Angular replaces the expression inside
22522 * `{{hash}}`. The `ngSrcset` directive solves this problem.
22524 * The buggy way to write it:
22526 * <img srcset="http://www.gravatar.com/avatar/{{hash}} 2x" alt="Description"/>
22529 * The correct way to write it:
22531 * <img ng-srcset="http://www.gravatar.com/avatar/{{hash}} 2x" alt="Description" />
22535 * @param {template} ngSrcset any string which can contain `{{}}` markup.
22546 * This directive sets the `disabled` attribute on the element (typically a form control,
22547 * e.g. `input`, `button`, `select` etc.) if the
22548 * {@link guide/expression expression} inside `ngDisabled` evaluates to truthy.
22550 * A special directive is necessary because we cannot use interpolation inside the `disabled`
22551 * attribute. See the {@link guide/interpolation interpolation guide} for more info.
22554 <example name="ng-disabled">
22555 <file name="index.html">
22556 <label>Click me to toggle: <input type="checkbox" ng-model="checked"></label><br/>
22557 <button ng-model="button" ng-disabled="checked">Button</button>
22559 <file name="protractor.js" type="protractor">
22560 it('should toggle button', function() {
22561 expect(element(by.css('button')).getAttribute('disabled')).toBeFalsy();
22562 element(by.model('checked')).click();
22563 expect(element(by.css('button')).getAttribute('disabled')).toBeTruthy();
22569 * @param {expression} ngDisabled If the {@link guide/expression expression} is truthy,
22570 * then the `disabled` attribute will be set on the element
22581 * Sets the `checked` attribute on the element, if the expression inside `ngChecked` is truthy.
22583 * Note that this directive should not be used together with {@link ngModel `ngModel`},
22584 * as this can lead to unexpected behavior.
22586 * A special directive is necessary because we cannot use interpolation inside the `checked`
22587 * attribute. See the {@link guide/interpolation interpolation guide} for more info.
22590 <example name="ng-checked">
22591 <file name="index.html">
22592 <label>Check me to check both: <input type="checkbox" ng-model="master"></label><br/>
22593 <input id="checkSlave" type="checkbox" ng-checked="master" aria-label="Slave input">
22595 <file name="protractor.js" type="protractor">
22596 it('should check both checkBoxes', function() {
22597 expect(element(by.id('checkSlave')).getAttribute('checked')).toBeFalsy();
22598 element(by.model('master')).click();
22599 expect(element(by.id('checkSlave')).getAttribute('checked')).toBeTruthy();
22605 * @param {expression} ngChecked If the {@link guide/expression expression} is truthy,
22606 * then the `checked` attribute will be set on the element
22618 * Sets the `readonly` attribute on the element, if the expression inside `ngReadonly` is truthy.
22619 * Note that `readonly` applies only to `input` elements with specific types. [See the input docs on
22620 * MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attr-readonly) for more information.
22622 * A special directive is necessary because we cannot use interpolation inside the `readonly`
22623 * attribute. See the {@link guide/interpolation interpolation guide} for more info.
22626 <example name="ng-readonly">
22627 <file name="index.html">
22628 <label>Check me to make text readonly: <input type="checkbox" ng-model="checked"></label><br/>
22629 <input type="text" ng-readonly="checked" value="I'm Angular" aria-label="Readonly field" />
22631 <file name="protractor.js" type="protractor">
22632 it('should toggle readonly attr', function() {
22633 expect(element(by.css('[type="text"]')).getAttribute('readonly')).toBeFalsy();
22634 element(by.model('checked')).click();
22635 expect(element(by.css('[type="text"]')).getAttribute('readonly')).toBeTruthy();
22641 * @param {expression} ngReadonly If the {@link guide/expression expression} is truthy,
22642 * then special attribute "readonly" will be set on the element
22654 * Sets the `selected` attribute on the element, if the expression inside `ngSelected` is truthy.
22656 * A special directive is necessary because we cannot use interpolation inside the `selected`
22657 * attribute. See the {@link guide/interpolation interpolation guide} for more info.
22659 * <div class="alert alert-warning">
22660 * **Note:** `ngSelected` does not interact with the `select` and `ngModel` directives, it only
22661 * sets the `selected` attribute on the element. If you are using `ngModel` on the select, you
22662 * should not use `ngSelected` on the options, as `ngModel` will set the select value and
22663 * selected options.
22667 <example name="ng-selected">
22668 <file name="index.html">
22669 <label>Check me to select: <input type="checkbox" ng-model="selected"></label><br/>
22670 <select aria-label="ngSelected demo">
22671 <option>Hello!</option>
22672 <option id="greet" ng-selected="selected">Greetings!</option>
22675 <file name="protractor.js" type="protractor">
22676 it('should select Greetings!', function() {
22677 expect(element(by.id('greet')).getAttribute('selected')).toBeFalsy();
22678 element(by.model('selected')).click();
22679 expect(element(by.id('greet')).getAttribute('selected')).toBeTruthy();
22685 * @param {expression} ngSelected If the {@link guide/expression expression} is truthy,
22686 * then special attribute "selected" will be set on the element
22697 * Sets the `open` attribute on the element, if the expression inside `ngOpen` is truthy.
22699 * A special directive is necessary because we cannot use interpolation inside the `open`
22700 * attribute. See the {@link guide/interpolation interpolation guide} for more info.
22702 * ## A note about browser compatibility
22704 * Edge, Firefox, and Internet Explorer do not support the `details` element, it is
22705 * recommended to use {@link ng.ngShow} and {@link ng.ngHide} instead.
22708 <example name="ng-open">
22709 <file name="index.html">
22710 <label>Check me check multiple: <input type="checkbox" ng-model="open"></label><br/>
22711 <details id="details" ng-open="open">
22712 <summary>Show/Hide me</summary>
22715 <file name="protractor.js" type="protractor">
22716 it('should toggle open', function() {
22717 expect(element(by.id('details')).getAttribute('open')).toBeFalsy();
22718 element(by.model('open')).click();
22719 expect(element(by.id('details')).getAttribute('open')).toBeTruthy();
22725 * @param {expression} ngOpen If the {@link guide/expression expression} is truthy,
22726 * then special attribute "open" will be set on the element
22729 var ngAttributeAliasDirectives = {};
22731 // boolean attrs are evaluated
22732 forEach(BOOLEAN_ATTR, function(propName, attrName) {
22733 // binding to multiple is not supported
22734 if (propName === 'multiple') return;
22736 function defaultLinkFn(scope, element, attr) {
22737 scope.$watch(attr[normalized], function ngBooleanAttrWatchAction(value) {
22738 attr.$set(attrName, !!value);
22742 var normalized = directiveNormalize('ng-' + attrName);
22743 var linkFn = defaultLinkFn;
22745 if (propName === 'checked') {
22746 linkFn = function(scope, element, attr) {
22747 // ensuring ngChecked doesn't interfere with ngModel when both are set on the same input
22748 if (attr.ngModel !== attr[normalized]) {
22749 defaultLinkFn(scope, element, attr);
22754 ngAttributeAliasDirectives[normalized] = function() {
22763 // aliased input attrs are evaluated
22764 forEach(ALIASED_ATTR, function(htmlAttr, ngAttr) {
22765 ngAttributeAliasDirectives[ngAttr] = function() {
22768 link: function(scope, element, attr) {
22769 //special case ngPattern when a literal regular expression value
22770 //is used as the expression (this way we don't have to watch anything).
22771 if (ngAttr === 'ngPattern' && attr.ngPattern.charAt(0) === '/') {
22772 var match = attr.ngPattern.match(REGEX_STRING_REGEXP);
22774 attr.$set('ngPattern', new RegExp(match[1], match[2]));
22779 scope.$watch(attr[ngAttr], function ngAttrAliasWatchAction(value) {
22780 attr.$set(ngAttr, value);
22787 // ng-src, ng-srcset, ng-href are interpolated
22788 forEach(['src', 'srcset', 'href'], function(attrName) {
22789 var normalized = directiveNormalize('ng-' + attrName);
22790 ngAttributeAliasDirectives[normalized] = function() {
22792 priority: 99, // it needs to run after the attributes are interpolated
22793 link: function(scope, element, attr) {
22794 var propName = attrName,
22797 if (attrName === 'href' &&
22798 toString.call(element.prop('href')) === '[object SVGAnimatedString]') {
22799 name = 'xlinkHref';
22800 attr.$attr[name] = 'xlink:href';
22804 attr.$observe(normalized, function(value) {
22806 if (attrName === 'href') {
22807 attr.$set(name, null);
22812 attr.$set(name, value);
22814 // Support: IE 9-11 only
22815 // On IE, if "ng:src" directive declaration is used and "src" attribute doesn't exist
22816 // then calling element.setAttribute('src', 'foo') doesn't do anything, so we need
22817 // to set the property as well to achieve the desired effect.
22818 // We use attr[attrName] value since $set can sanitize the url.
22819 if (msie && propName) element.prop(propName, attr[name]);
22826 /* global -nullFormCtrl, -PENDING_CLASS, -SUBMITTED_CLASS
22828 var nullFormCtrl = {
22830 $$renameControl: nullFormRenameControl,
22831 $removeControl: noop,
22832 $setValidity: noop,
22834 $setPristine: noop,
22835 $setSubmitted: noop
22837 PENDING_CLASS = 'ng-pending',
22838 SUBMITTED_CLASS = 'ng-submitted';
22840 function nullFormRenameControl(control, name) {
22841 control.$name = name;
22846 * @name form.FormController
22848 * @property {boolean} $pristine True if user has not interacted with the form yet.
22849 * @property {boolean} $dirty True if user has already interacted with the form.
22850 * @property {boolean} $valid True if all of the containing forms and controls are valid.
22851 * @property {boolean} $invalid True if at least one containing control or form is invalid.
22852 * @property {boolean} $pending True if at least one containing control or form is pending.
22853 * @property {boolean} $submitted True if user has submitted the form even if its invalid.
22855 * @property {Object} $error Is an object hash, containing references to controls or
22856 * forms with failing validators, where:
22858 * - keys are validation tokens (error names),
22859 * - values are arrays of controls or forms that have a failing validator for given error name.
22861 * Built-in validation tokens:
22873 * - `datetimelocal`
22879 * `FormController` keeps track of all its controls and nested forms as well as the state of them,
22880 * such as being valid/invalid or dirty/pristine.
22882 * Each {@link ng.directive:form form} directive creates an instance
22883 * of `FormController`.
22886 //asks for $scope to fool the BC controller module
22887 FormController.$inject = ['$element', '$attrs', '$scope', '$animate', '$interpolate'];
22888 function FormController($element, $attrs, $scope, $animate, $interpolate) {
22889 this.$$controls = [];
22893 this.$$success = {};
22894 this.$pending = undefined;
22895 this.$name = $interpolate($attrs.name || $attrs.ngForm || '')($scope);
22896 this.$dirty = false;
22897 this.$pristine = true;
22898 this.$valid = true;
22899 this.$invalid = false;
22900 this.$submitted = false;
22901 this.$$parentForm = nullFormCtrl;
22903 this.$$element = $element;
22904 this.$$animate = $animate;
22906 setupValidity(this);
22909 FormController.prototype = {
22912 * @name form.FormController#$rollbackViewValue
22915 * Rollback all form controls pending updates to the `$modelValue`.
22917 * Updates may be pending by a debounced event or because the input is waiting for a some future
22918 * event defined in `ng-model-options`. This method is typically needed by the reset button of
22919 * a form that uses `ng-model-options` to pend updates.
22921 $rollbackViewValue: function() {
22922 forEach(this.$$controls, function(control) {
22923 control.$rollbackViewValue();
22929 * @name form.FormController#$commitViewValue
22932 * Commit all form controls pending updates to the `$modelValue`.
22934 * Updates may be pending by a debounced event or because the input is waiting for a some future
22935 * event defined in `ng-model-options`. This method is rarely needed as `NgModelController`
22936 * usually handles calling this in response to input events.
22938 $commitViewValue: function() {
22939 forEach(this.$$controls, function(control) {
22940 control.$commitViewValue();
22946 * @name form.FormController#$addControl
22947 * @param {object} control control object, either a {@link form.FormController} or an
22948 * {@link ngModel.NgModelController}
22951 * Register a control with the form. Input elements using ngModelController do this automatically
22952 * when they are linked.
22954 * Note that the current state of the control will not be reflected on the new parent form. This
22955 * is not an issue with normal use, as freshly compiled and linked controls are in a `$pristine`
22958 * However, if the method is used programmatically, for example by adding dynamically created controls,
22959 * or controls that have been previously removed without destroying their corresponding DOM element,
22960 * it's the developers responsibility to make sure the current state propagates to the parent form.
22962 * For example, if an input control is added that is already `$dirty` and has `$error` properties,
22963 * calling `$setDirty()` and `$validate()` afterwards will propagate the state to the parent form.
22965 $addControl: function(control) {
22966 // Breaking change - before, inputs whose name was "hasOwnProperty" were quietly ignored
22967 // and not added to the scope. Now we throw an error.
22968 assertNotHasOwnProperty(control.$name, 'input');
22969 this.$$controls.push(control);
22971 if (control.$name) {
22972 this[control.$name] = control;
22975 control.$$parentForm = this;
22978 // Private API: rename a form control
22979 $$renameControl: function(control, newName) {
22980 var oldName = control.$name;
22982 if (this[oldName] === control) {
22983 delete this[oldName];
22985 this[newName] = control;
22986 control.$name = newName;
22991 * @name form.FormController#$removeControl
22992 * @param {object} control control object, either a {@link form.FormController} or an
22993 * {@link ngModel.NgModelController}
22996 * Deregister a control from the form.
22998 * Input elements using ngModelController do this automatically when they are destroyed.
23000 * Note that only the removed control's validation state (`$errors`etc.) will be removed from the
23001 * form. `$dirty`, `$submitted` states will not be changed, because the expected behavior can be
23002 * different from case to case. For example, removing the only `$dirty` control from a form may or
23003 * may not mean that the form is still `$dirty`.
23005 $removeControl: function(control) {
23006 if (control.$name && this[control.$name] === control) {
23007 delete this[control.$name];
23009 forEach(this.$pending, function(value, name) {
23010 // eslint-disable-next-line no-invalid-this
23011 this.$setValidity(name, null, control);
23013 forEach(this.$error, function(value, name) {
23014 // eslint-disable-next-line no-invalid-this
23015 this.$setValidity(name, null, control);
23017 forEach(this.$$success, function(value, name) {
23018 // eslint-disable-next-line no-invalid-this
23019 this.$setValidity(name, null, control);
23022 arrayRemove(this.$$controls, control);
23023 control.$$parentForm = nullFormCtrl;
23028 * @name form.FormController#$setDirty
23031 * Sets the form to a dirty state.
23033 * This method can be called to add the 'ng-dirty' class and set the form to a dirty
23034 * state (ng-dirty class). This method will also propagate to parent forms.
23036 $setDirty: function() {
23037 this.$$animate.removeClass(this.$$element, PRISTINE_CLASS);
23038 this.$$animate.addClass(this.$$element, DIRTY_CLASS);
23039 this.$dirty = true;
23040 this.$pristine = false;
23041 this.$$parentForm.$setDirty();
23046 * @name form.FormController#$setPristine
23049 * Sets the form to its pristine state.
23051 * This method sets the form's `$pristine` state to true, the `$dirty` state to false, removes
23052 * the `ng-dirty` class and adds the `ng-pristine` class. Additionally, it sets the `$submitted`
23055 * This method will also propagate to all the controls contained in this form.
23057 * Setting a form back to a pristine state is often useful when we want to 'reuse' a form after
23058 * saving or resetting it.
23060 $setPristine: function() {
23061 this.$$animate.setClass(this.$$element, PRISTINE_CLASS, DIRTY_CLASS + ' ' + SUBMITTED_CLASS);
23062 this.$dirty = false;
23063 this.$pristine = true;
23064 this.$submitted = false;
23065 forEach(this.$$controls, function(control) {
23066 control.$setPristine();
23072 * @name form.FormController#$setUntouched
23075 * Sets the form to its untouched state.
23077 * This method can be called to remove the 'ng-touched' class and set the form controls to their
23078 * untouched state (ng-untouched class).
23080 * Setting a form controls back to their untouched state is often useful when setting the form
23081 * back to its pristine state.
23083 $setUntouched: function() {
23084 forEach(this.$$controls, function(control) {
23085 control.$setUntouched();
23091 * @name form.FormController#$setSubmitted
23094 * Sets the form to its submitted state.
23096 $setSubmitted: function() {
23097 this.$$animate.addClass(this.$$element, SUBMITTED_CLASS);
23098 this.$submitted = true;
23099 this.$$parentForm.$setSubmitted();
23105 * @name form.FormController#$setValidity
23108 * Sets the validity of a form control.
23110 * This method will also propagate to parent forms.
23112 addSetValidityMethod({
23113 clazz: FormController,
23114 set: function(object, property, controller) {
23115 var list = object[property];
23117 object[property] = [controller];
23119 var index = list.indexOf(controller);
23120 if (index === -1) {
23121 list.push(controller);
23125 unset: function(object, property, controller) {
23126 var list = object[property];
23130 arrayRemove(list, controller);
23131 if (list.length === 0) {
23132 delete object[property];
23143 * Nestable alias of {@link ng.directive:form `form`} directive. HTML
23144 * does not allow nesting of form elements. It is useful to nest forms, for example if the validity of a
23145 * sub-group of controls needs to be determined.
23147 * Note: the purpose of `ngForm` is to group controls,
23148 * but not to be a replacement for the `<form>` tag with all of its capabilities
23149 * (e.g. posting to the server, ...).
23151 * @param {string=} ngForm|name Name of the form. If specified, the form controller will be published into
23152 * related scope, under this name.
23162 * Directive that instantiates
23163 * {@link form.FormController FormController}.
23165 * If the `name` attribute is specified, the form controller is published onto the current scope under
23168 * # Alias: {@link ng.directive:ngForm `ngForm`}
23170 * In Angular, forms can be nested. This means that the outer form is valid when all of the child
23171 * forms are valid as well. However, browsers do not allow nesting of `<form>` elements, so
23172 * Angular provides the {@link ng.directive:ngForm `ngForm`} directive, which behaves identically to
23173 * `form` but can be nested. Nested forms can be useful, for example, if the validity of a sub-group
23174 * of controls needs to be determined.
23177 * - `ng-valid` is set if the form is valid.
23178 * - `ng-invalid` is set if the form is invalid.
23179 * - `ng-pending` is set if the form is pending.
23180 * - `ng-pristine` is set if the form is pristine.
23181 * - `ng-dirty` is set if the form is dirty.
23182 * - `ng-submitted` is set if the form was submitted.
23184 * Keep in mind that ngAnimate can detect each of these classes when added and removed.
23187 * # Submitting a form and preventing the default action
23189 * Since the role of forms in client-side Angular applications is different than in classical
23190 * roundtrip apps, it is desirable for the browser not to translate the form submission into a full
23191 * page reload that sends the data to the server. Instead some javascript logic should be triggered
23192 * to handle the form submission in an application-specific way.
23194 * For this reason, Angular prevents the default action (form submission to the server) unless the
23195 * `<form>` element has an `action` attribute specified.
23197 * You can use one of the following two ways to specify what javascript method should be called when
23198 * a form is submitted:
23200 * - {@link ng.directive:ngSubmit ngSubmit} directive on the form element
23201 * - {@link ng.directive:ngClick ngClick} directive on the first
23202 * button or input field of type submit (input[type=submit])
23204 * To prevent double execution of the handler, use only one of the {@link ng.directive:ngSubmit ngSubmit}
23205 * or {@link ng.directive:ngClick ngClick} directives.
23206 * This is because of the following form submission rules in the HTML specification:
23208 * - If a form has only one input field then hitting enter in this field triggers form submit
23210 * - if a form has 2+ input fields and no buttons or input[type=submit] then hitting enter
23211 * doesn't trigger submit
23212 * - if a form has one or more input fields and one or more buttons or input[type=submit] then
23213 * hitting enter in any of the input fields will trigger the click handler on the *first* button or
23214 * input[type=submit] (`ngClick`) *and* a submit handler on the enclosing form (`ngSubmit`)
23216 * Any pending `ngModelOptions` changes will take place immediately when an enclosing form is
23217 * submitted. Note that `ngClick` events will occur before the model is updated. Use `ngSubmit`
23218 * to have access to the updated model.
23220 * ## Animation Hooks
23222 * Animations in ngForm are triggered when any of the associated CSS classes are added and removed.
23223 * These classes are: `.ng-pristine`, `.ng-dirty`, `.ng-invalid` and `.ng-valid` as well as any
23224 * other validations that are performed within the form. Animations in ngForm are similar to how
23225 * they work in ngClass and animations can be hooked into using CSS transitions, keyframes as well
23226 * as JS animations.
23228 * The following example shows a simple way to utilize CSS transitions to style a form element
23229 * that has been rendered as invalid after it has been validated:
23232 * //be sure to include ngAnimate as a module to hook into more
23233 * //advanced animations
23235 * transition:0.5s linear all;
23236 * background: white;
23238 * .my-form.ng-invalid {
23245 <example name="ng-form" deps="angular-animate.js" animations="true" fixBase="true" module="formExample">
23246 <file name="index.html">
23248 angular.module('formExample', [])
23249 .controller('FormController', ['$scope', function($scope) {
23250 $scope.userType = 'guest';
23255 transition:all linear 0.5s;
23256 background: transparent;
23258 .my-form.ng-invalid {
23262 <form name="myForm" ng-controller="FormController" class="my-form">
23263 userType: <input name="input" ng-model="userType" required>
23264 <span class="error" ng-show="myForm.input.$error.required">Required!</span><br>
23265 <code>userType = {{userType}}</code><br>
23266 <code>myForm.input.$valid = {{myForm.input.$valid}}</code><br>
23267 <code>myForm.input.$error = {{myForm.input.$error}}</code><br>
23268 <code>myForm.$valid = {{myForm.$valid}}</code><br>
23269 <code>myForm.$error.required = {{!!myForm.$error.required}}</code><br>
23272 <file name="protractor.js" type="protractor">
23273 it('should initialize to model', function() {
23274 var userType = element(by.binding('userType'));
23275 var valid = element(by.binding('myForm.input.$valid'));
23277 expect(userType.getText()).toContain('guest');
23278 expect(valid.getText()).toContain('true');
23281 it('should be invalid if empty', function() {
23282 var userType = element(by.binding('userType'));
23283 var valid = element(by.binding('myForm.input.$valid'));
23284 var userInput = element(by.model('userType'));
23287 userInput.sendKeys('');
23289 expect(userType.getText()).toEqual('userType =');
23290 expect(valid.getText()).toContain('false');
23295 * @param {string=} name Name of the form. If specified, the form controller will be published into
23296 * related scope, under this name.
23298 var formDirectiveFactory = function(isNgForm) {
23299 return ['$timeout', '$parse', function($timeout, $parse) {
23300 var formDirective = {
23302 restrict: isNgForm ? 'EAC' : 'E',
23303 require: ['form', '^^?form'], //first is the form's own ctrl, second is an optional parent form
23304 controller: FormController,
23305 compile: function ngFormCompile(formElement, attr) {
23306 // Setup initial state of the control
23307 formElement.addClass(PRISTINE_CLASS).addClass(VALID_CLASS);
23309 var nameAttr = attr.name ? 'name' : (isNgForm && attr.ngForm ? 'ngForm' : false);
23312 pre: function ngFormPreLink(scope, formElement, attr, ctrls) {
23313 var controller = ctrls[0];
23315 // if `action` attr is not present on the form, prevent the default action (submission)
23316 if (!('action' in attr)) {
23317 // we can't use jq events because if a form is destroyed during submission the default
23318 // action is not prevented. see #1238
23320 // IE 9 is not affected because it doesn't fire a submit event and try to do a full
23321 // page reload if the form was destroyed by submission of the form via a click handler
23322 // on a button in the form. Looks like an IE9 specific bug.
23323 var handleFormSubmission = function(event) {
23324 scope.$apply(function() {
23325 controller.$commitViewValue();
23326 controller.$setSubmitted();
23329 event.preventDefault();
23332 formElement[0].addEventListener('submit', handleFormSubmission);
23334 // unregister the preventDefault listener so that we don't not leak memory but in a
23335 // way that will achieve the prevention of the default action.
23336 formElement.on('$destroy', function() {
23337 $timeout(function() {
23338 formElement[0].removeEventListener('submit', handleFormSubmission);
23343 var parentFormCtrl = ctrls[1] || controller.$$parentForm;
23344 parentFormCtrl.$addControl(controller);
23346 var setter = nameAttr ? getSetter(controller.$name) : noop;
23349 setter(scope, controller);
23350 attr.$observe(nameAttr, function(newValue) {
23351 if (controller.$name === newValue) return;
23352 setter(scope, undefined);
23353 controller.$$parentForm.$$renameControl(controller, newValue);
23354 setter = getSetter(controller.$name);
23355 setter(scope, controller);
23358 formElement.on('$destroy', function() {
23359 controller.$$parentForm.$removeControl(controller);
23360 setter(scope, undefined);
23361 extend(controller, nullFormCtrl); //stop propagating child destruction handlers upwards
23368 return formDirective;
23370 function getSetter(expression) {
23371 if (expression === '') {
23372 //create an assignable expression, so forms with an empty name can be renamed later
23373 return $parse('this[""]').assign;
23375 return $parse(expression).assign || noop;
23380 var formDirective = formDirectiveFactory();
23381 var ngFormDirective = formDirectiveFactory(true);
23386 function setupValidity(instance) {
23387 instance.$$classCache = {};
23388 instance.$$classCache[INVALID_CLASS] = !(instance.$$classCache[VALID_CLASS] = instance.$$element.hasClass(VALID_CLASS));
23390 function addSetValidityMethod(context) {
23391 var clazz = context.clazz,
23393 unset = context.unset;
23395 clazz.prototype.$setValidity = function(validationErrorKey, state, controller) {
23396 if (isUndefined(state)) {
23397 createAndSet(this, '$pending', validationErrorKey, controller);
23399 unsetAndCleanup(this, '$pending', validationErrorKey, controller);
23401 if (!isBoolean(state)) {
23402 unset(this.$error, validationErrorKey, controller);
23403 unset(this.$$success, validationErrorKey, controller);
23406 unset(this.$error, validationErrorKey, controller);
23407 set(this.$$success, validationErrorKey, controller);
23409 set(this.$error, validationErrorKey, controller);
23410 unset(this.$$success, validationErrorKey, controller);
23413 if (this.$pending) {
23414 cachedToggleClass(this, PENDING_CLASS, true);
23415 this.$valid = this.$invalid = undefined;
23416 toggleValidationCss(this, '', null);
23418 cachedToggleClass(this, PENDING_CLASS, false);
23419 this.$valid = isObjectEmpty(this.$error);
23420 this.$invalid = !this.$valid;
23421 toggleValidationCss(this, '', this.$valid);
23424 // re-read the state as the set/unset methods could have
23425 // combined state in this.$error[validationError] (used for forms),
23426 // where setting/unsetting only increments/decrements the value,
23427 // and does not replace it.
23429 if (this.$pending && this.$pending[validationErrorKey]) {
23430 combinedState = undefined;
23431 } else if (this.$error[validationErrorKey]) {
23432 combinedState = false;
23433 } else if (this.$$success[validationErrorKey]) {
23434 combinedState = true;
23436 combinedState = null;
23439 toggleValidationCss(this, validationErrorKey, combinedState);
23440 this.$$parentForm.$setValidity(validationErrorKey, combinedState, this);
23443 function createAndSet(ctrl, name, value, controller) {
23447 set(ctrl[name], value, controller);
23450 function unsetAndCleanup(ctrl, name, value, controller) {
23452 unset(ctrl[name], value, controller);
23454 if (isObjectEmpty(ctrl[name])) {
23455 ctrl[name] = undefined;
23459 function cachedToggleClass(ctrl, className, switchValue) {
23460 if (switchValue && !ctrl.$$classCache[className]) {
23461 ctrl.$$animate.addClass(ctrl.$$element, className);
23462 ctrl.$$classCache[className] = true;
23463 } else if (!switchValue && ctrl.$$classCache[className]) {
23464 ctrl.$$animate.removeClass(ctrl.$$element, className);
23465 ctrl.$$classCache[className] = false;
23469 function toggleValidationCss(ctrl, validationErrorKey, isValid) {
23470 validationErrorKey = validationErrorKey ? '-' + snake_case(validationErrorKey, '-') : '';
23472 cachedToggleClass(ctrl, VALID_CLASS + validationErrorKey, isValid === true);
23473 cachedToggleClass(ctrl, INVALID_CLASS + validationErrorKey, isValid === false);
23477 function isObjectEmpty(obj) {
23479 for (var prop in obj) {
23480 if (obj.hasOwnProperty(prop)) {
23489 VALID_CLASS: false,
23490 INVALID_CLASS: false,
23491 PRISTINE_CLASS: false,
23492 DIRTY_CLASS: false,
23493 ngModelMinErr: false
23496 // Regex code was initially obtained from SO prior to modification: https://stackoverflow.com/questions/3143070/javascript-regex-iso-datetime#answer-3143231
23497 var 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)$/;
23498 // See valid URLs in RFC3987 (http://tools.ietf.org/html/rfc3987)
23499 // Note: We are being more lenient, because browsers are too.
23509 // 1111111111111111 222 333333 44444 55555555555555555555555 666 77777777 8888888 999
23510 var URL_REGEXP = /^[a-z][a-z\d.+-]*:\/*(?:[^:@]+(?::[^@]+)?@)?(?:[^\s:/?#]+|\[[a-f\d:]+])(?::\d+)?(?:\/[^?#]*)?(?:\?[^#]*)?(?:#.*)?$/i;
23511 // eslint-disable-next-line max-len
23512 var EMAIL_REGEXP = /^(?=.{1,254}$)(?=.{1,64}@)[-!#$%&'*+/0-9=?A-Z^_`a-z{|}~]+(\.[-!#$%&'*+/0-9=?A-Z^_`a-z{|}~]+)*@[A-Za-z0-9]([A-Za-z0-9-]{0,61}[A-Za-z0-9])?(\.[A-Za-z0-9]([A-Za-z0-9-]{0,61}[A-Za-z0-9])?)*$/;
23513 var NUMBER_REGEXP = /^\s*(-|\+)?(\d+|(\d*(\.\d*)))([eE][+-]?\d+)?\s*$/;
23514 var DATE_REGEXP = /^(\d{4,})-(\d{2})-(\d{2})$/;
23515 var DATETIMELOCAL_REGEXP = /^(\d{4,})-(\d\d)-(\d\d)T(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
23516 var WEEK_REGEXP = /^(\d{4,})-W(\d\d)$/;
23517 var MONTH_REGEXP = /^(\d{4,})-(\d\d)$/;
23518 var TIME_REGEXP = /^(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
23520 var PARTIAL_VALIDATION_EVENTS = 'keydown wheel mousedown';
23521 var PARTIAL_VALIDATION_TYPES = createMap();
23522 forEach('date,datetime-local,month,time,week'.split(','), function(type) {
23523 PARTIAL_VALIDATION_TYPES[type] = true;
23530 * @name input[text]
23533 * Standard HTML text input with angular data binding, inherited by most of the `input` elements.
23536 * @param {string} ngModel Assignable angular expression to data-bind to.
23537 * @param {string=} name Property name of the form under which the control is published.
23538 * @param {string=} required Adds `required` validation error key if the value is not entered.
23539 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
23540 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
23541 * `required` when you want to data-bind to the `required` attribute.
23542 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
23544 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
23545 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
23547 * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
23548 * that contains the regular expression body that will be converted to a regular expression
23549 * as in the ngPattern directive.
23550 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
23551 * does not match a RegExp found by evaluating the Angular expression given in the attribute value.
23552 * If the expression evaluates to a RegExp object, then this is used directly.
23553 * If the expression evaluates to a string, then it will be converted to a RegExp
23554 * after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
23555 * `new RegExp('^abc$')`.<br />
23556 * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
23557 * start at the index of the last search's match, thus not taking the whole input value into
23559 * @param {string=} ngChange Angular expression to be executed when input changes due to user
23560 * interaction with the input element.
23561 * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
23562 * This parameter is ignored for input[type=password] controls, which will never trim the
23566 <example name="text-input-directive" module="textInputExample">
23567 <file name="index.html">
23569 angular.module('textInputExample', [])
23570 .controller('ExampleController', ['$scope', function($scope) {
23573 word: /^\s*\w*\s*$/
23577 <form name="myForm" ng-controller="ExampleController">
23578 <label>Single word:
23579 <input type="text" name="input" ng-model="example.text"
23580 ng-pattern="example.word" required ng-trim="false">
23583 <span class="error" ng-show="myForm.input.$error.required">
23585 <span class="error" ng-show="myForm.input.$error.pattern">
23586 Single word only!</span>
23588 <code>text = {{example.text}}</code><br/>
23589 <code>myForm.input.$valid = {{myForm.input.$valid}}</code><br/>
23590 <code>myForm.input.$error = {{myForm.input.$error}}</code><br/>
23591 <code>myForm.$valid = {{myForm.$valid}}</code><br/>
23592 <code>myForm.$error.required = {{!!myForm.$error.required}}</code><br/>
23595 <file name="protractor.js" type="protractor">
23596 var text = element(by.binding('example.text'));
23597 var valid = element(by.binding('myForm.input.$valid'));
23598 var input = element(by.model('example.text'));
23600 it('should initialize to model', function() {
23601 expect(text.getText()).toContain('guest');
23602 expect(valid.getText()).toContain('true');
23605 it('should be invalid if empty', function() {
23607 input.sendKeys('');
23609 expect(text.getText()).toEqual('text =');
23610 expect(valid.getText()).toContain('false');
23613 it('should be invalid if multi word', function() {
23615 input.sendKeys('hello world');
23617 expect(valid.getText()).toContain('false');
23622 'text': textInputType,
23626 * @name input[date]
23629 * Input with date validation and transformation. In browsers that do not yet support
23630 * the HTML5 date input, a text element will be used. In that case, text must be entered in a valid ISO-8601
23631 * date format (yyyy-MM-dd), for example: `2009-01-06`. Since many
23632 * modern browsers do not yet support this input type, it is important to provide cues to users on the
23633 * expected input format via a placeholder or label.
23635 * The model must always be a Date object, otherwise Angular will throw an error.
23636 * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
23638 * The timezone to be used to read/write the `Date` instance in the model can be defined using
23639 * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
23641 * @param {string} ngModel Assignable angular expression to data-bind to.
23642 * @param {string=} name Property name of the form under which the control is published.
23643 * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be a
23644 * valid ISO date string (yyyy-MM-dd). You can also use interpolation inside this attribute
23645 * (e.g. `min="{{minDate | date:'yyyy-MM-dd'}}"`). Note that `min` will also add native HTML5
23646 * constraint validation.
23647 * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must be
23648 * a valid ISO date string (yyyy-MM-dd). You can also use interpolation inside this attribute
23649 * (e.g. `max="{{maxDate | date:'yyyy-MM-dd'}}"`). Note that `max` will also add native HTML5
23650 * constraint validation.
23651 * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO date string
23652 * the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
23653 * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO date string
23654 * the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
23655 * @param {string=} required Sets `required` validation error key if the value is not entered.
23656 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
23657 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
23658 * `required` when you want to data-bind to the `required` attribute.
23659 * @param {string=} ngChange Angular expression to be executed when input changes due to user
23660 * interaction with the input element.
23663 <example name="date-input-directive" module="dateInputExample">
23664 <file name="index.html">
23666 angular.module('dateInputExample', [])
23667 .controller('DateController', ['$scope', function($scope) {
23669 value: new Date(2013, 9, 22)
23673 <form name="myForm" ng-controller="DateController as dateCtrl">
23674 <label for="exampleInput">Pick a date in 2013:</label>
23675 <input type="date" id="exampleInput" name="input" ng-model="example.value"
23676 placeholder="yyyy-MM-dd" min="2013-01-01" max="2013-12-31" required />
23678 <span class="error" ng-show="myForm.input.$error.required">
23680 <span class="error" ng-show="myForm.input.$error.date">
23681 Not a valid date!</span>
23683 <tt>value = {{example.value | date: "yyyy-MM-dd"}}</tt><br/>
23684 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
23685 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
23686 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
23687 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
23690 <file name="protractor.js" type="protractor">
23691 var value = element(by.binding('example.value | date: "yyyy-MM-dd"'));
23692 var valid = element(by.binding('myForm.input.$valid'));
23694 // currently protractor/webdriver does not support
23695 // sending keys to all known HTML5 input controls
23696 // for various browsers (see https://github.com/angular/protractor/issues/562).
23697 function setInput(val) {
23698 // set the value of the element and force validation.
23699 var scr = "var ipt = document.getElementById('exampleInput'); " +
23700 "ipt.value = '" + val + "';" +
23701 "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
23702 browser.executeScript(scr);
23705 it('should initialize to model', function() {
23706 expect(value.getText()).toContain('2013-10-22');
23707 expect(valid.getText()).toContain('myForm.input.$valid = true');
23710 it('should be invalid if empty', function() {
23712 expect(value.getText()).toEqual('value =');
23713 expect(valid.getText()).toContain('myForm.input.$valid = false');
23716 it('should be invalid if over max', function() {
23717 setInput('2015-01-01');
23718 expect(value.getText()).toContain('');
23719 expect(valid.getText()).toContain('myForm.input.$valid = false');
23724 'date': createDateInputType('date', DATE_REGEXP,
23725 createDateParser(DATE_REGEXP, ['yyyy', 'MM', 'dd']),
23730 * @name input[datetime-local]
23733 * Input with datetime validation and transformation. In browsers that do not yet support
23734 * the HTML5 date input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
23735 * local datetime format (yyyy-MM-ddTHH:mm:ss), for example: `2010-12-28T14:57:00`.
23737 * The model must always be a Date object, otherwise Angular will throw an error.
23738 * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
23740 * The timezone to be used to read/write the `Date` instance in the model can be defined using
23741 * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
23743 * @param {string} ngModel Assignable angular expression to data-bind to.
23744 * @param {string=} name Property name of the form under which the control is published.
23745 * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
23746 * This must be a valid ISO datetime format (yyyy-MM-ddTHH:mm:ss). You can also use interpolation
23747 * inside this attribute (e.g. `min="{{minDatetimeLocal | date:'yyyy-MM-ddTHH:mm:ss'}}"`).
23748 * Note that `min` will also add native HTML5 constraint validation.
23749 * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
23750 * This must be a valid ISO datetime format (yyyy-MM-ddTHH:mm:ss). You can also use interpolation
23751 * inside this attribute (e.g. `max="{{maxDatetimeLocal | date:'yyyy-MM-ddTHH:mm:ss'}}"`).
23752 * Note that `max` will also add native HTML5 constraint validation.
23753 * @param {(date|string)=} ngMin Sets the `min` validation error key to the Date / ISO datetime string
23754 * the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
23755 * @param {(date|string)=} ngMax Sets the `max` validation error key to the Date / ISO datetime string
23756 * the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
23757 * @param {string=} required Sets `required` validation error key if the value is not entered.
23758 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
23759 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
23760 * `required` when you want to data-bind to the `required` attribute.
23761 * @param {string=} ngChange Angular expression to be executed when input changes due to user
23762 * interaction with the input element.
23765 <example name="datetimelocal-input-directive" module="dateExample">
23766 <file name="index.html">
23768 angular.module('dateExample', [])
23769 .controller('DateController', ['$scope', function($scope) {
23771 value: new Date(2010, 11, 28, 14, 57)
23775 <form name="myForm" ng-controller="DateController as dateCtrl">
23776 <label for="exampleInput">Pick a date between in 2013:</label>
23777 <input type="datetime-local" id="exampleInput" name="input" ng-model="example.value"
23778 placeholder="yyyy-MM-ddTHH:mm:ss" min="2001-01-01T00:00:00" max="2013-12-31T00:00:00" required />
23780 <span class="error" ng-show="myForm.input.$error.required">
23782 <span class="error" ng-show="myForm.input.$error.datetimelocal">
23783 Not a valid date!</span>
23785 <tt>value = {{example.value | date: "yyyy-MM-ddTHH:mm:ss"}}</tt><br/>
23786 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
23787 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
23788 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
23789 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
23792 <file name="protractor.js" type="protractor">
23793 var value = element(by.binding('example.value | date: "yyyy-MM-ddTHH:mm:ss"'));
23794 var valid = element(by.binding('myForm.input.$valid'));
23796 // currently protractor/webdriver does not support
23797 // sending keys to all known HTML5 input controls
23798 // for various browsers (https://github.com/angular/protractor/issues/562).
23799 function setInput(val) {
23800 // set the value of the element and force validation.
23801 var scr = "var ipt = document.getElementById('exampleInput'); " +
23802 "ipt.value = '" + val + "';" +
23803 "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
23804 browser.executeScript(scr);
23807 it('should initialize to model', function() {
23808 expect(value.getText()).toContain('2010-12-28T14:57:00');
23809 expect(valid.getText()).toContain('myForm.input.$valid = true');
23812 it('should be invalid if empty', function() {
23814 expect(value.getText()).toEqual('value =');
23815 expect(valid.getText()).toContain('myForm.input.$valid = false');
23818 it('should be invalid if over max', function() {
23819 setInput('2015-01-01T23:59:00');
23820 expect(value.getText()).toContain('');
23821 expect(valid.getText()).toContain('myForm.input.$valid = false');
23826 'datetime-local': createDateInputType('datetimelocal', DATETIMELOCAL_REGEXP,
23827 createDateParser(DATETIMELOCAL_REGEXP, ['yyyy', 'MM', 'dd', 'HH', 'mm', 'ss', 'sss']),
23828 'yyyy-MM-ddTHH:mm:ss.sss'),
23832 * @name input[time]
23835 * Input with time validation and transformation. In browsers that do not yet support
23836 * the HTML5 time input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
23837 * local time format (HH:mm:ss), for example: `14:57:00`. Model must be a Date object. This binding will always output a
23838 * Date object to the model of January 1, 1970, or local date `new Date(1970, 0, 1, HH, mm, ss)`.
23840 * The model must always be a Date object, otherwise Angular will throw an error.
23841 * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
23843 * The timezone to be used to read/write the `Date` instance in the model can be defined using
23844 * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
23846 * @param {string} ngModel Assignable angular expression to data-bind to.
23847 * @param {string=} name Property name of the form under which the control is published.
23848 * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
23849 * This must be a valid ISO time format (HH:mm:ss). You can also use interpolation inside this
23850 * attribute (e.g. `min="{{minTime | date:'HH:mm:ss'}}"`). Note that `min` will also add
23851 * native HTML5 constraint validation.
23852 * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
23853 * This must be a valid ISO time format (HH:mm:ss). You can also use interpolation inside this
23854 * attribute (e.g. `max="{{maxTime | date:'HH:mm:ss'}}"`). Note that `max` will also add
23855 * native HTML5 constraint validation.
23856 * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO time string the
23857 * `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
23858 * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO time string the
23859 * `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
23860 * @param {string=} required Sets `required` validation error key if the value is not entered.
23861 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
23862 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
23863 * `required` when you want to data-bind to the `required` attribute.
23864 * @param {string=} ngChange Angular expression to be executed when input changes due to user
23865 * interaction with the input element.
23868 <example name="time-input-directive" module="timeExample">
23869 <file name="index.html">
23871 angular.module('timeExample', [])
23872 .controller('DateController', ['$scope', function($scope) {
23874 value: new Date(1970, 0, 1, 14, 57, 0)
23878 <form name="myForm" ng-controller="DateController as dateCtrl">
23879 <label for="exampleInput">Pick a time between 8am and 5pm:</label>
23880 <input type="time" id="exampleInput" name="input" ng-model="example.value"
23881 placeholder="HH:mm:ss" min="08:00:00" max="17:00:00" required />
23883 <span class="error" ng-show="myForm.input.$error.required">
23885 <span class="error" ng-show="myForm.input.$error.time">
23886 Not a valid date!</span>
23888 <tt>value = {{example.value | date: "HH:mm:ss"}}</tt><br/>
23889 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
23890 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
23891 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
23892 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
23895 <file name="protractor.js" type="protractor">
23896 var value = element(by.binding('example.value | date: "HH:mm:ss"'));
23897 var valid = element(by.binding('myForm.input.$valid'));
23899 // currently protractor/webdriver does not support
23900 // sending keys to all known HTML5 input controls
23901 // for various browsers (https://github.com/angular/protractor/issues/562).
23902 function setInput(val) {
23903 // set the value of the element and force validation.
23904 var scr = "var ipt = document.getElementById('exampleInput'); " +
23905 "ipt.value = '" + val + "';" +
23906 "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
23907 browser.executeScript(scr);
23910 it('should initialize to model', function() {
23911 expect(value.getText()).toContain('14:57:00');
23912 expect(valid.getText()).toContain('myForm.input.$valid = true');
23915 it('should be invalid if empty', function() {
23917 expect(value.getText()).toEqual('value =');
23918 expect(valid.getText()).toContain('myForm.input.$valid = false');
23921 it('should be invalid if over max', function() {
23922 setInput('23:59:00');
23923 expect(value.getText()).toContain('');
23924 expect(valid.getText()).toContain('myForm.input.$valid = false');
23929 'time': createDateInputType('time', TIME_REGEXP,
23930 createDateParser(TIME_REGEXP, ['HH', 'mm', 'ss', 'sss']),
23935 * @name input[week]
23938 * Input with week-of-the-year validation and transformation to Date. In browsers that do not yet support
23939 * the HTML5 week input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
23940 * week format (yyyy-W##), for example: `2013-W02`.
23942 * The model must always be a Date object, otherwise Angular will throw an error.
23943 * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
23945 * The timezone to be used to read/write the `Date` instance in the model can be defined using
23946 * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
23948 * @param {string} ngModel Assignable angular expression to data-bind to.
23949 * @param {string=} name Property name of the form under which the control is published.
23950 * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
23951 * This must be a valid ISO week format (yyyy-W##). You can also use interpolation inside this
23952 * attribute (e.g. `min="{{minWeek | date:'yyyy-Www'}}"`). Note that `min` will also add
23953 * native HTML5 constraint validation.
23954 * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
23955 * This must be a valid ISO week format (yyyy-W##). You can also use interpolation inside this
23956 * attribute (e.g. `max="{{maxWeek | date:'yyyy-Www'}}"`). Note that `max` will also add
23957 * native HTML5 constraint validation.
23958 * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO week string
23959 * the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
23960 * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO week string
23961 * the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
23962 * @param {string=} required Sets `required` validation error key if the value is not entered.
23963 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
23964 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
23965 * `required` when you want to data-bind to the `required` attribute.
23966 * @param {string=} ngChange Angular expression to be executed when input changes due to user
23967 * interaction with the input element.
23970 <example name="week-input-directive" module="weekExample">
23971 <file name="index.html">
23973 angular.module('weekExample', [])
23974 .controller('DateController', ['$scope', function($scope) {
23976 value: new Date(2013, 0, 3)
23980 <form name="myForm" ng-controller="DateController as dateCtrl">
23981 <label>Pick a date between in 2013:
23982 <input id="exampleInput" type="week" name="input" ng-model="example.value"
23983 placeholder="YYYY-W##" min="2012-W32"
23984 max="2013-W52" required />
23987 <span class="error" ng-show="myForm.input.$error.required">
23989 <span class="error" ng-show="myForm.input.$error.week">
23990 Not a valid date!</span>
23992 <tt>value = {{example.value | date: "yyyy-Www"}}</tt><br/>
23993 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
23994 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
23995 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
23996 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
23999 <file name="protractor.js" type="protractor">
24000 var value = element(by.binding('example.value | date: "yyyy-Www"'));
24001 var valid = element(by.binding('myForm.input.$valid'));
24003 // currently protractor/webdriver does not support
24004 // sending keys to all known HTML5 input controls
24005 // for various browsers (https://github.com/angular/protractor/issues/562).
24006 function setInput(val) {
24007 // set the value of the element and force validation.
24008 var scr = "var ipt = document.getElementById('exampleInput'); " +
24009 "ipt.value = '" + val + "';" +
24010 "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
24011 browser.executeScript(scr);
24014 it('should initialize to model', function() {
24015 expect(value.getText()).toContain('2013-W01');
24016 expect(valid.getText()).toContain('myForm.input.$valid = true');
24019 it('should be invalid if empty', function() {
24021 expect(value.getText()).toEqual('value =');
24022 expect(valid.getText()).toContain('myForm.input.$valid = false');
24025 it('should be invalid if over max', function() {
24026 setInput('2015-W01');
24027 expect(value.getText()).toContain('');
24028 expect(valid.getText()).toContain('myForm.input.$valid = false');
24033 'week': createDateInputType('week', WEEK_REGEXP, weekParser, 'yyyy-Www'),
24037 * @name input[month]
24040 * Input with month validation and transformation. In browsers that do not yet support
24041 * the HTML5 month input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
24042 * month format (yyyy-MM), for example: `2009-01`.
24044 * The model must always be a Date object, otherwise Angular will throw an error.
24045 * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
24046 * If the model is not set to the first of the month, the next view to model update will set it
24047 * to the first of the month.
24049 * The timezone to be used to read/write the `Date` instance in the model can be defined using
24050 * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
24052 * @param {string} ngModel Assignable angular expression to data-bind to.
24053 * @param {string=} name Property name of the form under which the control is published.
24054 * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
24055 * This must be a valid ISO month format (yyyy-MM). You can also use interpolation inside this
24056 * attribute (e.g. `min="{{minMonth | date:'yyyy-MM'}}"`). Note that `min` will also add
24057 * native HTML5 constraint validation.
24058 * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
24059 * This must be a valid ISO month format (yyyy-MM). You can also use interpolation inside this
24060 * attribute (e.g. `max="{{maxMonth | date:'yyyy-MM'}}"`). Note that `max` will also add
24061 * native HTML5 constraint validation.
24062 * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO week string
24063 * the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
24064 * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO week string
24065 * the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
24067 * @param {string=} required Sets `required` validation error key if the value is not entered.
24068 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
24069 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
24070 * `required` when you want to data-bind to the `required` attribute.
24071 * @param {string=} ngChange Angular expression to be executed when input changes due to user
24072 * interaction with the input element.
24075 <example name="month-input-directive" module="monthExample">
24076 <file name="index.html">
24078 angular.module('monthExample', [])
24079 .controller('DateController', ['$scope', function($scope) {
24081 value: new Date(2013, 9, 1)
24085 <form name="myForm" ng-controller="DateController as dateCtrl">
24086 <label for="exampleInput">Pick a month in 2013:</label>
24087 <input id="exampleInput" type="month" name="input" ng-model="example.value"
24088 placeholder="yyyy-MM" min="2013-01" max="2013-12" required />
24090 <span class="error" ng-show="myForm.input.$error.required">
24092 <span class="error" ng-show="myForm.input.$error.month">
24093 Not a valid month!</span>
24095 <tt>value = {{example.value | date: "yyyy-MM"}}</tt><br/>
24096 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
24097 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
24098 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
24099 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
24102 <file name="protractor.js" type="protractor">
24103 var value = element(by.binding('example.value | date: "yyyy-MM"'));
24104 var valid = element(by.binding('myForm.input.$valid'));
24106 // currently protractor/webdriver does not support
24107 // sending keys to all known HTML5 input controls
24108 // for various browsers (https://github.com/angular/protractor/issues/562).
24109 function setInput(val) {
24110 // set the value of the element and force validation.
24111 var scr = "var ipt = document.getElementById('exampleInput'); " +
24112 "ipt.value = '" + val + "';" +
24113 "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
24114 browser.executeScript(scr);
24117 it('should initialize to model', function() {
24118 expect(value.getText()).toContain('2013-10');
24119 expect(valid.getText()).toContain('myForm.input.$valid = true');
24122 it('should be invalid if empty', function() {
24124 expect(value.getText()).toEqual('value =');
24125 expect(valid.getText()).toContain('myForm.input.$valid = false');
24128 it('should be invalid if over max', function() {
24129 setInput('2015-01');
24130 expect(value.getText()).toContain('');
24131 expect(valid.getText()).toContain('myForm.input.$valid = false');
24136 'month': createDateInputType('month', MONTH_REGEXP,
24137 createDateParser(MONTH_REGEXP, ['yyyy', 'MM']),
24142 * @name input[number]
24145 * Text input with number validation and transformation. Sets the `number` validation
24146 * error if not a valid number.
24148 * <div class="alert alert-warning">
24149 * The model must always be of type `number` otherwise Angular will throw an error.
24150 * Be aware that a string containing a number is not enough. See the {@link ngModel:numfmt}
24151 * error docs for more information and an example of how to convert your model if necessary.
24154 * ## Issues with HTML5 constraint validation
24156 * In browsers that follow the
24157 * [HTML5 specification](https://html.spec.whatwg.org/multipage/forms.html#number-state-%28type=number%29),
24158 * `input[number]` does not work as expected with {@link ngModelOptions `ngModelOptions.allowInvalid`}.
24159 * If a non-number is entered in the input, the browser will report the value as an empty string,
24160 * which means the view / model values in `ngModel` and subsequently the scope value
24161 * will also be an empty string.
24164 * @param {string} ngModel Assignable angular expression to data-bind to.
24165 * @param {string=} name Property name of the form under which the control is published.
24166 * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
24167 * Can be interpolated.
24168 * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
24169 * Can be interpolated.
24170 * @param {string=} ngMin Like `min`, sets the `min` validation error key if the value entered is less than `ngMin`,
24171 * but does not trigger HTML5 native validation. Takes an expression.
24172 * @param {string=} ngMax Like `max`, sets the `max` validation error key if the value entered is greater than `ngMax`,
24173 * but does not trigger HTML5 native validation. Takes an expression.
24174 * @param {string=} step Sets the `step` validation error key if the value entered does not fit the `step` constraint.
24175 * Can be interpolated.
24176 * @param {string=} ngStep Like `step`, sets the `step` validation error key if the value entered does not fit the `ngStep` constraint,
24177 * but does not trigger HTML5 native validation. Takes an expression.
24178 * @param {string=} required Sets `required` validation error key if the value is not entered.
24179 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
24180 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
24181 * `required` when you want to data-bind to the `required` attribute.
24182 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
24184 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
24185 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
24187 * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
24188 * that contains the regular expression body that will be converted to a regular expression
24189 * as in the ngPattern directive.
24190 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
24191 * does not match a RegExp found by evaluating the Angular expression given in the attribute value.
24192 * If the expression evaluates to a RegExp object, then this is used directly.
24193 * If the expression evaluates to a string, then it will be converted to a RegExp
24194 * after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
24195 * `new RegExp('^abc$')`.<br />
24196 * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
24197 * start at the index of the last search's match, thus not taking the whole input value into
24199 * @param {string=} ngChange Angular expression to be executed when input changes due to user
24200 * interaction with the input element.
24203 <example name="number-input-directive" module="numberExample">
24204 <file name="index.html">
24206 angular.module('numberExample', [])
24207 .controller('ExampleController', ['$scope', function($scope) {
24213 <form name="myForm" ng-controller="ExampleController">
24215 <input type="number" name="input" ng-model="example.value"
24216 min="0" max="99" required>
24219 <span class="error" ng-show="myForm.input.$error.required">
24221 <span class="error" ng-show="myForm.input.$error.number">
24222 Not valid number!</span>
24224 <tt>value = {{example.value}}</tt><br/>
24225 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
24226 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
24227 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
24228 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
24231 <file name="protractor.js" type="protractor">
24232 var value = element(by.binding('example.value'));
24233 var valid = element(by.binding('myForm.input.$valid'));
24234 var input = element(by.model('example.value'));
24236 it('should initialize to model', function() {
24237 expect(value.getText()).toContain('12');
24238 expect(valid.getText()).toContain('true');
24241 it('should be invalid if empty', function() {
24243 input.sendKeys('');
24244 expect(value.getText()).toEqual('value =');
24245 expect(valid.getText()).toContain('false');
24248 it('should be invalid if over max', function() {
24250 input.sendKeys('123');
24251 expect(value.getText()).toEqual('value =');
24252 expect(valid.getText()).toContain('false');
24257 'number': numberInputType,
24265 * Text input with URL validation. Sets the `url` validation error key if the content is not a
24268 * <div class="alert alert-warning">
24269 * **Note:** `input[url]` uses a regex to validate urls that is derived from the regex
24270 * used in Chromium. If you need stricter validation, you can use `ng-pattern` or modify
24271 * the built-in validators (see the {@link guide/forms Forms guide})
24274 * @param {string} ngModel Assignable angular expression to data-bind to.
24275 * @param {string=} name Property name of the form under which the control is published.
24276 * @param {string=} required Sets `required` validation error key if the value is not entered.
24277 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
24278 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
24279 * `required` when you want to data-bind to the `required` attribute.
24280 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
24282 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
24283 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
24285 * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
24286 * that contains the regular expression body that will be converted to a regular expression
24287 * as in the ngPattern directive.
24288 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
24289 * does not match a RegExp found by evaluating the Angular expression given in the attribute value.
24290 * If the expression evaluates to a RegExp object, then this is used directly.
24291 * If the expression evaluates to a string, then it will be converted to a RegExp
24292 * after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
24293 * `new RegExp('^abc$')`.<br />
24294 * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
24295 * start at the index of the last search's match, thus not taking the whole input value into
24297 * @param {string=} ngChange Angular expression to be executed when input changes due to user
24298 * interaction with the input element.
24301 <example name="url-input-directive" module="urlExample">
24302 <file name="index.html">
24304 angular.module('urlExample', [])
24305 .controller('ExampleController', ['$scope', function($scope) {
24307 text: 'http://google.com'
24311 <form name="myForm" ng-controller="ExampleController">
24313 <input type="url" name="input" ng-model="url.text" required>
24316 <span class="error" ng-show="myForm.input.$error.required">
24318 <span class="error" ng-show="myForm.input.$error.url">
24319 Not valid url!</span>
24321 <tt>text = {{url.text}}</tt><br/>
24322 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
24323 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
24324 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
24325 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
24326 <tt>myForm.$error.url = {{!!myForm.$error.url}}</tt><br/>
24329 <file name="protractor.js" type="protractor">
24330 var text = element(by.binding('url.text'));
24331 var valid = element(by.binding('myForm.input.$valid'));
24332 var input = element(by.model('url.text'));
24334 it('should initialize to model', function() {
24335 expect(text.getText()).toContain('http://google.com');
24336 expect(valid.getText()).toContain('true');
24339 it('should be invalid if empty', function() {
24341 input.sendKeys('');
24343 expect(text.getText()).toEqual('text =');
24344 expect(valid.getText()).toContain('false');
24347 it('should be invalid if not url', function() {
24349 input.sendKeys('box');
24351 expect(valid.getText()).toContain('false');
24356 'url': urlInputType,
24361 * @name input[email]
24364 * Text input with email validation. Sets the `email` validation error key if not a valid email
24367 * <div class="alert alert-warning">
24368 * **Note:** `input[email]` uses a regex to validate email addresses that is derived from the regex
24369 * used in Chromium. If you need stricter validation (e.g. requiring a top-level domain), you can
24370 * use `ng-pattern` or modify the built-in validators (see the {@link guide/forms Forms guide})
24373 * @param {string} ngModel Assignable angular expression to data-bind to.
24374 * @param {string=} name Property name of the form under which the control is published.
24375 * @param {string=} required Sets `required` validation error key if the value is not entered.
24376 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
24377 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
24378 * `required` when you want to data-bind to the `required` attribute.
24379 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
24381 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
24382 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
24384 * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
24385 * that contains the regular expression body that will be converted to a regular expression
24386 * as in the ngPattern directive.
24387 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
24388 * does not match a RegExp found by evaluating the Angular expression given in the attribute value.
24389 * If the expression evaluates to a RegExp object, then this is used directly.
24390 * If the expression evaluates to a string, then it will be converted to a RegExp
24391 * after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
24392 * `new RegExp('^abc$')`.<br />
24393 * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
24394 * start at the index of the last search's match, thus not taking the whole input value into
24396 * @param {string=} ngChange Angular expression to be executed when input changes due to user
24397 * interaction with the input element.
24400 <example name="email-input-directive" module="emailExample">
24401 <file name="index.html">
24403 angular.module('emailExample', [])
24404 .controller('ExampleController', ['$scope', function($scope) {
24406 text: 'me@example.com'
24410 <form name="myForm" ng-controller="ExampleController">
24412 <input type="email" name="input" ng-model="email.text" required>
24415 <span class="error" ng-show="myForm.input.$error.required">
24417 <span class="error" ng-show="myForm.input.$error.email">
24418 Not valid email!</span>
24420 <tt>text = {{email.text}}</tt><br/>
24421 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
24422 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
24423 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
24424 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
24425 <tt>myForm.$error.email = {{!!myForm.$error.email}}</tt><br/>
24428 <file name="protractor.js" type="protractor">
24429 var text = element(by.binding('email.text'));
24430 var valid = element(by.binding('myForm.input.$valid'));
24431 var input = element(by.model('email.text'));
24433 it('should initialize to model', function() {
24434 expect(text.getText()).toContain('me@example.com');
24435 expect(valid.getText()).toContain('true');
24438 it('should be invalid if empty', function() {
24440 input.sendKeys('');
24441 expect(text.getText()).toEqual('text =');
24442 expect(valid.getText()).toContain('false');
24445 it('should be invalid if not email', function() {
24447 input.sendKeys('xxx');
24449 expect(valid.getText()).toContain('false');
24454 'email': emailInputType,
24459 * @name input[radio]
24462 * HTML radio button.
24464 * @param {string} ngModel Assignable angular expression to data-bind to.
24465 * @param {string} value The value to which the `ngModel` expression should be set when selected.
24466 * Note that `value` only supports `string` values, i.e. the scope model needs to be a string,
24467 * too. Use `ngValue` if you need complex models (`number`, `object`, ...).
24468 * @param {string=} name Property name of the form under which the control is published.
24469 * @param {string=} ngChange Angular expression to be executed when input changes due to user
24470 * interaction with the input element.
24471 * @param {string} ngValue Angular expression to which `ngModel` will be be set when the radio
24472 * is selected. Should be used instead of the `value` attribute if you need
24473 * a non-string `ngModel` (`boolean`, `array`, ...).
24476 <example name="radio-input-directive" module="radioExample">
24477 <file name="index.html">
24479 angular.module('radioExample', [])
24480 .controller('ExampleController', ['$scope', function($scope) {
24484 $scope.specialValue = {
24490 <form name="myForm" ng-controller="ExampleController">
24492 <input type="radio" ng-model="color.name" value="red">
24496 <input type="radio" ng-model="color.name" ng-value="specialValue">
24500 <input type="radio" ng-model="color.name" value="blue">
24503 <tt>color = {{color.name | json}}</tt><br/>
24505 Note that `ng-value="specialValue"` sets radio item's value to be the value of `$scope.specialValue`.
24507 <file name="protractor.js" type="protractor">
24508 it('should change state', function() {
24509 var inputs = element.all(by.model('color.name'));
24510 var color = element(by.binding('color.name'));
24512 expect(color.getText()).toContain('blue');
24514 inputs.get(0).click();
24515 expect(color.getText()).toContain('red');
24517 inputs.get(1).click();
24518 expect(color.getText()).toContain('green');
24523 'radio': radioInputType,
24527 * @name input[range]
24530 * Native range input with validation and transformation.
24532 * The model for the range input must always be a `Number`.
24534 * IE9 and other browsers that do not support the `range` type fall back
24535 * to a text input without any default values for `min`, `max` and `step`. Model binding,
24536 * validation and number parsing are nevertheless supported.
24538 * Browsers that support range (latest Chrome, Safari, Firefox, Edge) treat `input[range]`
24539 * in a way that never allows the input to hold an invalid value. That means:
24540 * - any non-numerical value is set to `(max + min) / 2`.
24541 * - any numerical value that is less than the current min val, or greater than the current max val
24542 * is set to the min / max val respectively.
24543 * - additionally, the current `step` is respected, so the nearest value that satisfies a step
24546 * See the [HTML Spec on input[type=range]](https://www.w3.org/TR/html5/forms.html#range-state-(type=range))
24549 * This has the following consequences for Angular:
24551 * Since the element value should always reflect the current model value, a range input
24552 * will set the bound ngModel expression to the value that the browser has set for the
24553 * input element. For example, in the following input `<input type="range" ng-model="model.value">`,
24554 * if the application sets `model.value = null`, the browser will set the input to `'50'`.
24555 * Angular will then set the model to `50`, to prevent input and model value being out of sync.
24557 * That means the model for range will immediately be set to `50` after `ngModel` has been
24558 * initialized. It also means a range input can never have the required error.
24560 * This does not only affect changes to the model value, but also to the values of the `min`,
24561 * `max`, and `step` attributes. When these change in a way that will cause the browser to modify
24562 * the input value, Angular will also update the model value.
24564 * Automatic value adjustment also means that a range input element can never have the `required`,
24565 * `min`, or `max` errors.
24567 * However, `step` is currently only fully implemented by Firefox. Other browsers have problems
24568 * when the step value changes dynamically - they do not adjust the element value correctly, but
24569 * instead may set the `stepMismatch` error. If that's the case, the Angular will set the `step`
24570 * error on the input, and set the model to `undefined`.
24572 * Note that `input[range]` is not compatible with`ngMax`, `ngMin`, and `ngStep`, because they do
24573 * not set the `min` and `max` attributes, which means that the browser won't automatically adjust
24574 * the input value based on their values, and will always assume min = 0, max = 100, and step = 1.
24576 * @param {string} ngModel Assignable angular expression to data-bind to.
24577 * @param {string=} name Property name of the form under which the control is published.
24578 * @param {string=} min Sets the `min` validation to ensure that the value entered is greater
24579 * than `min`. Can be interpolated.
24580 * @param {string=} max Sets the `max` validation to ensure that the value entered is less than `max`.
24581 * Can be interpolated.
24582 * @param {string=} step Sets the `step` validation to ensure that the value entered matches the `step`
24583 * Can be interpolated.
24584 * @param {string=} ngChange Angular expression to be executed when the ngModel value changes due
24585 * to user interaction with the input element.
24586 * @param {expression=} ngChecked If the expression is truthy, then the `checked` attribute will be set on the
24587 * element. **Note** : `ngChecked` should not be used alongside `ngModel`.
24588 * Checkout {@link ng.directive:ngChecked ngChecked} for usage.
24591 <example name="range-input-directive" module="rangeExample">
24592 <file name="index.html">
24594 angular.module('rangeExample', [])
24595 .controller('ExampleController', ['$scope', function($scope) {
24601 <form name="myForm" ng-controller="ExampleController">
24603 Model as range: <input type="range" name="range" ng-model="value" min="{{min}}" max="{{max}}">
24605 Model as number: <input type="number" ng-model="value"><br>
24606 Min: <input type="number" ng-model="min"><br>
24607 Max: <input type="number" ng-model="max"><br>
24608 value = <code>{{value}}</code><br/>
24609 myForm.range.$valid = <code>{{myForm.range.$valid}}</code><br/>
24610 myForm.range.$error = <code>{{myForm.range.$error}}</code>
24615 * ## Range Input with ngMin & ngMax attributes
24618 <example name="range-input-directive-ng" module="rangeExample">
24619 <file name="index.html">
24621 angular.module('rangeExample', [])
24622 .controller('ExampleController', ['$scope', function($scope) {
24628 <form name="myForm" ng-controller="ExampleController">
24629 Model as range: <input type="range" name="range" ng-model="value" ng-min="min" ng-max="max">
24631 Model as number: <input type="number" ng-model="value"><br>
24632 Min: <input type="number" ng-model="min"><br>
24633 Max: <input type="number" ng-model="max"><br>
24634 value = <code>{{value}}</code><br/>
24635 myForm.range.$valid = <code>{{myForm.range.$valid}}</code><br/>
24636 myForm.range.$error = <code>{{myForm.range.$error}}</code>
24642 'range': rangeInputType,
24646 * @name input[checkbox]
24651 * @param {string} ngModel Assignable angular expression to data-bind to.
24652 * @param {string=} name Property name of the form under which the control is published.
24653 * @param {expression=} ngTrueValue The value to which the expression should be set when selected.
24654 * @param {expression=} ngFalseValue The value to which the expression should be set when not selected.
24655 * @param {string=} ngChange Angular expression to be executed when input changes due to user
24656 * interaction with the input element.
24659 <example name="checkbox-input-directive" module="checkboxExample">
24660 <file name="index.html">
24662 angular.module('checkboxExample', [])
24663 .controller('ExampleController', ['$scope', function($scope) {
24664 $scope.checkboxModel = {
24670 <form name="myForm" ng-controller="ExampleController">
24672 <input type="checkbox" ng-model="checkboxModel.value1">
24675 <input type="checkbox" ng-model="checkboxModel.value2"
24676 ng-true-value="'YES'" ng-false-value="'NO'">
24678 <tt>value1 = {{checkboxModel.value1}}</tt><br/>
24679 <tt>value2 = {{checkboxModel.value2}}</tt><br/>
24682 <file name="protractor.js" type="protractor">
24683 it('should change state', function() {
24684 var value1 = element(by.binding('checkboxModel.value1'));
24685 var value2 = element(by.binding('checkboxModel.value2'));
24687 expect(value1.getText()).toContain('true');
24688 expect(value2.getText()).toContain('YES');
24690 element(by.model('checkboxModel.value1')).click();
24691 element(by.model('checkboxModel.value2')).click();
24693 expect(value1.getText()).toContain('false');
24694 expect(value2.getText()).toContain('NO');
24699 'checkbox': checkboxInputType,
24708 function stringBasedInputType(ctrl) {
24709 ctrl.$formatters.push(function(value) {
24710 return ctrl.$isEmpty(value) ? value : value.toString();
24714 function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
24715 baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
24716 stringBasedInputType(ctrl);
24719 function baseInputType(scope, element, attr, ctrl, $sniffer, $browser) {
24720 var type = lowercase(element[0].type);
24722 // In composition mode, users are still inputting intermediate text buffer,
24723 // hold the listener until composition is done.
24724 // More about composition events: https://developer.mozilla.org/en-US/docs/Web/API/CompositionEvent
24725 if (!$sniffer.android) {
24726 var composing = false;
24728 element.on('compositionstart', function() {
24732 element.on('compositionend', function() {
24740 var listener = function(ev) {
24742 $browser.defer.cancel(timeout);
24745 if (composing) return;
24746 var value = element.val(),
24747 event = ev && ev.type;
24749 // By default we will trim the value
24750 // If the attribute ng-trim exists we will avoid trimming
24751 // If input type is 'password', the value is never trimmed
24752 if (type !== 'password' && (!attr.ngTrim || attr.ngTrim !== 'false')) {
24753 value = trim(value);
24756 // If a control is suffering from bad input (due to native validators), browsers discard its
24757 // value, so it may be necessary to revalidate (by calling $setViewValue again) even if the
24758 // control's value is the same empty value twice in a row.
24759 if (ctrl.$viewValue !== value || (value === '' && ctrl.$$hasNativeValidators)) {
24760 ctrl.$setViewValue(value, event);
24764 // if the browser does support "input" event, we are fine - except on IE9 which doesn't fire the
24765 // input event on backspace, delete or cut
24766 if ($sniffer.hasEvent('input')) {
24767 element.on('input', listener);
24769 var deferListener = function(ev, input, origValue) {
24771 timeout = $browser.defer(function() {
24773 if (!input || input.value !== origValue) {
24780 element.on('keydown', /** @this */ function(event) {
24781 var key = event.keyCode;
24784 // command modifiers arrows
24785 if (key === 91 || (15 < key && key < 19) || (37 <= key && key <= 40)) return;
24787 deferListener(event, this, this.value);
24790 // if user modifies input value using context menu in IE, we need "paste" and "cut" events to catch it
24791 if ($sniffer.hasEvent('paste')) {
24792 element.on('paste cut', deferListener);
24796 // if user paste into input using mouse on older browser
24797 // or form autocomplete on newer browser, we need "change" event to catch it
24798 element.on('change', listener);
24800 // Some native input types (date-family) have the ability to change validity without
24801 // firing any input/change events.
24802 // For these event types, when native validators are present and the browser supports the type,
24803 // check for validity changes on various DOM events.
24804 if (PARTIAL_VALIDATION_TYPES[type] && ctrl.$$hasNativeValidators && type === attr.type) {
24805 element.on(PARTIAL_VALIDATION_EVENTS, /** @this */ function(ev) {
24807 var validity = this[VALIDITY_STATE_PROPERTY];
24808 var origBadInput = validity.badInput;
24809 var origTypeMismatch = validity.typeMismatch;
24810 timeout = $browser.defer(function() {
24812 if (validity.badInput !== origBadInput || validity.typeMismatch !== origTypeMismatch) {
24820 ctrl.$render = function() {
24821 // Workaround for Firefox validation #12102.
24822 var value = ctrl.$isEmpty(ctrl.$viewValue) ? '' : ctrl.$viewValue;
24823 if (element.val() !== value) {
24824 element.val(value);
24829 function weekParser(isoWeek, existingDate) {
24830 if (isDate(isoWeek)) {
24834 if (isString(isoWeek)) {
24835 WEEK_REGEXP.lastIndex = 0;
24836 var parts = WEEK_REGEXP.exec(isoWeek);
24838 var year = +parts[1],
24844 firstThurs = getFirstThursdayOfYear(year),
24845 addDays = (week - 1) * 7;
24847 if (existingDate) {
24848 hours = existingDate.getHours();
24849 minutes = existingDate.getMinutes();
24850 seconds = existingDate.getSeconds();
24851 milliseconds = existingDate.getMilliseconds();
24854 return new Date(year, 0, firstThurs.getDate() + addDays, hours, minutes, seconds, milliseconds);
24861 function createDateParser(regexp, mapping) {
24862 return function(iso, date) {
24869 if (isString(iso)) {
24870 // When a date is JSON'ified to wraps itself inside of an extra
24871 // set of double quotes. This makes the date parsing code unable
24872 // to match the date string and parse it as a date.
24873 if (iso.charAt(0) === '"' && iso.charAt(iso.length - 1) === '"') {
24874 iso = iso.substring(1, iso.length - 1);
24876 if (ISO_DATE_REGEXP.test(iso)) {
24877 return new Date(iso);
24879 regexp.lastIndex = 0;
24880 parts = regexp.exec(iso);
24886 yyyy: date.getFullYear(),
24887 MM: date.getMonth() + 1,
24888 dd: date.getDate(),
24889 HH: date.getHours(),
24890 mm: date.getMinutes(),
24891 ss: date.getSeconds(),
24892 sss: date.getMilliseconds() / 1000
24895 map = { yyyy: 1970, MM: 1, dd: 1, HH: 0, mm: 0, ss: 0, sss: 0 };
24898 forEach(parts, function(part, index) {
24899 if (index < mapping.length) {
24900 map[mapping[index]] = +part;
24903 return new Date(map.yyyy, map.MM - 1, map.dd, map.HH, map.mm, map.ss || 0, map.sss * 1000 || 0);
24911 function createDateInputType(type, regexp, parseDate, format) {
24912 return function dynamicDateInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter) {
24913 badInputChecker(scope, element, attr, ctrl);
24914 baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
24915 var timezone = ctrl && ctrl.$options.getOption('timezone');
24918 ctrl.$$parserName = type;
24919 ctrl.$parsers.push(function(value) {
24920 if (ctrl.$isEmpty(value)) return null;
24921 if (regexp.test(value)) {
24922 // Note: We cannot read ctrl.$modelValue, as there might be a different
24923 // parser/formatter in the processing chain so that the model
24924 // contains some different data format!
24925 var parsedDate = parseDate(value, previousDate);
24927 parsedDate = convertTimezoneToLocal(parsedDate, timezone);
24934 ctrl.$formatters.push(function(value) {
24935 if (value && !isDate(value)) {
24936 throw ngModelMinErr('datefmt', 'Expected `{0}` to be a date', value);
24938 if (isValidDate(value)) {
24939 previousDate = value;
24940 if (previousDate && timezone) {
24941 previousDate = convertTimezoneToLocal(previousDate, timezone, true);
24943 return $filter('date')(value, format, timezone);
24945 previousDate = null;
24950 if (isDefined(attr.min) || attr.ngMin) {
24952 ctrl.$validators.min = function(value) {
24953 return !isValidDate(value) || isUndefined(minVal) || parseDate(value) >= minVal;
24955 attr.$observe('min', function(val) {
24956 minVal = parseObservedDateValue(val);
24961 if (isDefined(attr.max) || attr.ngMax) {
24963 ctrl.$validators.max = function(value) {
24964 return !isValidDate(value) || isUndefined(maxVal) || parseDate(value) <= maxVal;
24966 attr.$observe('max', function(val) {
24967 maxVal = parseObservedDateValue(val);
24972 function isValidDate(value) {
24973 // Invalid Date: getTime() returns NaN
24974 return value && !(value.getTime && value.getTime() !== value.getTime());
24977 function parseObservedDateValue(val) {
24978 return isDefined(val) && !isDate(val) ? parseDate(val) || undefined : val;
24983 function badInputChecker(scope, element, attr, ctrl) {
24984 var node = element[0];
24985 var nativeValidation = ctrl.$$hasNativeValidators = isObject(node.validity);
24986 if (nativeValidation) {
24987 ctrl.$parsers.push(function(value) {
24988 var validity = element.prop(VALIDITY_STATE_PROPERTY) || {};
24989 return validity.badInput || validity.typeMismatch ? undefined : value;
24994 function numberFormatterParser(ctrl) {
24995 ctrl.$$parserName = 'number';
24996 ctrl.$parsers.push(function(value) {
24997 if (ctrl.$isEmpty(value)) return null;
24998 if (NUMBER_REGEXP.test(value)) return parseFloat(value);
25002 ctrl.$formatters.push(function(value) {
25003 if (!ctrl.$isEmpty(value)) {
25004 if (!isNumber(value)) {
25005 throw ngModelMinErr('numfmt', 'Expected `{0}` to be a number', value);
25007 value = value.toString();
25013 function parseNumberAttrVal(val) {
25014 if (isDefined(val) && !isNumber(val)) {
25015 val = parseFloat(val);
25017 return !isNumberNaN(val) ? val : undefined;
25020 function isNumberInteger(num) {
25021 // See http://stackoverflow.com/questions/14636536/how-to-check-if-a-variable-is-an-integer-in-javascript#14794066
25022 // (minus the assumption that `num` is a number)
25024 // eslint-disable-next-line no-bitwise
25025 return (num | 0) === num;
25028 function countDecimals(num) {
25029 var numString = num.toString();
25030 var decimalSymbolIndex = numString.indexOf('.');
25032 if (decimalSymbolIndex === -1) {
25033 if (-1 < num && num < 1) {
25034 // It may be in the exponential notation format (`1e-X`)
25035 var match = /e-(\d+)$/.exec(numString);
25038 return Number(match[1]);
25045 return numString.length - decimalSymbolIndex - 1;
25048 function isValidForStep(viewValue, stepBase, step) {
25049 // At this point `stepBase` and `step` are expected to be non-NaN values
25050 // and `viewValue` is expected to be a valid stringified number.
25051 var value = Number(viewValue);
25053 var isNonIntegerValue = !isNumberInteger(value);
25054 var isNonIntegerStepBase = !isNumberInteger(stepBase);
25055 var isNonIntegerStep = !isNumberInteger(step);
25057 // Due to limitations in Floating Point Arithmetic (e.g. `0.3 - 0.2 !== 0.1` or
25058 // `0.5 % 0.1 !== 0`), we need to convert all numbers to integers.
25059 if (isNonIntegerValue || isNonIntegerStepBase || isNonIntegerStep) {
25060 var valueDecimals = isNonIntegerValue ? countDecimals(value) : 0;
25061 var stepBaseDecimals = isNonIntegerStepBase ? countDecimals(stepBase) : 0;
25062 var stepDecimals = isNonIntegerStep ? countDecimals(step) : 0;
25064 var decimalCount = Math.max(valueDecimals, stepBaseDecimals, stepDecimals);
25065 var multiplier = Math.pow(10, decimalCount);
25067 value = value * multiplier;
25068 stepBase = stepBase * multiplier;
25069 step = step * multiplier;
25071 if (isNonIntegerValue) value = Math.round(value);
25072 if (isNonIntegerStepBase) stepBase = Math.round(stepBase);
25073 if (isNonIntegerStep) step = Math.round(step);
25076 return (value - stepBase) % step === 0;
25079 function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) {
25080 badInputChecker(scope, element, attr, ctrl);
25081 numberFormatterParser(ctrl);
25082 baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
25087 if (isDefined(attr.min) || attr.ngMin) {
25088 ctrl.$validators.min = function(value) {
25089 return ctrl.$isEmpty(value) || isUndefined(minVal) || value >= minVal;
25092 attr.$observe('min', function(val) {
25093 minVal = parseNumberAttrVal(val);
25094 // TODO(matsko): implement validateLater to reduce number of validations
25099 if (isDefined(attr.max) || attr.ngMax) {
25100 ctrl.$validators.max = function(value) {
25101 return ctrl.$isEmpty(value) || isUndefined(maxVal) || value <= maxVal;
25104 attr.$observe('max', function(val) {
25105 maxVal = parseNumberAttrVal(val);
25106 // TODO(matsko): implement validateLater to reduce number of validations
25111 if (isDefined(attr.step) || attr.ngStep) {
25113 ctrl.$validators.step = function(modelValue, viewValue) {
25114 return ctrl.$isEmpty(viewValue) || isUndefined(stepVal) ||
25115 isValidForStep(viewValue, minVal || 0, stepVal);
25118 attr.$observe('step', function(val) {
25119 stepVal = parseNumberAttrVal(val);
25120 // TODO(matsko): implement validateLater to reduce number of validations
25126 function rangeInputType(scope, element, attr, ctrl, $sniffer, $browser) {
25127 badInputChecker(scope, element, attr, ctrl);
25128 numberFormatterParser(ctrl);
25129 baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
25131 var supportsRange = ctrl.$$hasNativeValidators && element[0].type === 'range',
25132 minVal = supportsRange ? 0 : undefined,
25133 maxVal = supportsRange ? 100 : undefined,
25134 stepVal = supportsRange ? 1 : undefined,
25135 validity = element[0].validity,
25136 hasMinAttr = isDefined(attr.min),
25137 hasMaxAttr = isDefined(attr.max),
25138 hasStepAttr = isDefined(attr.step);
25140 var originalRender = ctrl.$render;
25142 ctrl.$render = supportsRange && isDefined(validity.rangeUnderflow) && isDefined(validity.rangeOverflow) ?
25143 //Browsers that implement range will set these values automatically, but reading the adjusted values after
25144 //$render would cause the min / max validators to be applied with the wrong value
25145 function rangeRender() {
25147 ctrl.$setViewValue(element.val());
25152 ctrl.$validators.min = supportsRange ?
25153 // Since all browsers set the input to a valid value, we don't need to check validity
25154 function noopMinValidator() { return true; } :
25155 // non-support browsers validate the min val
25156 function minValidator(modelValue, viewValue) {
25157 return ctrl.$isEmpty(viewValue) || isUndefined(minVal) || viewValue >= minVal;
25160 setInitialValueAndObserver('min', minChange);
25164 ctrl.$validators.max = supportsRange ?
25165 // Since all browsers set the input to a valid value, we don't need to check validity
25166 function noopMaxValidator() { return true; } :
25167 // non-support browsers validate the max val
25168 function maxValidator(modelValue, viewValue) {
25169 return ctrl.$isEmpty(viewValue) || isUndefined(maxVal) || viewValue <= maxVal;
25172 setInitialValueAndObserver('max', maxChange);
25176 ctrl.$validators.step = supportsRange ?
25177 function nativeStepValidator() {
25178 // Currently, only FF implements the spec on step change correctly (i.e. adjusting the
25179 // input element value to a valid value). It's possible that other browsers set the stepMismatch
25180 // validity error instead, so we can at least report an error in that case.
25181 return !validity.stepMismatch;
25183 // ngStep doesn't set the setp attr, so the browser doesn't adjust the input value as setting step would
25184 function stepValidator(modelValue, viewValue) {
25185 return ctrl.$isEmpty(viewValue) || isUndefined(stepVal) ||
25186 isValidForStep(viewValue, minVal || 0, stepVal);
25189 setInitialValueAndObserver('step', stepChange);
25192 function setInitialValueAndObserver(htmlAttrName, changeFn) {
25193 // interpolated attributes set the attribute value only after a digest, but we need the
25194 // attribute value when the input is first rendered, so that the browser can adjust the
25195 // input value based on the min/max value
25196 element.attr(htmlAttrName, attr[htmlAttrName]);
25197 attr.$observe(htmlAttrName, changeFn);
25200 function minChange(val) {
25201 minVal = parseNumberAttrVal(val);
25202 // ignore changes before model is initialized
25203 if (isNumberNaN(ctrl.$modelValue)) {
25207 if (supportsRange) {
25208 var elVal = element.val();
25209 // IE11 doesn't set the el val correctly if the minVal is greater than the element value
25210 if (minVal > elVal) {
25212 element.val(elVal);
25214 ctrl.$setViewValue(elVal);
25216 // TODO(matsko): implement validateLater to reduce number of validations
25221 function maxChange(val) {
25222 maxVal = parseNumberAttrVal(val);
25223 // ignore changes before model is initialized
25224 if (isNumberNaN(ctrl.$modelValue)) {
25228 if (supportsRange) {
25229 var elVal = element.val();
25230 // IE11 doesn't set the el val correctly if the maxVal is less than the element value
25231 if (maxVal < elVal) {
25232 element.val(maxVal);
25233 // IE11 and Chrome don't set the value to the minVal when max < min
25234 elVal = maxVal < minVal ? minVal : maxVal;
25236 ctrl.$setViewValue(elVal);
25238 // TODO(matsko): implement validateLater to reduce number of validations
25243 function stepChange(val) {
25244 stepVal = parseNumberAttrVal(val);
25245 // ignore changes before model is initialized
25246 if (isNumberNaN(ctrl.$modelValue)) {
25250 // Some browsers don't adjust the input value correctly, but set the stepMismatch error
25251 if (supportsRange && ctrl.$viewValue !== element.val()) {
25252 ctrl.$setViewValue(element.val());
25254 // TODO(matsko): implement validateLater to reduce number of validations
25260 function urlInputType(scope, element, attr, ctrl, $sniffer, $browser) {
25261 // Note: no badInputChecker here by purpose as `url` is only a validation
25262 // in browsers, i.e. we can always read out input.value even if it is not valid!
25263 baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
25264 stringBasedInputType(ctrl);
25266 ctrl.$$parserName = 'url';
25267 ctrl.$validators.url = function(modelValue, viewValue) {
25268 var value = modelValue || viewValue;
25269 return ctrl.$isEmpty(value) || URL_REGEXP.test(value);
25273 function emailInputType(scope, element, attr, ctrl, $sniffer, $browser) {
25274 // Note: no badInputChecker here by purpose as `url` is only a validation
25275 // in browsers, i.e. we can always read out input.value even if it is not valid!
25276 baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
25277 stringBasedInputType(ctrl);
25279 ctrl.$$parserName = 'email';
25280 ctrl.$validators.email = function(modelValue, viewValue) {
25281 var value = modelValue || viewValue;
25282 return ctrl.$isEmpty(value) || EMAIL_REGEXP.test(value);
25286 function radioInputType(scope, element, attr, ctrl) {
25287 var doTrim = !attr.ngTrim || trim(attr.ngTrim) !== 'false';
25288 // make the name unique, if not defined
25289 if (isUndefined(attr.name)) {
25290 element.attr('name', nextUid());
25293 var listener = function(ev) {
25295 if (element[0].checked) {
25296 value = attr.value;
25298 value = trim(value);
25300 ctrl.$setViewValue(value, ev && ev.type);
25304 element.on('click', listener);
25306 ctrl.$render = function() {
25307 var value = attr.value;
25309 value = trim(value);
25311 element[0].checked = (value === ctrl.$viewValue);
25314 attr.$observe('value', ctrl.$render);
25317 function parseConstantExpr($parse, context, name, expression, fallback) {
25319 if (isDefined(expression)) {
25320 parseFn = $parse(expression);
25321 if (!parseFn.constant) {
25322 throw ngModelMinErr('constexpr', 'Expected constant expression for `{0}`, but saw ' +
25323 '`{1}`.', name, expression);
25325 return parseFn(context);
25330 function checkboxInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter, $parse) {
25331 var trueValue = parseConstantExpr($parse, scope, 'ngTrueValue', attr.ngTrueValue, true);
25332 var falseValue = parseConstantExpr($parse, scope, 'ngFalseValue', attr.ngFalseValue, false);
25334 var listener = function(ev) {
25335 ctrl.$setViewValue(element[0].checked, ev && ev.type);
25338 element.on('click', listener);
25340 ctrl.$render = function() {
25341 element[0].checked = ctrl.$viewValue;
25344 // Override the standard `$isEmpty` because the $viewValue of an empty checkbox is always set to `false`
25345 // This is because of the parser below, which compares the `$modelValue` with `trueValue` to convert
25346 // it to a boolean.
25347 ctrl.$isEmpty = function(value) {
25348 return value === false;
25351 ctrl.$formatters.push(function(value) {
25352 return equals(value, trueValue);
25355 ctrl.$parsers.push(function(value) {
25356 return value ? trueValue : falseValue;
25367 * HTML textarea element control with angular data-binding. The data-binding and validation
25368 * properties of this element are exactly the same as those of the
25369 * {@link ng.directive:input input element}.
25371 * @param {string} ngModel Assignable angular expression to data-bind to.
25372 * @param {string=} name Property name of the form under which the control is published.
25373 * @param {string=} required Sets `required` validation error key if the value is not entered.
25374 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
25375 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
25376 * `required` when you want to data-bind to the `required` attribute.
25377 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
25379 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
25380 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any
25382 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
25383 * does not match a RegExp found by evaluating the Angular expression given in the attribute value.
25384 * If the expression evaluates to a RegExp object, then this is used directly.
25385 * If the expression evaluates to a string, then it will be converted to a RegExp
25386 * after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
25387 * `new RegExp('^abc$')`.<br />
25388 * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
25389 * start at the index of the last search's match, thus not taking the whole input value into
25391 * @param {string=} ngChange Angular expression to be executed when input changes due to user
25392 * interaction with the input element.
25393 * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
25397 * When specifying the `placeholder` attribute of `<textarea>`, Internet Explorer will temporarily
25398 * insert the placeholder value as the textarea's content. If the placeholder value contains
25399 * interpolation (`{{ ... }}`), an error will be logged in the console when Angular tries to update
25400 * the value of the by-then-removed text node. This doesn't affect the functionality of the
25401 * textarea, but can be undesirable.
25403 * You can work around this Internet Explorer issue by using `ng-attr-placeholder` instead of
25404 * `placeholder` on textareas, whenever you need interpolation in the placeholder value. You can
25405 * find more details on `ngAttr` in the
25406 * [Interpolation](guide/interpolation#-ngattr-for-binding-to-arbitrary-attributes) section of the
25417 * HTML input element control. When used together with {@link ngModel `ngModel`}, it provides data-binding,
25418 * input state control, and validation.
25419 * Input control follows HTML5 input types and polyfills the HTML5 validation behavior for older browsers.
25421 * <div class="alert alert-warning">
25422 * **Note:** Not every feature offered is available for all input types.
25423 * Specifically, data binding and event handling via `ng-model` is unsupported for `input[file]`.
25426 * @param {string} ngModel Assignable angular expression to data-bind to.
25427 * @param {string=} name Property name of the form under which the control is published.
25428 * @param {string=} required Sets `required` validation error key if the value is not entered.
25429 * @param {boolean=} ngRequired Sets `required` attribute if set to true
25430 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
25432 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
25433 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any
25435 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
25436 * value does not match a RegExp found by evaluating the Angular expression given in the attribute value.
25437 * If the expression evaluates to a RegExp object, then this is used directly.
25438 * If the expression evaluates to a string, then it will be converted to a RegExp
25439 * after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
25440 * `new RegExp('^abc$')`.<br />
25441 * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
25442 * start at the index of the last search's match, thus not taking the whole input value into
25444 * @param {string=} ngChange Angular expression to be executed when input changes due to user
25445 * interaction with the input element.
25446 * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
25447 * This parameter is ignored for input[type=password] controls, which will never trim the
25451 <example name="input-directive" module="inputExample">
25452 <file name="index.html">
25454 angular.module('inputExample', [])
25455 .controller('ExampleController', ['$scope', function($scope) {
25456 $scope.user = {name: 'guest', last: 'visitor'};
25459 <div ng-controller="ExampleController">
25460 <form name="myForm">
25463 <input type="text" name="userName" ng-model="user.name" required>
25466 <span class="error" ng-show="myForm.userName.$error.required">
25471 <input type="text" name="lastName" ng-model="user.last"
25472 ng-minlength="3" ng-maxlength="10">
25475 <span class="error" ng-show="myForm.lastName.$error.minlength">
25477 <span class="error" ng-show="myForm.lastName.$error.maxlength">
25482 <tt>user = {{user}}</tt><br/>
25483 <tt>myForm.userName.$valid = {{myForm.userName.$valid}}</tt><br/>
25484 <tt>myForm.userName.$error = {{myForm.userName.$error}}</tt><br/>
25485 <tt>myForm.lastName.$valid = {{myForm.lastName.$valid}}</tt><br/>
25486 <tt>myForm.lastName.$error = {{myForm.lastName.$error}}</tt><br/>
25487 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
25488 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
25489 <tt>myForm.$error.minlength = {{!!myForm.$error.minlength}}</tt><br/>
25490 <tt>myForm.$error.maxlength = {{!!myForm.$error.maxlength}}</tt><br/>
25493 <file name="protractor.js" type="protractor">
25494 var user = element(by.exactBinding('user'));
25495 var userNameValid = element(by.binding('myForm.userName.$valid'));
25496 var lastNameValid = element(by.binding('myForm.lastName.$valid'));
25497 var lastNameError = element(by.binding('myForm.lastName.$error'));
25498 var formValid = element(by.binding('myForm.$valid'));
25499 var userNameInput = element(by.model('user.name'));
25500 var userLastInput = element(by.model('user.last'));
25502 it('should initialize to model', function() {
25503 expect(user.getText()).toContain('{"name":"guest","last":"visitor"}');
25504 expect(userNameValid.getText()).toContain('true');
25505 expect(formValid.getText()).toContain('true');
25508 it('should be invalid if empty when required', function() {
25509 userNameInput.clear();
25510 userNameInput.sendKeys('');
25512 expect(user.getText()).toContain('{"last":"visitor"}');
25513 expect(userNameValid.getText()).toContain('false');
25514 expect(formValid.getText()).toContain('false');
25517 it('should be valid if empty when min length is set', function() {
25518 userLastInput.clear();
25519 userLastInput.sendKeys('');
25521 expect(user.getText()).toContain('{"name":"guest","last":""}');
25522 expect(lastNameValid.getText()).toContain('true');
25523 expect(formValid.getText()).toContain('true');
25526 it('should be invalid if less than required min length', function() {
25527 userLastInput.clear();
25528 userLastInput.sendKeys('xx');
25530 expect(user.getText()).toContain('{"name":"guest"}');
25531 expect(lastNameValid.getText()).toContain('false');
25532 expect(lastNameError.getText()).toContain('minlength');
25533 expect(formValid.getText()).toContain('false');
25536 it('should be invalid if longer than max length', function() {
25537 userLastInput.clear();
25538 userLastInput.sendKeys('some ridiculously long name');
25540 expect(user.getText()).toContain('{"name":"guest"}');
25541 expect(lastNameValid.getText()).toContain('false');
25542 expect(lastNameError.getText()).toContain('maxlength');
25543 expect(formValid.getText()).toContain('false');
25548 var inputDirective = ['$browser', '$sniffer', '$filter', '$parse',
25549 function($browser, $sniffer, $filter, $parse) {
25552 require: ['?ngModel'],
25554 pre: function(scope, element, attr, ctrls) {
25556 (inputType[lowercase(attr.type)] || inputType.text)(scope, element, attr, ctrls[0], $sniffer,
25557 $browser, $filter, $parse);
25566 var CONSTANT_VALUE_REGEXP = /^(true|false|\d+)$/;
25572 * Binds the given expression to the value of the element.
25574 * It is mainly used on {@link input[radio] `input[radio]`} and option elements,
25575 * so that when the element is selected, the {@link ngModel `ngModel`} of that element (or its
25576 * {@link select `select`} parent element) is set to the bound value. It is especially useful
25577 * for dynamically generated lists using {@link ngRepeat `ngRepeat`}, as shown below.
25579 * It can also be used to achieve one-way binding of a given expression to an input element
25580 * such as an `input[text]` or a `textarea`, when that element does not use ngModel.
25583 * @param {string=} ngValue angular expression, whose value will be bound to the `value` attribute
25584 * and `value` property of the element.
25587 <example name="ngValue-directive" module="valueExample">
25588 <file name="index.html">
25590 angular.module('valueExample', [])
25591 .controller('ExampleController', ['$scope', function($scope) {
25592 $scope.names = ['pizza', 'unicorns', 'robots'];
25593 $scope.my = { favorite: 'unicorns' };
25596 <form ng-controller="ExampleController">
25597 <h2>Which is your favorite?</h2>
25598 <label ng-repeat="name in names" for="{{name}}">
25600 <input type="radio"
25601 ng-model="my.favorite"
25606 <div>You chose {{my.favorite}}</div>
25609 <file name="protractor.js" type="protractor">
25610 var favorite = element(by.binding('my.favorite'));
25612 it('should initialize to model', function() {
25613 expect(favorite.getText()).toContain('unicorns');
25615 it('should bind the values to the inputs', function() {
25616 element.all(by.model('my.favorite')).get(0).click();
25617 expect(favorite.getText()).toContain('pizza');
25622 var ngValueDirective = function() {
25624 * inputs use the value attribute as their default value if the value property is not set.
25625 * Once the value property has been set (by adding input), it will not react to changes to
25626 * the value attribute anymore. Setting both attribute and property fixes this behavior, and
25627 * makes it possible to use ngValue as a sort of one-way bind.
25629 function updateElementValue(element, attr, value) {
25630 // Support: IE9 only
25631 // In IE9 values are converted to string (e.g. `input.value = null` results in `input.value === 'null'`).
25632 var propValue = isDefined(value) ? value : (msie === 9) ? '' : null;
25633 element.prop('value', propValue);
25634 attr.$set('value', value);
25640 compile: function(tpl, tplAttr) {
25641 if (CONSTANT_VALUE_REGEXP.test(tplAttr.ngValue)) {
25642 return function ngValueConstantLink(scope, elm, attr) {
25643 var value = scope.$eval(attr.ngValue);
25644 updateElementValue(elm, attr, value);
25647 return function ngValueLink(scope, elm, attr) {
25648 scope.$watch(attr.ngValue, function valueWatchAction(value) {
25649 updateElementValue(elm, attr, value);
25663 * The `ngBind` attribute tells Angular to replace the text content of the specified HTML element
25664 * with the value of a given expression, and to update the text content when the value of that
25665 * expression changes.
25667 * Typically, you don't use `ngBind` directly, but instead you use the double curly markup like
25668 * `{{ expression }}` which is similar but less verbose.
25670 * It is preferable to use `ngBind` instead of `{{ expression }}` if a template is momentarily
25671 * displayed by the browser in its raw state before Angular compiles it. Since `ngBind` is an
25672 * element attribute, it makes the bindings invisible to the user while the page is loading.
25674 * An alternative solution to this problem would be using the
25675 * {@link ng.directive:ngCloak ngCloak} directive.
25679 * @param {expression} ngBind {@link guide/expression Expression} to evaluate.
25682 * Enter a name in the Live Preview text box; the greeting below the text box changes instantly.
25683 <example module="bindExample" name="ng-bind">
25684 <file name="index.html">
25686 angular.module('bindExample', [])
25687 .controller('ExampleController', ['$scope', function($scope) {
25688 $scope.name = 'Whirled';
25691 <div ng-controller="ExampleController">
25692 <label>Enter name: <input type="text" ng-model="name"></label><br>
25693 Hello <span ng-bind="name"></span>!
25696 <file name="protractor.js" type="protractor">
25697 it('should check ng-bind', function() {
25698 var nameInput = element(by.model('name'));
25700 expect(element(by.binding('name')).getText()).toBe('Whirled');
25702 nameInput.sendKeys('world');
25703 expect(element(by.binding('name')).getText()).toBe('world');
25708 var ngBindDirective = ['$compile', function($compile) {
25711 compile: function ngBindCompile(templateElement) {
25712 $compile.$$addBindingClass(templateElement);
25713 return function ngBindLink(scope, element, attr) {
25714 $compile.$$addBindingInfo(element, attr.ngBind);
25715 element = element[0];
25716 scope.$watch(attr.ngBind, function ngBindWatchAction(value) {
25717 element.textContent = stringify(value);
25727 * @name ngBindTemplate
25730 * The `ngBindTemplate` directive specifies that the element
25731 * text content should be replaced with the interpolation of the template
25732 * in the `ngBindTemplate` attribute.
25733 * Unlike `ngBind`, the `ngBindTemplate` can contain multiple `{{` `}}`
25734 * expressions. This directive is needed since some HTML elements
25735 * (such as TITLE and OPTION) cannot contain SPAN elements.
25738 * @param {string} ngBindTemplate template of form
25739 * <tt>{{</tt> <tt>expression</tt> <tt>}}</tt> to eval.
25742 * Try it here: enter text in text box and watch the greeting change.
25743 <example module="bindExample" name="ng-bind-template">
25744 <file name="index.html">
25746 angular.module('bindExample', [])
25747 .controller('ExampleController', ['$scope', function($scope) {
25748 $scope.salutation = 'Hello';
25749 $scope.name = 'World';
25752 <div ng-controller="ExampleController">
25753 <label>Salutation: <input type="text" ng-model="salutation"></label><br>
25754 <label>Name: <input type="text" ng-model="name"></label><br>
25755 <pre ng-bind-template="{{salutation}} {{name}}!"></pre>
25758 <file name="protractor.js" type="protractor">
25759 it('should check ng-bind', function() {
25760 var salutationElem = element(by.binding('salutation'));
25761 var salutationInput = element(by.model('salutation'));
25762 var nameInput = element(by.model('name'));
25764 expect(salutationElem.getText()).toBe('Hello World!');
25766 salutationInput.clear();
25767 salutationInput.sendKeys('Greetings');
25769 nameInput.sendKeys('user');
25771 expect(salutationElem.getText()).toBe('Greetings user!');
25776 var ngBindTemplateDirective = ['$interpolate', '$compile', function($interpolate, $compile) {
25778 compile: function ngBindTemplateCompile(templateElement) {
25779 $compile.$$addBindingClass(templateElement);
25780 return function ngBindTemplateLink(scope, element, attr) {
25781 var interpolateFn = $interpolate(element.attr(attr.$attr.ngBindTemplate));
25782 $compile.$$addBindingInfo(element, interpolateFn.expressions);
25783 element = element[0];
25784 attr.$observe('ngBindTemplate', function(value) {
25785 element.textContent = isUndefined(value) ? '' : value;
25798 * Evaluates the expression and inserts the resulting HTML into the element in a secure way. By default,
25799 * the resulting HTML content will be sanitized using the {@link ngSanitize.$sanitize $sanitize} service.
25800 * To utilize this functionality, ensure that `$sanitize` is available, for example, by including {@link
25801 * ngSanitize} in your module's dependencies (not in core Angular). In order to use {@link ngSanitize}
25802 * in your module's dependencies, you need to include "angular-sanitize.js" in your application.
25804 * You may also bypass sanitization for values you know are safe. To do so, bind to
25805 * an explicitly trusted value via {@link ng.$sce#trustAsHtml $sce.trustAsHtml}. See the example
25806 * under {@link ng.$sce#show-me-an-example-using-sce- Strict Contextual Escaping (SCE)}.
25808 * Note: If a `$sanitize` service is unavailable and the bound value isn't explicitly trusted, you
25809 * will have an exception (instead of an exploit.)
25812 * @param {expression} ngBindHtml {@link guide/expression Expression} to evaluate.
25816 <example module="bindHtmlExample" deps="angular-sanitize.js" name="ng-bind-html">
25817 <file name="index.html">
25818 <div ng-controller="ExampleController">
25819 <p ng-bind-html="myHTML"></p>
25823 <file name="script.js">
25824 angular.module('bindHtmlExample', ['ngSanitize'])
25825 .controller('ExampleController', ['$scope', function($scope) {
25827 'I am an <code>HTML</code>string with ' +
25828 '<a href="#">links!</a> and other <em>stuff</em>';
25832 <file name="protractor.js" type="protractor">
25833 it('should check ng-bind-html', function() {
25834 expect(element(by.binding('myHTML')).getText()).toBe(
25835 'I am an HTMLstring with links! and other stuff');
25840 var ngBindHtmlDirective = ['$sce', '$parse', '$compile', function($sce, $parse, $compile) {
25843 compile: function ngBindHtmlCompile(tElement, tAttrs) {
25844 var ngBindHtmlGetter = $parse(tAttrs.ngBindHtml);
25845 var ngBindHtmlWatch = $parse(tAttrs.ngBindHtml, function sceValueOf(val) {
25846 // Unwrap the value to compare the actual inner safe value, not the wrapper object.
25847 return $sce.valueOf(val);
25849 $compile.$$addBindingClass(tElement);
25851 return function ngBindHtmlLink(scope, element, attr) {
25852 $compile.$$addBindingInfo(element, attr.ngBindHtml);
25854 scope.$watch(ngBindHtmlWatch, function ngBindHtmlWatchAction() {
25855 // The watched value is the unwrapped value. To avoid re-escaping, use the direct getter.
25856 var value = ngBindHtmlGetter(scope);
25857 element.html($sce.getTrustedHtml(value) || '');
25869 * Evaluate the given expression when the user changes the input.
25870 * The expression is evaluated immediately, unlike the JavaScript onchange event
25871 * which only triggers at the end of a change (usually, when the user leaves the
25872 * form element or presses the return key).
25874 * The `ngChange` expression is only evaluated when a change in the input value causes
25875 * a new value to be committed to the model.
25877 * It will not be evaluated:
25878 * * if the value returned from the `$parsers` transformation pipeline has not changed
25879 * * if the input has continued to be invalid since the model will stay `null`
25880 * * if the model is changed programmatically and not by a change to the input value
25883 * Note, this directive requires `ngModel` to be present.
25886 * @param {expression} ngChange {@link guide/expression Expression} to evaluate upon change
25890 * <example name="ngChange-directive" module="changeExample">
25891 * <file name="index.html">
25893 * angular.module('changeExample', [])
25894 * .controller('ExampleController', ['$scope', function($scope) {
25895 * $scope.counter = 0;
25896 * $scope.change = function() {
25897 * $scope.counter++;
25901 * <div ng-controller="ExampleController">
25902 * <input type="checkbox" ng-model="confirmed" ng-change="change()" id="ng-change-example1" />
25903 * <input type="checkbox" ng-model="confirmed" id="ng-change-example2" />
25904 * <label for="ng-change-example2">Confirmed</label><br />
25905 * <tt>debug = {{confirmed}}</tt><br/>
25906 * <tt>counter = {{counter}}</tt><br/>
25909 * <file name="protractor.js" type="protractor">
25910 * var counter = element(by.binding('counter'));
25911 * var debug = element(by.binding('confirmed'));
25913 * it('should evaluate the expression if changing from view', function() {
25914 * expect(counter.getText()).toContain('0');
25916 * element(by.id('ng-change-example1')).click();
25918 * expect(counter.getText()).toContain('1');
25919 * expect(debug.getText()).toContain('true');
25922 * it('should not evaluate the expression if changing from model', function() {
25923 * element(by.id('ng-change-example2')).click();
25925 * expect(counter.getText()).toContain('0');
25926 * expect(debug.getText()).toContain('true');
25931 var ngChangeDirective = valueFn({
25933 require: 'ngModel',
25934 link: function(scope, element, attr, ctrl) {
25935 ctrl.$viewChangeListeners.push(function() {
25936 scope.$eval(attr.ngChange);
25943 ngClassEvenDirective,
25944 ngClassOddDirective
25947 function classDirective(name, selector) {
25948 name = 'ngClass' + name;
25949 var indexWatchExpression;
25951 return ['$parse', function($parse) {
25954 link: function(scope, element, attr) {
25955 var expression = attr[name].trim();
25956 var isOneTime = (expression.charAt(0) === ':') && (expression.charAt(1) === ':');
25958 var watchInterceptor = isOneTime ? toFlatValue : toClassString;
25959 var watchExpression = $parse(expression, watchInterceptor);
25960 var watchAction = isOneTime ? ngClassOneTimeWatchAction : ngClassWatchAction;
25962 var classCounts = element.data('$classCounts');
25963 var oldModulo = true;
25964 var oldClassString;
25966 if (!classCounts) {
25967 // Use createMap() to prevent class assumptions involving property
25968 // names in Object.prototype
25969 classCounts = createMap();
25970 element.data('$classCounts', classCounts);
25973 if (name !== 'ngClass') {
25974 if (!indexWatchExpression) {
25975 indexWatchExpression = $parse('$index', function moduloTwo($index) {
25976 // eslint-disable-next-line no-bitwise
25981 scope.$watch(indexWatchExpression, ngClassIndexWatchAction);
25984 scope.$watch(watchExpression, watchAction, isOneTime);
25986 function addClasses(classString) {
25987 classString = digestClassCounts(split(classString), 1);
25988 attr.$addClass(classString);
25991 function removeClasses(classString) {
25992 classString = digestClassCounts(split(classString), -1);
25993 attr.$removeClass(classString);
25996 function updateClasses(oldClassString, newClassString) {
25997 var oldClassArray = split(oldClassString);
25998 var newClassArray = split(newClassString);
26000 var toRemoveArray = arrayDifference(oldClassArray, newClassArray);
26001 var toAddArray = arrayDifference(newClassArray, oldClassArray);
26003 var toRemoveString = digestClassCounts(toRemoveArray, -1);
26004 var toAddString = digestClassCounts(toAddArray, 1);
26006 attr.$addClass(toAddString);
26007 attr.$removeClass(toRemoveString);
26010 function digestClassCounts(classArray, count) {
26011 var classesToUpdate = [];
26013 forEach(classArray, function(className) {
26014 if (count > 0 || classCounts[className]) {
26015 classCounts[className] = (classCounts[className] || 0) + count;
26016 if (classCounts[className] === +(count > 0)) {
26017 classesToUpdate.push(className);
26022 return classesToUpdate.join(' ');
26025 function ngClassIndexWatchAction(newModulo) {
26026 // This watch-action should run before the `ngClass[OneTime]WatchAction()`, thus it
26027 // adds/removes `oldClassString`. If the `ngClass` expression has changed as well, the
26028 // `ngClass[OneTime]WatchAction()` will update the classes.
26029 if (newModulo === selector) {
26030 addClasses(oldClassString);
26032 removeClasses(oldClassString);
26035 oldModulo = newModulo;
26038 function ngClassOneTimeWatchAction(newClassValue) {
26039 var newClassString = toClassString(newClassValue);
26041 if (newClassString !== oldClassString) {
26042 ngClassWatchAction(newClassString);
26046 function ngClassWatchAction(newClassString) {
26047 if (oldModulo === selector) {
26048 updateClasses(oldClassString, newClassString);
26051 oldClassString = newClassString;
26058 function arrayDifference(tokens1, tokens2) {
26059 if (!tokens1 || !tokens1.length) return [];
26060 if (!tokens2 || !tokens2.length) return tokens1;
26065 for (var i = 0; i < tokens1.length; i++) {
26066 var token = tokens1[i];
26067 for (var j = 0; j < tokens2.length; j++) {
26068 if (token === tokens2[j]) continue outer;
26070 values.push(token);
26076 function split(classString) {
26077 return classString && classString.split(' ');
26080 function toClassString(classValue) {
26081 var classString = classValue;
26083 if (isArray(classValue)) {
26084 classString = classValue.map(toClassString).join(' ');
26085 } else if (isObject(classValue)) {
26086 classString = Object.keys(classValue).
26087 filter(function(key) { return classValue[key]; }).
26091 return classString;
26094 function toFlatValue(classValue) {
26095 var flatValue = classValue;
26097 if (isArray(classValue)) {
26098 flatValue = classValue.map(toFlatValue);
26099 } else if (isObject(classValue)) {
26100 var hasUndefined = false;
26102 flatValue = Object.keys(classValue).filter(function(key) {
26103 var value = classValue[key];
26105 if (!hasUndefined && isUndefined(value)) {
26106 hasUndefined = true;
26112 if (hasUndefined) {
26113 // Prevent the `oneTimeLiteralWatchInterceptor` from unregistering
26114 // the watcher, by including at least one `undefined` value.
26115 flatValue.push(undefined);
26129 * The `ngClass` directive allows you to dynamically set CSS classes on an HTML element by databinding
26130 * an expression that represents all classes to be added.
26132 * The directive operates in three different ways, depending on which of three types the expression
26135 * 1. If the expression evaluates to a string, the string should be one or more space-delimited class
26138 * 2. If the expression evaluates to an object, then for each key-value pair of the
26139 * object with a truthy value the corresponding key is used as a class name.
26141 * 3. If the expression evaluates to an array, each element of the array should either be a string as in
26142 * type 1 or an object as in type 2. This means that you can mix strings and objects together in an array
26143 * to give you more control over what CSS classes appear. See the code below for an example of this.
26146 * The directive won't add duplicate classes if a particular class was already set.
26148 * When the expression changes, the previously added classes are removed and only then are the
26149 * new classes added.
26152 * You should not use {@link guide/interpolation interpolation} in the value of the `class`
26153 * attribute, when using the `ngClass` directive on the same element.
26154 * See {@link guide/interpolation#known-issues here} for more info.
26157 * | Animation | Occurs |
26158 * |----------------------------------|-------------------------------------|
26159 * | {@link ng.$animate#addClass addClass} | just before the class is applied to the element |
26160 * | {@link ng.$animate#removeClass removeClass} | just before the class is removed from the element |
26163 * @param {expression} ngClass {@link guide/expression Expression} to eval. The result
26164 * of the evaluation can be a string representing space delimited class
26165 * names, an array, or a map of class names to boolean values. In the case of a map, the
26166 * names of the properties whose values are truthy will be added as css classes to the
26169 * @example Example that demonstrates basic bindings via ngClass directive.
26170 <example name="ng-class">
26171 <file name="index.html">
26172 <p ng-class="{strike: deleted, bold: important, 'has-error': error}">Map Syntax Example</p>
26174 <input type="checkbox" ng-model="deleted">
26175 deleted (apply "strike" class)
26178 <input type="checkbox" ng-model="important">
26179 important (apply "bold" class)
26182 <input type="checkbox" ng-model="error">
26183 error (apply "has-error" class)
26186 <p ng-class="style">Using String Syntax</p>
26187 <input type="text" ng-model="style"
26188 placeholder="Type: bold strike red" aria-label="Type: bold strike red">
26190 <p ng-class="[style1, style2, style3]">Using Array Syntax</p>
26191 <input ng-model="style1"
26192 placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red"><br>
26193 <input ng-model="style2"
26194 placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red 2"><br>
26195 <input ng-model="style3"
26196 placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red 3"><br>
26198 <p ng-class="[style4, {orange: warning}]">Using Array and Map Syntax</p>
26199 <input ng-model="style4" placeholder="Type: bold, strike" aria-label="Type: bold, strike"><br>
26200 <label><input type="checkbox" ng-model="warning"> warning (apply "orange" class)</label>
26202 <file name="style.css">
26204 text-decoration: line-through;
26214 background-color: yellow;
26220 <file name="protractor.js" type="protractor">
26221 var ps = element.all(by.css('p'));
26223 it('should let you toggle the class', function() {
26225 expect(ps.first().getAttribute('class')).not.toMatch(/bold/);
26226 expect(ps.first().getAttribute('class')).not.toMatch(/has-error/);
26228 element(by.model('important')).click();
26229 expect(ps.first().getAttribute('class')).toMatch(/bold/);
26231 element(by.model('error')).click();
26232 expect(ps.first().getAttribute('class')).toMatch(/has-error/);
26235 it('should let you toggle string example', function() {
26236 expect(ps.get(1).getAttribute('class')).toBe('');
26237 element(by.model('style')).clear();
26238 element(by.model('style')).sendKeys('red');
26239 expect(ps.get(1).getAttribute('class')).toBe('red');
26242 it('array example should have 3 classes', function() {
26243 expect(ps.get(2).getAttribute('class')).toBe('');
26244 element(by.model('style1')).sendKeys('bold');
26245 element(by.model('style2')).sendKeys('strike');
26246 element(by.model('style3')).sendKeys('red');
26247 expect(ps.get(2).getAttribute('class')).toBe('bold strike red');
26250 it('array with map example should have 2 classes', function() {
26251 expect(ps.last().getAttribute('class')).toBe('');
26252 element(by.model('style4')).sendKeys('bold');
26253 element(by.model('warning')).click();
26254 expect(ps.last().getAttribute('class')).toBe('bold orange');
26261 The example below demonstrates how to perform animations using ngClass.
26263 <example module="ngAnimate" deps="angular-animate.js" animations="true" name="ng-class">
26264 <file name="index.html">
26265 <input id="setbtn" type="button" value="set" ng-click="myVar='my-class'">
26266 <input id="clearbtn" type="button" value="clear" ng-click="myVar=''">
26268 <span class="base-class" ng-class="myVar">Sample Text</span>
26270 <file name="style.css">
26272 transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
26275 .base-class.my-class {
26280 <file name="protractor.js" type="protractor">
26281 it('should check ng-class', function() {
26282 expect(element(by.css('.base-class')).getAttribute('class')).not.
26283 toMatch(/my-class/);
26285 element(by.id('setbtn')).click();
26287 expect(element(by.css('.base-class')).getAttribute('class')).
26288 toMatch(/my-class/);
26290 element(by.id('clearbtn')).click();
26292 expect(element(by.css('.base-class')).getAttribute('class')).not.
26293 toMatch(/my-class/);
26299 ## ngClass and pre-existing CSS3 Transitions/Animations
26300 The ngClass directive still supports CSS3 Transitions/Animations even if they do not follow the ngAnimate CSS naming structure.
26301 Upon animation ngAnimate will apply supplementary CSS classes to track the start and end of an animation, but this will not hinder
26302 any pre-existing CSS transitions already on the element. To get an idea of what happens during a class-based animation, be sure
26303 to view the step by step details of {@link $animate#addClass $animate.addClass} and
26304 {@link $animate#removeClass $animate.removeClass}.
26306 var ngClassDirective = classDirective('', true);
26314 * The `ngClassOdd` and `ngClassEven` directives work exactly as
26315 * {@link ng.directive:ngClass ngClass}, except they work in
26316 * conjunction with `ngRepeat` and take effect only on odd (even) rows.
26318 * This directive can be applied only within the scope of an
26319 * {@link ng.directive:ngRepeat ngRepeat}.
26322 * @param {expression} ngClassOdd {@link guide/expression Expression} to eval. The result
26323 * of the evaluation can be a string representing space delimited class names or an array.
26326 <example name="ng-class-odd">
26327 <file name="index.html">
26328 <ol ng-init="names=['John', 'Mary', 'Cate', 'Suz']">
26329 <li ng-repeat="name in names">
26330 <span ng-class-odd="'odd'" ng-class-even="'even'">
26336 <file name="style.css">
26344 <file name="protractor.js" type="protractor">
26345 it('should check ng-class-odd and ng-class-even', function() {
26346 expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')).
26348 expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')).
26354 var ngClassOddDirective = classDirective('Odd', 0);
26358 * @name ngClassEven
26362 * The `ngClassOdd` and `ngClassEven` directives work exactly as
26363 * {@link ng.directive:ngClass ngClass}, except they work in
26364 * conjunction with `ngRepeat` and take effect only on odd (even) rows.
26366 * This directive can be applied only within the scope of an
26367 * {@link ng.directive:ngRepeat ngRepeat}.
26370 * @param {expression} ngClassEven {@link guide/expression Expression} to eval. The
26371 * result of the evaluation can be a string representing space delimited class names or an array.
26374 <example name="ng-class-even">
26375 <file name="index.html">
26376 <ol ng-init="names=['John', 'Mary', 'Cate', 'Suz']">
26377 <li ng-repeat="name in names">
26378 <span ng-class-odd="'odd'" ng-class-even="'even'">
26379 {{name}}
26384 <file name="style.css">
26392 <file name="protractor.js" type="protractor">
26393 it('should check ng-class-odd and ng-class-even', function() {
26394 expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')).
26396 expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')).
26402 var ngClassEvenDirective = classDirective('Even', 1);
26410 * The `ngCloak` directive is used to prevent the Angular html template from being briefly
26411 * displayed by the browser in its raw (uncompiled) form while your application is loading. Use this
26412 * directive to avoid the undesirable flicker effect caused by the html template display.
26414 * The directive can be applied to the `<body>` element, but the preferred usage is to apply
26415 * multiple `ngCloak` directives to small portions of the page to permit progressive rendering
26416 * of the browser view.
26418 * `ngCloak` works in cooperation with the following css rule embedded within `angular.js` and
26419 * `angular.min.js`.
26420 * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
26423 * [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
26424 * display: none !important;
26428 * When this css rule is loaded by the browser, all html elements (including their children) that
26429 * are tagged with the `ngCloak` directive are hidden. When Angular encounters this directive
26430 * during the compilation of the template it deletes the `ngCloak` element attribute, making
26431 * the compiled element visible.
26433 * For the best result, the `angular.js` script must be loaded in the head section of the html
26434 * document; alternatively, the css rule above must be included in the external stylesheet of the
26440 <example name="ng-cloak">
26441 <file name="index.html">
26442 <div id="template1" ng-cloak>{{ 'hello' }}</div>
26443 <div id="template2" class="ng-cloak">{{ 'world' }}</div>
26445 <file name="protractor.js" type="protractor">
26446 it('should remove the template directive and css class', function() {
26447 expect($('#template1').getAttribute('ng-cloak')).
26449 expect($('#template2').getAttribute('ng-cloak')).
26456 var ngCloakDirective = ngDirective({
26457 compile: function(element, attr) {
26458 attr.$set('ngCloak', undefined);
26459 element.removeClass('ng-cloak');
26465 * @name ngController
26468 * The `ngController` directive attaches a controller class to the view. This is a key aspect of how angular
26469 * supports the principles behind the Model-View-Controller design pattern.
26471 * MVC components in angular:
26473 * * Model — Models are the properties of a scope; scopes are attached to the DOM where scope properties
26474 * are accessed through bindings.
26475 * * View — The template (HTML with data bindings) that is rendered into the View.
26476 * * Controller — The `ngController` directive specifies a Controller class; the class contains business
26477 * logic behind the application to decorate the scope with functions and values
26479 * Note that you can also attach controllers to the DOM by declaring it in a route definition
26480 * via the {@link ngRoute.$route $route} service. A common mistake is to declare the controller
26481 * again using `ng-controller` in the template itself. This will cause the controller to be attached
26482 * and executed twice.
26487 * @param {expression} ngController Name of a constructor function registered with the current
26488 * {@link ng.$controllerProvider $controllerProvider} or an {@link guide/expression expression}
26489 * that on the current scope evaluates to a constructor function.
26491 * The controller instance can be published into a scope property by specifying
26492 * `ng-controller="as propertyName"`.
26494 * If the current `$controllerProvider` is configured to use globals (via
26495 * {@link ng.$controllerProvider#allowGlobals `$controllerProvider.allowGlobals()` }), this may
26496 * also be the name of a globally accessible constructor function (deprecated, not recommended).
26499 * Here is a simple form for editing user contact information. Adding, removing, clearing, and
26500 * greeting are methods declared on the controller (see source tab). These methods can
26501 * easily be called from the angular markup. Any changes to the data are automatically reflected
26502 * in the View without the need for a manual update.
26504 * Two different declaration styles are included below:
26506 * * one binds methods and properties directly onto the controller using `this`:
26507 * `ng-controller="SettingsController1 as settings"`
26508 * * one injects `$scope` into the controller:
26509 * `ng-controller="SettingsController2"`
26511 * The second option is more common in the Angular community, and is generally used in boilerplates
26512 * and in this guide. However, there are advantages to binding properties directly to the controller
26513 * and avoiding scope.
26515 * * Using `controller as` makes it obvious which controller you are accessing in the template when
26516 * multiple controllers apply to an element.
26517 * * If you are writing your controllers as classes you have easier access to the properties and
26518 * methods, which will appear on the scope, from inside the controller code.
26519 * * Since there is always a `.` in the bindings, you don't have to worry about prototypal
26520 * inheritance masking primitives.
26522 * This example demonstrates the `controller as` syntax.
26524 * <example name="ngControllerAs" module="controllerAsExample">
26525 * <file name="index.html">
26526 * <div id="ctrl-as-exmpl" ng-controller="SettingsController1 as settings">
26527 * <label>Name: <input type="text" ng-model="settings.name"/></label>
26528 * <button ng-click="settings.greet()">greet</button><br/>
26531 * <li ng-repeat="contact in settings.contacts">
26532 * <select ng-model="contact.type" aria-label="Contact method" id="select_{{$index}}">
26533 * <option>phone</option>
26534 * <option>email</option>
26536 * <input type="text" ng-model="contact.value" aria-labelledby="select_{{$index}}" />
26537 * <button ng-click="settings.clearContact(contact)">clear</button>
26538 * <button ng-click="settings.removeContact(contact)" aria-label="Remove">X</button>
26540 * <li><button ng-click="settings.addContact()">add</button></li>
26544 * <file name="app.js">
26545 * angular.module('controllerAsExample', [])
26546 * .controller('SettingsController1', SettingsController1);
26548 * function SettingsController1() {
26549 * this.name = 'John Smith';
26550 * this.contacts = [
26551 * {type: 'phone', value: '408 555 1212'},
26552 * {type: 'email', value: 'john.smith@example.org'}
26556 * SettingsController1.prototype.greet = function() {
26557 * alert(this.name);
26560 * SettingsController1.prototype.addContact = function() {
26561 * this.contacts.push({type: 'email', value: 'yourname@example.org'});
26564 * SettingsController1.prototype.removeContact = function(contactToRemove) {
26565 * var index = this.contacts.indexOf(contactToRemove);
26566 * this.contacts.splice(index, 1);
26569 * SettingsController1.prototype.clearContact = function(contact) {
26570 * contact.type = 'phone';
26571 * contact.value = '';
26574 * <file name="protractor.js" type="protractor">
26575 * it('should check controller as', function() {
26576 * var container = element(by.id('ctrl-as-exmpl'));
26577 * expect(container.element(by.model('settings.name'))
26578 * .getAttribute('value')).toBe('John Smith');
26580 * var firstRepeat =
26581 * container.element(by.repeater('contact in settings.contacts').row(0));
26582 * var secondRepeat =
26583 * container.element(by.repeater('contact in settings.contacts').row(1));
26585 * expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
26586 * .toBe('408 555 1212');
26588 * expect(secondRepeat.element(by.model('contact.value')).getAttribute('value'))
26589 * .toBe('john.smith@example.org');
26591 * firstRepeat.element(by.buttonText('clear')).click();
26593 * expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
26596 * container.element(by.buttonText('add')).click();
26598 * expect(container.element(by.repeater('contact in settings.contacts').row(2))
26599 * .element(by.model('contact.value'))
26600 * .getAttribute('value'))
26601 * .toBe('yourname@example.org');
26606 * This example demonstrates the "attach to `$scope`" style of controller.
26608 * <example name="ngController" module="controllerExample">
26609 * <file name="index.html">
26610 * <div id="ctrl-exmpl" ng-controller="SettingsController2">
26611 * <label>Name: <input type="text" ng-model="name"/></label>
26612 * <button ng-click="greet()">greet</button><br/>
26615 * <li ng-repeat="contact in contacts">
26616 * <select ng-model="contact.type" id="select_{{$index}}">
26617 * <option>phone</option>
26618 * <option>email</option>
26620 * <input type="text" ng-model="contact.value" aria-labelledby="select_{{$index}}" />
26621 * <button ng-click="clearContact(contact)">clear</button>
26622 * <button ng-click="removeContact(contact)">X</button>
26624 * <li>[ <button ng-click="addContact()">add</button> ]</li>
26628 * <file name="app.js">
26629 * angular.module('controllerExample', [])
26630 * .controller('SettingsController2', ['$scope', SettingsController2]);
26632 * function SettingsController2($scope) {
26633 * $scope.name = 'John Smith';
26634 * $scope.contacts = [
26635 * {type:'phone', value:'408 555 1212'},
26636 * {type:'email', value:'john.smith@example.org'}
26639 * $scope.greet = function() {
26640 * alert($scope.name);
26643 * $scope.addContact = function() {
26644 * $scope.contacts.push({type:'email', value:'yourname@example.org'});
26647 * $scope.removeContact = function(contactToRemove) {
26648 * var index = $scope.contacts.indexOf(contactToRemove);
26649 * $scope.contacts.splice(index, 1);
26652 * $scope.clearContact = function(contact) {
26653 * contact.type = 'phone';
26654 * contact.value = '';
26658 * <file name="protractor.js" type="protractor">
26659 * it('should check controller', function() {
26660 * var container = element(by.id('ctrl-exmpl'));
26662 * expect(container.element(by.model('name'))
26663 * .getAttribute('value')).toBe('John Smith');
26665 * var firstRepeat =
26666 * container.element(by.repeater('contact in contacts').row(0));
26667 * var secondRepeat =
26668 * container.element(by.repeater('contact in contacts').row(1));
26670 * expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
26671 * .toBe('408 555 1212');
26672 * expect(secondRepeat.element(by.model('contact.value')).getAttribute('value'))
26673 * .toBe('john.smith@example.org');
26675 * firstRepeat.element(by.buttonText('clear')).click();
26677 * expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
26680 * container.element(by.buttonText('add')).click();
26682 * expect(container.element(by.repeater('contact in contacts').row(2))
26683 * .element(by.model('contact.value'))
26684 * .getAttribute('value'))
26685 * .toBe('yourname@example.org');
26691 var ngControllerDirective = [function() {
26708 * Angular has some features that can conflict with certain restrictions that are applied when using
26709 * [CSP (Content Security Policy)](https://developer.mozilla.org/en/Security/CSP) rules.
26711 * If you intend to implement CSP with these rules then you must tell Angular not to use these
26714 * This is necessary when developing things like Google Chrome Extensions or Universal Windows Apps.
26717 * The following default rules in CSP affect Angular:
26719 * * The use of `eval()`, `Function(string)` and similar functions to dynamically create and execute
26720 * code from strings is forbidden. Angular makes use of this in the {@link $parse} service to
26721 * provide a 30% increase in the speed of evaluating Angular expressions. (This CSP rule can be
26722 * disabled with the CSP keyword `unsafe-eval`, but it is generally not recommended as it would
26723 * weaken the protections offered by CSP.)
26725 * * The use of inline resources, such as inline `<script>` and `<style>` elements, are forbidden.
26726 * This prevents apps from injecting custom styles directly into the document. Angular makes use of
26727 * this to include some CSS rules (e.g. {@link ngCloak} and {@link ngHide}). To make these
26728 * directives work when a CSP rule is blocking inline styles, you must link to the `angular-csp.css`
26729 * in your HTML manually. (This CSP rule can be disabled with the CSP keyword `unsafe-inline`, but
26730 * it is generally not recommended as it would weaken the protections offered by CSP.)
26732 * If you do not provide `ngCsp` then Angular tries to autodetect if CSP is blocking dynamic code
26733 * creation from strings (e.g., `unsafe-eval` not specified in CSP header) and automatically
26734 * deactivates this feature in the {@link $parse} service. This autodetection, however, triggers a
26735 * CSP error to be logged in the console:
26738 * Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of
26739 * script in the following Content Security Policy directive: "default-src 'self'". Note that
26740 * 'script-src' was not explicitly set, so 'default-src' is used as a fallback.
26743 * This error is harmless but annoying. To prevent the error from showing up, put the `ngCsp`
26744 * directive on an element of the HTML document that appears before the `<script>` tag that loads
26745 * the `angular.js` file.
26747 * *Note: This directive is only available in the `ng-csp` and `data-ng-csp` attribute form.*
26749 * You can specify which of the CSP related Angular features should be deactivated by providing
26750 * a value for the `ng-csp` attribute. The options are as follows:
26752 * * no-inline-style: this stops Angular from injecting CSS styles into the DOM
26754 * * no-unsafe-eval: this stops Angular from optimizing $parse with unsafe eval of strings
26756 * You can use these values in the following combinations:
26759 * * No declaration means that Angular will assume that you can do inline styles, but it will do
26760 * a runtime check for unsafe-eval. E.g. `<body>`. This is backwardly compatible with previous
26761 * versions of Angular.
26763 * * A simple `ng-csp` (or `data-ng-csp`) attribute will tell Angular to deactivate both inline
26764 * styles and unsafe eval. E.g. `<body ng-csp>`. This is backwardly compatible with previous
26765 * versions of Angular.
26767 * * Specifying only `no-unsafe-eval` tells Angular that we must not use eval, but that we can
26768 * inject inline styles. E.g. `<body ng-csp="no-unsafe-eval">`.
26770 * * Specifying only `no-inline-style` tells Angular that we must not inject styles, but that we can
26771 * run eval - no automatic check for unsafe eval will occur. E.g. `<body ng-csp="no-inline-style">`
26773 * * Specifying both `no-unsafe-eval` and `no-inline-style` tells Angular that we must not inject
26774 * styles nor use eval, which is the same as an empty: ng-csp.
26775 * E.g.`<body ng-csp="no-inline-style;no-unsafe-eval">`
26778 * This example shows how to apply the `ngCsp` directive to the `html` tag.
26781 <html ng-app ng-csp>
26787 <!-- Note: the `.csp` suffix in the example name triggers CSP mode in our http server! -->
26788 <example name="example.csp" module="cspExample" ng-csp="true">
26789 <file name="index.html">
26790 <div ng-controller="MainController as ctrl">
26792 <button ng-click="ctrl.inc()" id="inc">Increment</button>
26793 <span id="counter">
26799 <button ng-click="ctrl.evil()" id="evil">Evil</button>
26800 <span id="evilError">
26806 <file name="script.js">
26807 angular.module('cspExample', [])
26808 .controller('MainController', function MainController() {
26810 this.inc = function() {
26813 this.evil = function() {
26815 eval('1+2'); // eslint-disable-line no-eval
26817 this.evilError = e.message;
26822 <file name="protractor.js" type="protractor">
26823 var util, webdriver;
26825 var incBtn = element(by.id('inc'));
26826 var counter = element(by.id('counter'));
26827 var evilBtn = element(by.id('evil'));
26828 var evilError = element(by.id('evilError'));
26830 function getAndClearSevereErrors() {
26831 return browser.manage().logs().get('browser').then(function(browserLog) {
26832 return browserLog.filter(function(logEntry) {
26833 return logEntry.level.value > webdriver.logging.Level.WARNING.value;
26838 function clearErrors() {
26839 getAndClearSevereErrors();
26842 function expectNoErrors() {
26843 getAndClearSevereErrors().then(function(filteredLog) {
26844 expect(filteredLog.length).toEqual(0);
26845 if (filteredLog.length) {
26846 console.log('browser console errors: ' + util.inspect(filteredLog));
26851 function expectError(regex) {
26852 getAndClearSevereErrors().then(function(filteredLog) {
26854 filteredLog.forEach(function(log) {
26855 if (log.message.match(regex)) {
26860 throw new Error('expected an error that matches ' + regex);
26865 beforeEach(function() {
26866 util = require('util');
26867 webdriver = require('selenium-webdriver');
26870 // For now, we only test on Chrome,
26871 // as Safari does not load the page with Protractor's injected scripts,
26872 // and Firefox webdriver always disables content security policy (#6358)
26873 if (browser.params.browser !== 'chrome') {
26877 it('should not report errors when the page is loaded', function() {
26878 // clear errors so we are not dependent on previous tests
26880 // Need to reload the page as the page is already loaded when
26882 browser.driver.getCurrentUrl().then(function(url) {
26888 it('should evaluate expressions', function() {
26889 expect(counter.getText()).toEqual('0');
26891 expect(counter.getText()).toEqual('1');
26895 it('should throw and report an error when using "eval"', function() {
26897 expect(evilError.getText()).toMatch(/Content Security Policy/);
26898 expectError(/Content Security Policy/);
26904 // `ngCsp` is not implemented as a proper directive any more, because we need it be processed while
26905 // we bootstrap the app (before `$parse` is instantiated). For this reason, we just have the `csp()`
26906 // fn that looks for the `ng-csp` attribute anywhere in the current doc.
26913 * The ngClick directive allows you to specify custom behavior when
26914 * an element is clicked.
26918 * @param {expression} ngClick {@link guide/expression Expression} to evaluate upon
26919 * click. ({@link guide/expression#-event- Event object is available as `$event`})
26922 <example name="ng-click">
26923 <file name="index.html">
26924 <button ng-click="count = count + 1" ng-init="count=0">
26931 <file name="protractor.js" type="protractor">
26932 it('should check ng-click', function() {
26933 expect(element(by.binding('count')).getText()).toMatch('0');
26934 element(by.css('button')).click();
26935 expect(element(by.binding('count')).getText()).toMatch('1');
26941 * A collection of directives that allows creation of custom event handlers that are defined as
26942 * angular expressions and are compiled and executed within the current scope.
26944 var ngEventDirectives = {};
26946 // For events that might fire synchronously during DOM manipulation
26947 // we need to execute their event handlers asynchronously using $evalAsync,
26948 // so that they are not executed in an inconsistent state.
26949 var forceAsyncEvents = {
26954 'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste'.split(' '),
26955 function(eventName) {
26956 var directiveName = directiveNormalize('ng-' + eventName);
26957 ngEventDirectives[directiveName] = ['$parse', '$rootScope', function($parse, $rootScope) {
26960 compile: function($element, attr) {
26962 // We expose the powerful `$event` object on the scope that provides access to the Window,
26963 // etc. This is OK, because expressions are not sandboxed any more (and the expression
26964 // sandbox was never meant to be a security feature anyway).
26965 var fn = $parse(attr[directiveName]);
26966 return function ngEventHandler(scope, element) {
26967 element.on(eventName, function(event) {
26968 var callback = function() {
26969 fn(scope, {$event: event});
26971 if (forceAsyncEvents[eventName] && $rootScope.$$phase) {
26972 scope.$evalAsync(callback);
26974 scope.$apply(callback);
26989 * The `ngDblclick` directive allows you to specify custom behavior on a dblclick event.
26993 * @param {expression} ngDblclick {@link guide/expression Expression} to evaluate upon
26994 * a dblclick. (The Event object is available as `$event`)
26997 <example name="ng-dblclick">
26998 <file name="index.html">
26999 <button ng-dblclick="count = count + 1" ng-init="count=0">
27000 Increment (on double click)
27010 * @name ngMousedown
27013 * The ngMousedown directive allows you to specify custom behavior on mousedown event.
27017 * @param {expression} ngMousedown {@link guide/expression Expression} to evaluate upon
27018 * mousedown. ({@link guide/expression#-event- Event object is available as `$event`})
27021 <example name="ng-mousedown">
27022 <file name="index.html">
27023 <button ng-mousedown="count = count + 1" ng-init="count=0">
27024 Increment (on mouse down)
27037 * Specify custom behavior on mouseup event.
27041 * @param {expression} ngMouseup {@link guide/expression Expression} to evaluate upon
27042 * mouseup. ({@link guide/expression#-event- Event object is available as `$event`})
27045 <example name="ng-mouseup">
27046 <file name="index.html">
27047 <button ng-mouseup="count = count + 1" ng-init="count=0">
27048 Increment (on mouse up)
27057 * @name ngMouseover
27060 * Specify custom behavior on mouseover event.
27064 * @param {expression} ngMouseover {@link guide/expression Expression} to evaluate upon
27065 * mouseover. ({@link guide/expression#-event- Event object is available as `$event`})
27068 <example name="ng-mouseover">
27069 <file name="index.html">
27070 <button ng-mouseover="count = count + 1" ng-init="count=0">
27071 Increment (when mouse is over)
27081 * @name ngMouseenter
27084 * Specify custom behavior on mouseenter event.
27088 * @param {expression} ngMouseenter {@link guide/expression Expression} to evaluate upon
27089 * mouseenter. ({@link guide/expression#-event- Event object is available as `$event`})
27092 <example name="ng-mouseenter">
27093 <file name="index.html">
27094 <button ng-mouseenter="count = count + 1" ng-init="count=0">
27095 Increment (when mouse enters)
27105 * @name ngMouseleave
27108 * Specify custom behavior on mouseleave event.
27112 * @param {expression} ngMouseleave {@link guide/expression Expression} to evaluate upon
27113 * mouseleave. ({@link guide/expression#-event- Event object is available as `$event`})
27116 <example name="ng-mouseleave">
27117 <file name="index.html">
27118 <button ng-mouseleave="count = count + 1" ng-init="count=0">
27119 Increment (when mouse leaves)
27129 * @name ngMousemove
27132 * Specify custom behavior on mousemove event.
27136 * @param {expression} ngMousemove {@link guide/expression Expression} to evaluate upon
27137 * mousemove. ({@link guide/expression#-event- Event object is available as `$event`})
27140 <example name="ng-mousemove">
27141 <file name="index.html">
27142 <button ng-mousemove="count = count + 1" ng-init="count=0">
27143 Increment (when mouse moves)
27156 * Specify custom behavior on keydown event.
27160 * @param {expression} ngKeydown {@link guide/expression Expression} to evaluate upon
27161 * keydown. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
27164 <example name="ng-keydown">
27165 <file name="index.html">
27166 <input ng-keydown="count = count + 1" ng-init="count=0">
27167 key down count: {{count}}
27178 * Specify custom behavior on keyup event.
27182 * @param {expression} ngKeyup {@link guide/expression Expression} to evaluate upon
27183 * keyup. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
27186 <example name="ng-keyup">
27187 <file name="index.html">
27188 <p>Typing in the input box below updates the key count</p>
27189 <input ng-keyup="count = count + 1" ng-init="count=0"> key up count: {{count}}
27191 <p>Typing in the input box below updates the keycode</p>
27192 <input ng-keyup="event=$event">
27193 <p>event keyCode: {{ event.keyCode }}</p>
27194 <p>event altKey: {{ event.altKey }}</p>
27205 * Specify custom behavior on keypress event.
27208 * @param {expression} ngKeypress {@link guide/expression Expression} to evaluate upon
27209 * keypress. ({@link guide/expression#-event- Event object is available as `$event`}
27210 * and can be interrogated for keyCode, altKey, etc.)
27213 <example name="ng-keypress">
27214 <file name="index.html">
27215 <input ng-keypress="count = count + 1" ng-init="count=0">
27216 key press count: {{count}}
27227 * Enables binding angular expressions to onsubmit events.
27229 * Additionally it prevents the default action (which for form means sending the request to the
27230 * server and reloading the current page), but only if the form does not contain `action`,
27231 * `data-action`, or `x-action` attributes.
27233 * <div class="alert alert-warning">
27234 * **Warning:** Be careful not to cause "double-submission" by using both the `ngClick` and
27235 * `ngSubmit` handlers together. See the
27236 * {@link form#submitting-a-form-and-preventing-the-default-action `form` directive documentation}
27237 * for a detailed discussion of when `ngSubmit` may be triggered.
27242 * @param {expression} ngSubmit {@link guide/expression Expression} to eval.
27243 * ({@link guide/expression#-event- Event object is available as `$event`})
27246 <example module="submitExample" name="ng-submit">
27247 <file name="index.html">
27249 angular.module('submitExample', [])
27250 .controller('ExampleController', ['$scope', function($scope) {
27252 $scope.text = 'hello';
27253 $scope.submit = function() {
27255 $scope.list.push(this.text);
27261 <form ng-submit="submit()" ng-controller="ExampleController">
27262 Enter text and hit enter:
27263 <input type="text" ng-model="text" name="text" />
27264 <input type="submit" id="submit" value="Submit" />
27265 <pre>list={{list}}</pre>
27268 <file name="protractor.js" type="protractor">
27269 it('should check ng-submit', function() {
27270 expect(element(by.binding('list')).getText()).toBe('list=[]');
27271 element(by.css('#submit')).click();
27272 expect(element(by.binding('list')).getText()).toContain('hello');
27273 expect(element(by.model('text')).getAttribute('value')).toBe('');
27275 it('should ignore empty strings', function() {
27276 expect(element(by.binding('list')).getText()).toBe('list=[]');
27277 element(by.css('#submit')).click();
27278 element(by.css('#submit')).click();
27279 expect(element(by.binding('list')).getText()).toContain('hello');
27290 * Specify custom behavior on focus event.
27292 * Note: As the `focus` event is executed synchronously when calling `input.focus()`
27293 * AngularJS executes the expression using `scope.$evalAsync` if the event is fired
27294 * during an `$apply` to ensure a consistent state.
27296 * @element window, input, select, textarea, a
27298 * @param {expression} ngFocus {@link guide/expression Expression} to evaluate upon
27299 * focus. ({@link guide/expression#-event- Event object is available as `$event`})
27302 * See {@link ng.directive:ngClick ngClick}
27310 * Specify custom behavior on blur event.
27312 * A [blur event](https://developer.mozilla.org/en-US/docs/Web/Events/blur) fires when
27313 * an element has lost focus.
27315 * Note: As the `blur` event is executed synchronously also during DOM manipulations
27316 * (e.g. removing a focussed input),
27317 * AngularJS executes the expression using `scope.$evalAsync` if the event is fired
27318 * during an `$apply` to ensure a consistent state.
27320 * @element window, input, select, textarea, a
27322 * @param {expression} ngBlur {@link guide/expression Expression} to evaluate upon
27323 * blur. ({@link guide/expression#-event- Event object is available as `$event`})
27326 * See {@link ng.directive:ngClick ngClick}
27334 * Specify custom behavior on copy event.
27336 * @element window, input, select, textarea, a
27338 * @param {expression} ngCopy {@link guide/expression Expression} to evaluate upon
27339 * copy. ({@link guide/expression#-event- Event object is available as `$event`})
27342 <example name="ng-copy">
27343 <file name="index.html">
27344 <input ng-copy="copied=true" ng-init="copied=false; value='copy me'" ng-model="value">
27355 * Specify custom behavior on cut event.
27357 * @element window, input, select, textarea, a
27359 * @param {expression} ngCut {@link guide/expression Expression} to evaluate upon
27360 * cut. ({@link guide/expression#-event- Event object is available as `$event`})
27363 <example name="ng-cut">
27364 <file name="index.html">
27365 <input ng-cut="cut=true" ng-init="cut=false; value='cut me'" ng-model="value">
27376 * Specify custom behavior on paste event.
27378 * @element window, input, select, textarea, a
27380 * @param {expression} ngPaste {@link guide/expression Expression} to evaluate upon
27381 * paste. ({@link guide/expression#-event- Event object is available as `$event`})
27384 <example name="ng-paste">
27385 <file name="index.html">
27386 <input ng-paste="paste=true" ng-init="paste=false" placeholder='paste here'>
27399 * The `ngIf` directive removes or recreates a portion of the DOM tree based on an
27400 * {expression}. If the expression assigned to `ngIf` evaluates to a false
27401 * value then the element is removed from the DOM, otherwise a clone of the
27402 * element is reinserted into the DOM.
27404 * `ngIf` differs from `ngShow` and `ngHide` in that `ngIf` completely removes and recreates the
27405 * element in the DOM rather than changing its visibility via the `display` css property. A common
27406 * case when this difference is significant is when using css selectors that rely on an element's
27407 * position within the DOM, such as the `:first-child` or `:last-child` pseudo-classes.
27409 * Note that when an element is removed using `ngIf` its scope is destroyed and a new scope
27410 * is created when the element is restored. The scope created within `ngIf` inherits from
27411 * its parent scope using
27412 * [prototypal inheritance](https://github.com/angular/angular.js/wiki/Understanding-Scopes#javascript-prototypal-inheritance).
27413 * An important implication of this is if `ngModel` is used within `ngIf` to bind to
27414 * a javascript primitive defined in the parent scope. In this case any modifications made to the
27415 * variable within the child scope will override (hide) the value in the parent scope.
27417 * Also, `ngIf` recreates elements using their compiled state. An example of this behavior
27418 * is if an element's class attribute is directly modified after it's compiled, using something like
27419 * jQuery's `.addClass()` method, and the element is later removed. When `ngIf` recreates the element
27420 * the added class will be lost because the original compiled state is used to regenerate the element.
27422 * Additionally, you can provide animations via the `ngAnimate` module to animate the `enter`
27423 * and `leave` effects.
27426 * | Animation | Occurs |
27427 * |----------------------------------|-------------------------------------|
27428 * | {@link ng.$animate#enter enter} | just after the `ngIf` contents change and a new DOM element is created and injected into the `ngIf` container |
27429 * | {@link ng.$animate#leave leave} | just before the `ngIf` contents are removed from the DOM |
27434 * @param {expression} ngIf If the {@link guide/expression expression} is falsy then
27435 * the element is removed from the DOM tree. If it is truthy a copy of the compiled
27436 * element is added to the DOM tree.
27439 <example module="ngAnimate" deps="angular-animate.js" animations="true" name="ng-if">
27440 <file name="index.html">
27441 <label>Click me: <input type="checkbox" ng-model="checked" ng-init="checked=true" /></label><br/>
27443 <span ng-if="checked" class="animate-if">
27444 This is removed when the checkbox is unchecked.
27447 <file name="animations.css">
27450 border:1px solid black;
27454 .animate-if.ng-enter, .animate-if.ng-leave {
27455 transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
27458 .animate-if.ng-enter,
27459 .animate-if.ng-leave.ng-leave-active {
27463 .animate-if.ng-leave,
27464 .animate-if.ng-enter.ng-enter-active {
27470 var ngIfDirective = ['$animate', '$compile', function($animate, $compile) {
27472 multiElement: true,
27473 transclude: 'element',
27478 link: function($scope, $element, $attr, ctrl, $transclude) {
27479 var block, childScope, previousElements;
27480 $scope.$watch($attr.ngIf, function ngIfWatchAction(value) {
27484 $transclude(function(clone, newScope) {
27485 childScope = newScope;
27486 clone[clone.length++] = $compile.$$createComment('end ngIf', $attr.ngIf);
27487 // Note: We only need the first/last node of the cloned nodes.
27488 // However, we need to keep the reference to the jqlite wrapper as it might be changed later
27489 // by a directive with templateUrl when its template arrives.
27493 $animate.enter(clone, $element.parent(), $element);
27497 if (previousElements) {
27498 previousElements.remove();
27499 previousElements = null;
27502 childScope.$destroy();
27506 previousElements = getBlockNodes(block.clone);
27507 $animate.leave(previousElements).done(function(response) {
27508 if (response !== false) previousElements = null;
27524 * Fetches, compiles and includes an external HTML fragment.
27526 * By default, the template URL is restricted to the same domain and protocol as the
27527 * application document. This is done by calling {@link $sce#getTrustedResourceUrl
27528 * $sce.getTrustedResourceUrl} on it. To load templates from other domains or protocols
27529 * you may either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist them} or
27530 * {@link $sce#trustAsResourceUrl wrap them} as trusted values. Refer to Angular's {@link
27531 * ng.$sce Strict Contextual Escaping}.
27533 * In addition, the browser's
27534 * [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest)
27535 * and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/)
27536 * policy may further restrict whether the template is successfully loaded.
27537 * For example, `ngInclude` won't work for cross-domain requests on all browsers and for `file://`
27538 * access on some browsers.
27541 * | Animation | Occurs |
27542 * |----------------------------------|-------------------------------------|
27543 * | {@link ng.$animate#enter enter} | when the expression changes, on the new include |
27544 * | {@link ng.$animate#leave leave} | when the expression changes, on the old include |
27546 * The enter and leave animation occur concurrently.
27551 * @param {string} ngInclude|src angular expression evaluating to URL. If the source is a string constant,
27552 * make sure you wrap it in **single** quotes, e.g. `src="'myPartialTemplate.html'"`.
27553 * @param {string=} onload Expression to evaluate when a new partial is loaded.
27554 * <div class="alert alert-warning">
27555 * **Note:** When using onload on SVG elements in IE11, the browser will try to call
27556 * a function with the name on the window element, which will usually throw a
27557 * "function is undefined" error. To fix this, you can instead use `data-onload` or a
27558 * different form that {@link guide/directive#normalization matches} `onload`.
27561 * @param {string=} autoscroll Whether `ngInclude` should call {@link ng.$anchorScroll
27562 * $anchorScroll} to scroll the viewport after the content is loaded.
27564 * - If the attribute is not set, disable scrolling.
27565 * - If the attribute is set without value, enable scrolling.
27566 * - Otherwise enable scrolling only if the expression evaluates to truthy value.
27569 <example module="includeExample" deps="angular-animate.js" animations="true" name="ng-include">
27570 <file name="index.html">
27571 <div ng-controller="ExampleController">
27572 <select ng-model="template" ng-options="t.name for t in templates">
27573 <option value="">(blank)</option>
27575 url of the template: <code>{{template.url}}</code>
27577 <div class="slide-animate-container">
27578 <div class="slide-animate" ng-include="template.url"></div>
27582 <file name="script.js">
27583 angular.module('includeExample', ['ngAnimate'])
27584 .controller('ExampleController', ['$scope', function($scope) {
27586 [{ name: 'template1.html', url: 'template1.html'},
27587 { name: 'template2.html', url: 'template2.html'}];
27588 $scope.template = $scope.templates[0];
27591 <file name="template1.html">
27592 Content of template1.html
27594 <file name="template2.html">
27595 Content of template2.html
27597 <file name="animations.css">
27598 .slide-animate-container {
27601 border:1px solid black;
27610 .slide-animate.ng-enter, .slide-animate.ng-leave {
27611 transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
27622 .slide-animate.ng-enter {
27625 .slide-animate.ng-enter.ng-enter-active {
27629 .slide-animate.ng-leave {
27632 .slide-animate.ng-leave.ng-leave-active {
27636 <file name="protractor.js" type="protractor">
27637 var templateSelect = element(by.model('template'));
27638 var includeElem = element(by.css('[ng-include]'));
27640 it('should load template1.html', function() {
27641 expect(includeElem.getText()).toMatch(/Content of template1.html/);
27644 it('should load template2.html', function() {
27645 if (browser.params.browser === 'firefox') {
27646 // Firefox can't handle using selects
27647 // See https://github.com/angular/protractor/issues/480
27650 templateSelect.click();
27651 templateSelect.all(by.css('option')).get(2).click();
27652 expect(includeElem.getText()).toMatch(/Content of template2.html/);
27655 it('should change to blank', function() {
27656 if (browser.params.browser === 'firefox') {
27657 // Firefox can't handle using selects
27660 templateSelect.click();
27661 templateSelect.all(by.css('option')).get(0).click();
27662 expect(includeElem.isPresent()).toBe(false);
27671 * @name ngInclude#$includeContentRequested
27672 * @eventType emit on the scope ngInclude was declared in
27674 * Emitted every time the ngInclude content is requested.
27676 * @param {Object} angularEvent Synthetic event object.
27677 * @param {String} src URL of content to load.
27683 * @name ngInclude#$includeContentLoaded
27684 * @eventType emit on the current ngInclude scope
27686 * Emitted every time the ngInclude content is reloaded.
27688 * @param {Object} angularEvent Synthetic event object.
27689 * @param {String} src URL of content to load.
27695 * @name ngInclude#$includeContentError
27696 * @eventType emit on the scope ngInclude was declared in
27698 * Emitted when a template HTTP request yields an erroneous response (status < 200 || status > 299)
27700 * @param {Object} angularEvent Synthetic event object.
27701 * @param {String} src URL of content to load.
27703 var ngIncludeDirective = ['$templateRequest', '$anchorScroll', '$animate',
27704 function($templateRequest, $anchorScroll, $animate) {
27709 transclude: 'element',
27710 controller: angular.noop,
27711 compile: function(element, attr) {
27712 var srcExp = attr.ngInclude || attr.src,
27713 onloadExp = attr.onload || '',
27714 autoScrollExp = attr.autoscroll;
27716 return function(scope, $element, $attr, ctrl, $transclude) {
27717 var changeCounter = 0,
27722 var cleanupLastIncludeContent = function() {
27723 if (previousElement) {
27724 previousElement.remove();
27725 previousElement = null;
27727 if (currentScope) {
27728 currentScope.$destroy();
27729 currentScope = null;
27731 if (currentElement) {
27732 $animate.leave(currentElement).done(function(response) {
27733 if (response !== false) previousElement = null;
27735 previousElement = currentElement;
27736 currentElement = null;
27740 scope.$watch(srcExp, function ngIncludeWatchAction(src) {
27741 var afterAnimation = function(response) {
27742 if (response !== false && isDefined(autoScrollExp) &&
27743 (!autoScrollExp || scope.$eval(autoScrollExp))) {
27747 var thisChangeId = ++changeCounter;
27750 //set the 2nd param to true to ignore the template request error so that the inner
27751 //contents and scope can be cleaned up.
27752 $templateRequest(src, true).then(function(response) {
27753 if (scope.$$destroyed) return;
27755 if (thisChangeId !== changeCounter) return;
27756 var newScope = scope.$new();
27757 ctrl.template = response;
27759 // Note: This will also link all children of ng-include that were contained in the original
27760 // html. If that content contains controllers, ... they could pollute/change the scope.
27761 // However, using ng-include on an element with additional content does not make sense...
27762 // Note: We can't remove them in the cloneAttchFn of $transclude as that
27763 // function is called before linking the content, which would apply child
27764 // directives to non existing elements.
27765 var clone = $transclude(newScope, function(clone) {
27766 cleanupLastIncludeContent();
27767 $animate.enter(clone, null, $element).done(afterAnimation);
27770 currentScope = newScope;
27771 currentElement = clone;
27773 currentScope.$emit('$includeContentLoaded', src);
27774 scope.$eval(onloadExp);
27776 if (scope.$$destroyed) return;
27778 if (thisChangeId === changeCounter) {
27779 cleanupLastIncludeContent();
27780 scope.$emit('$includeContentError', src);
27783 scope.$emit('$includeContentRequested', src);
27785 cleanupLastIncludeContent();
27786 ctrl.template = null;
27794 // This directive is called during the $transclude call of the first `ngInclude` directive.
27795 // It will replace and compile the content of the element with the loaded template.
27796 // We need this directive so that the element content is already filled when
27797 // the link function of another directive on the same element as ngInclude
27799 var ngIncludeFillContentDirective = ['$compile',
27800 function($compile) {
27804 require: 'ngInclude',
27805 link: function(scope, $element, $attr, ctrl) {
27806 if (toString.call($element[0]).match(/SVG/)) {
27807 // WebKit: https://bugs.webkit.org/show_bug.cgi?id=135698 --- SVG elements do not
27808 // support innerHTML, so detect this here and try to generate the contents
27811 $compile(jqLiteBuildFragment(ctrl.template, window.document).childNodes)(scope,
27812 function namespaceAdaptedClone(clone) {
27813 $element.append(clone);
27814 }, {futureParentElement: $element});
27818 $element.html(ctrl.template);
27819 $compile($element.contents())(scope);
27830 * The `ngInit` directive allows you to evaluate an expression in the
27833 * <div class="alert alert-danger">
27834 * This directive can be abused to add unnecessary amounts of logic into your templates.
27835 * There are only a few appropriate uses of `ngInit`, such as for aliasing special properties of
27836 * {@link ng.directive:ngRepeat `ngRepeat`}, as seen in the demo below; and for injecting data via
27837 * server side scripting. Besides these few cases, you should use {@link guide/controller controllers}
27838 * rather than `ngInit` to initialize values on a scope.
27841 * <div class="alert alert-warning">
27842 * **Note**: If you have assignment in `ngInit` along with a {@link ng.$filter `filter`}, make
27843 * sure you have parentheses to ensure correct operator precedence:
27844 * <pre class="prettyprint">
27845 * `<div ng-init="test1 = ($index | toString)"></div>`
27852 * @param {expression} ngInit {@link guide/expression Expression} to eval.
27855 <example module="initExample" name="ng-init">
27856 <file name="index.html">
27858 angular.module('initExample', [])
27859 .controller('ExampleController', ['$scope', function($scope) {
27860 $scope.list = [['a', 'b'], ['c', 'd']];
27863 <div ng-controller="ExampleController">
27864 <div ng-repeat="innerList in list" ng-init="outerIndex = $index">
27865 <div ng-repeat="value in innerList" ng-init="innerIndex = $index">
27866 <span class="example-init">list[ {{outerIndex}} ][ {{innerIndex}} ] = {{value}};</span>
27871 <file name="protractor.js" type="protractor">
27872 it('should alias index positions', function() {
27873 var elements = element.all(by.css('.example-init'));
27874 expect(elements.get(0).getText()).toBe('list[ 0 ][ 0 ] = a;');
27875 expect(elements.get(1).getText()).toBe('list[ 0 ][ 1 ] = b;');
27876 expect(elements.get(2).getText()).toBe('list[ 1 ][ 0 ] = c;');
27877 expect(elements.get(3).getText()).toBe('list[ 1 ][ 1 ] = d;');
27882 var ngInitDirective = ngDirective({
27884 compile: function() {
27886 pre: function(scope, element, attrs) {
27887 scope.$eval(attrs.ngInit);
27898 * Text input that converts between a delimited string and an array of strings. The default
27899 * delimiter is a comma followed by a space - equivalent to `ng-list=", "`. You can specify a custom
27900 * delimiter as the value of the `ngList` attribute - for example, `ng-list=" | "`.
27902 * The behaviour of the directive is affected by the use of the `ngTrim` attribute.
27903 * * If `ngTrim` is set to `"false"` then whitespace around both the separator and each
27904 * list item is respected. This implies that the user of the directive is responsible for
27905 * dealing with whitespace but also allows you to use whitespace as a delimiter, such as a
27906 * tab or newline character.
27907 * * Otherwise whitespace around the delimiter is ignored when splitting (although it is respected
27908 * when joining the list items back together) and whitespace around each list item is stripped
27909 * before it is added to the model.
27911 * ### Example with Validation
27913 * <example name="ngList-directive" module="listExample">
27914 * <file name="app.js">
27915 * angular.module('listExample', [])
27916 * .controller('ExampleController', ['$scope', function($scope) {
27917 * $scope.names = ['morpheus', 'neo', 'trinity'];
27920 * <file name="index.html">
27921 * <form name="myForm" ng-controller="ExampleController">
27922 * <label>List: <input name="namesInput" ng-model="names" ng-list required></label>
27923 * <span role="alert">
27924 * <span class="error" ng-show="myForm.namesInput.$error.required">
27928 * <tt>names = {{names}}</tt><br/>
27929 * <tt>myForm.namesInput.$valid = {{myForm.namesInput.$valid}}</tt><br/>
27930 * <tt>myForm.namesInput.$error = {{myForm.namesInput.$error}}</tt><br/>
27931 * <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
27932 * <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
27935 * <file name="protractor.js" type="protractor">
27936 * var listInput = element(by.model('names'));
27937 * var names = element(by.exactBinding('names'));
27938 * var valid = element(by.binding('myForm.namesInput.$valid'));
27939 * var error = element(by.css('span.error'));
27941 * it('should initialize to model', function() {
27942 * expect(names.getText()).toContain('["morpheus","neo","trinity"]');
27943 * expect(valid.getText()).toContain('true');
27944 * expect(error.getCssValue('display')).toBe('none');
27947 * it('should be invalid if empty', function() {
27948 * listInput.clear();
27949 * listInput.sendKeys('');
27951 * expect(names.getText()).toContain('');
27952 * expect(valid.getText()).toContain('false');
27953 * expect(error.getCssValue('display')).not.toBe('none');
27958 * ### Example - splitting on newline
27959 * <example name="ngList-directive-newlines">
27960 * <file name="index.html">
27961 * <textarea ng-model="list" ng-list=" " ng-trim="false"></textarea>
27962 * <pre>{{ list | json }}</pre>
27964 * <file name="protractor.js" type="protractor">
27965 * it("should split the text by newlines", function() {
27966 * var listInput = element(by.model('list'));
27967 * var output = element(by.binding('list | json'));
27968 * listInput.sendKeys('abc\ndef\nghi');
27969 * expect(output.getText()).toContain('[\n "abc",\n "def",\n "ghi"\n]');
27975 * @param {string=} ngList optional delimiter that should be used to split the value.
27977 var ngListDirective = function() {
27981 require: 'ngModel',
27982 link: function(scope, element, attr, ctrl) {
27983 var ngList = attr.ngList || ', ';
27984 var trimValues = attr.ngTrim !== 'false';
27985 var separator = trimValues ? trim(ngList) : ngList;
27987 var parse = function(viewValue) {
27988 // If the viewValue is invalid (say required but empty) it will be `undefined`
27989 if (isUndefined(viewValue)) return;
27994 forEach(viewValue.split(separator), function(value) {
27995 if (value) list.push(trimValues ? trim(value) : value);
28002 ctrl.$parsers.push(parse);
28003 ctrl.$formatters.push(function(value) {
28004 if (isArray(value)) {
28005 return value.join(ngList);
28011 // Override the standard $isEmpty because an empty array means the input is empty.
28012 ctrl.$isEmpty = function(value) {
28013 return !value || !value.length;
28019 /* global VALID_CLASS: true,
28020 INVALID_CLASS: true,
28021 PRISTINE_CLASS: true,
28023 UNTOUCHED_CLASS: true,
28024 TOUCHED_CLASS: true,
28025 PENDING_CLASS: true,
28026 addSetValidityMethod: true,
28027 setupValidity: true,
28028 defaultModelOptions: false
28032 var VALID_CLASS = 'ng-valid',
28033 INVALID_CLASS = 'ng-invalid',
28034 PRISTINE_CLASS = 'ng-pristine',
28035 DIRTY_CLASS = 'ng-dirty',
28036 UNTOUCHED_CLASS = 'ng-untouched',
28037 TOUCHED_CLASS = 'ng-touched',
28038 EMPTY_CLASS = 'ng-empty',
28039 NOT_EMPTY_CLASS = 'ng-not-empty';
28041 var ngModelMinErr = minErr('ngModel');
28045 * @name ngModel.NgModelController
28047 * @property {*} $viewValue The actual value from the control's view. For `input` elements, this is a
28048 * String. See {@link ngModel.NgModelController#$setViewValue} for information about when the $viewValue
28051 * @property {*} $modelValue The value in the model that the control is bound to.
28053 * @property {Array.<Function>} $parsers Array of functions to execute, as a pipeline, whenever
28054 * the control updates the ngModelController with a new {@link ngModel.NgModelController#$viewValue
28055 `$viewValue`} from the DOM, usually via user input.
28056 See {@link ngModel.NgModelController#$setViewValue `$setViewValue()`} for a detailed lifecycle explanation.
28057 Note that the `$parsers` are not called when the bound ngModel expression changes programmatically.
28059 The functions are called in array order, each passing
28060 its return value through to the next. The last return value is forwarded to the
28061 {@link ngModel.NgModelController#$validators `$validators`} collection.
28063 Parsers are used to sanitize / convert the {@link ngModel.NgModelController#$viewValue
28066 Returning `undefined` from a parser means a parse error occurred. In that case,
28067 no {@link ngModel.NgModelController#$validators `$validators`} will run and the `ngModel`
28068 will be set to `undefined` unless {@link ngModelOptions `ngModelOptions.allowInvalid`}
28069 is set to `true`. The parse error is stored in `ngModel.$error.parse`.
28071 This simple example shows a parser that would convert text input value to lowercase:
28073 * function parse(value) {
28075 * return value.toLowerCase();
28078 * ngModelController.$parsers.push(parse);
28082 * @property {Array.<Function>} $formatters Array of functions to execute, as a pipeline, whenever
28083 the bound ngModel expression changes programmatically. The `$formatters` are not called when the
28084 value of the control is changed by user interaction.
28086 Formatters are used to format / convert the {@link ngModel.NgModelController#$modelValue
28087 `$modelValue`} for display in the control.
28089 The functions are called in reverse array order, each passing the value through to the
28090 next. The last return value is used as the actual DOM value.
28092 This simple example shows a formatter that would convert the model value to uppercase:
28095 * function format(value) {
28097 * return value.toUpperCase();
28100 * ngModel.$formatters.push(format);
28103 * @property {Object.<string, function>} $validators A collection of validators that are applied
28104 * whenever the model value changes. The key value within the object refers to the name of the
28105 * validator while the function refers to the validation operation. The validation operation is
28106 * provided with the model value as an argument and must return a true or false value depending
28107 * on the response of that validation.
28110 * ngModel.$validators.validCharacters = function(modelValue, viewValue) {
28111 * var value = modelValue || viewValue;
28112 * return /[0-9]+/.test(value) &&
28113 * /[a-z]+/.test(value) &&
28114 * /[A-Z]+/.test(value) &&
28115 * /\W+/.test(value);
28119 * @property {Object.<string, function>} $asyncValidators A collection of validations that are expected to
28120 * perform an asynchronous validation (e.g. a HTTP request). The validation function that is provided
28121 * is expected to return a promise when it is run during the model validation process. Once the promise
28122 * is delivered then the validation status will be set to true when fulfilled and false when rejected.
28123 * When the asynchronous validators are triggered, each of the validators will run in parallel and the model
28124 * value will only be updated once all validators have been fulfilled. As long as an asynchronous validator
28125 * is unfulfilled, its key will be added to the controllers `$pending` property. Also, all asynchronous validators
28126 * will only run once all synchronous validators have passed.
28128 * Please note that if $http is used then it is important that the server returns a success HTTP response code
28129 * in order to fulfill the validation and a status level of `4xx` in order to reject the validation.
28132 * ngModel.$asyncValidators.uniqueUsername = function(modelValue, viewValue) {
28133 * var value = modelValue || viewValue;
28135 * // Lookup user by username
28136 * return $http.get('/api/users/' + value).
28137 * then(function resolved() {
28138 * //username exists, this means validation fails
28139 * return $q.reject('exists');
28140 * }, function rejected() {
28141 * //username does not exist, therefore this validation passes
28147 * @property {Array.<Function>} $viewChangeListeners Array of functions to execute whenever the
28148 * view value has changed. It is called with no arguments, and its return value is ignored.
28149 * This can be used in place of additional $watches against the model value.
28151 * @property {Object} $error An object hash with all failing validator ids as keys.
28152 * @property {Object} $pending An object hash with all pending validator ids as keys.
28154 * @property {boolean} $untouched True if control has not lost focus yet.
28155 * @property {boolean} $touched True if control has lost focus.
28156 * @property {boolean} $pristine True if user has not interacted with the control yet.
28157 * @property {boolean} $dirty True if user has already interacted with the control.
28158 * @property {boolean} $valid True if there is no error.
28159 * @property {boolean} $invalid True if at least one error on the control.
28160 * @property {string} $name The name attribute of the control.
28164 * `NgModelController` provides API for the {@link ngModel `ngModel`} directive.
28165 * The controller contains services for data-binding, validation, CSS updates, and value formatting
28166 * and parsing. It purposefully does not contain any logic which deals with DOM rendering or
28167 * listening to DOM events.
28168 * Such DOM related logic should be provided by other directives which make use of
28169 * `NgModelController` for data-binding to control elements.
28170 * Angular provides this DOM logic for most {@link input `input`} elements.
28171 * At the end of this page you can find a {@link ngModel.NgModelController#custom-control-example
28172 * custom control example} that uses `ngModelController` to bind to `contenteditable` elements.
28175 * ### Custom Control Example
28176 * This example shows how to use `NgModelController` with a custom control to achieve
28177 * data-binding. Notice how different directives (`contenteditable`, `ng-model`, and `required`)
28178 * collaborate together to achieve the desired result.
28180 * `contenteditable` is an HTML5 attribute, which tells the browser to let the element
28181 * contents be edited in place by the user.
28183 * We are using the {@link ng.service:$sce $sce} service here and include the {@link ngSanitize $sanitize}
28184 * module to automatically remove "bad" content like inline event listener (e.g. `<span onclick="...">`).
28185 * However, as we are using `$sce` the model can still decide to provide unsafe content if it marks
28186 * that content using the `$sce` service.
28188 * <example name="NgModelController" module="customControl" deps="angular-sanitize.js">
28189 <file name="style.css">
28190 [contenteditable] {
28191 border: 1px solid black;
28192 background-color: white;
28197 border: 1px solid red;
28201 <file name="script.js">
28202 angular.module('customControl', ['ngSanitize']).
28203 directive('contenteditable', ['$sce', function($sce) {
28205 restrict: 'A', // only activate on element attribute
28206 require: '?ngModel', // get a hold of NgModelController
28207 link: function(scope, element, attrs, ngModel) {
28208 if (!ngModel) return; // do nothing if no ng-model
28210 // Specify how UI should be updated
28211 ngModel.$render = function() {
28212 element.html($sce.getTrustedHtml(ngModel.$viewValue || ''));
28215 // Listen for change events to enable binding
28216 element.on('blur keyup change', function() {
28217 scope.$evalAsync(read);
28219 read(); // initialize
28221 // Write data to the model
28223 var html = element.html();
28224 // When we clear the content editable the browser leaves a <br> behind
28225 // If strip-br attribute is provided then we strip this out
28226 if (attrs.stripBr && html === '<br>') {
28229 ngModel.$setViewValue(html);
28235 <file name="index.html">
28236 <form name="myForm">
28237 <div contenteditable
28238 name="myWidget" ng-model="userContent"
28240 required>Change me!</div>
28241 <span ng-show="myForm.myWidget.$error.required">Required!</span>
28243 <textarea ng-model="userContent" aria-label="Dynamic textarea"></textarea>
28246 <file name="protractor.js" type="protractor">
28247 it('should data-bind and become invalid', function() {
28248 if (browser.params.browser === 'safari' || browser.params.browser === 'firefox') {
28249 // SafariDriver can't handle contenteditable
28250 // and Firefox driver can't clear contenteditables very well
28253 var contentEditable = element(by.css('[contenteditable]'));
28254 var content = 'Change me!';
28256 expect(contentEditable.getText()).toEqual(content);
28258 contentEditable.clear();
28259 contentEditable.sendKeys(protractor.Key.BACK_SPACE);
28260 expect(contentEditable.getText()).toEqual('');
28261 expect(contentEditable.getAttribute('class')).toMatch(/ng-invalid-required/);
28268 NgModelController.$inject = ['$scope', '$exceptionHandler', '$attrs', '$element', '$parse', '$animate', '$timeout', '$q', '$interpolate'];
28269 function NgModelController($scope, $exceptionHandler, $attr, $element, $parse, $animate, $timeout, $q, $interpolate) {
28270 this.$viewValue = Number.NaN;
28271 this.$modelValue = Number.NaN;
28272 this.$$rawModelValue = undefined; // stores the parsed modelValue / model set from scope regardless of validity.
28273 this.$validators = {};
28274 this.$asyncValidators = {};
28275 this.$parsers = [];
28276 this.$formatters = [];
28277 this.$viewChangeListeners = [];
28278 this.$untouched = true;
28279 this.$touched = false;
28280 this.$pristine = true;
28281 this.$dirty = false;
28282 this.$valid = true;
28283 this.$invalid = false;
28284 this.$error = {}; // keep invalid keys here
28285 this.$$success = {}; // keep valid keys here
28286 this.$pending = undefined; // keep pending keys here
28287 this.$name = $interpolate($attr.name || '', false)($scope);
28288 this.$$parentForm = nullFormCtrl;
28289 this.$options = defaultModelOptions;
28291 this.$$parsedNgModel = $parse($attr.ngModel);
28292 this.$$parsedNgModelAssign = this.$$parsedNgModel.assign;
28293 this.$$ngModelGet = this.$$parsedNgModel;
28294 this.$$ngModelSet = this.$$parsedNgModelAssign;
28295 this.$$pendingDebounce = null;
28296 this.$$parserValid = undefined;
28298 this.$$currentValidationRunId = 0;
28300 this.$$scope = $scope;
28301 this.$$attr = $attr;
28302 this.$$element = $element;
28303 this.$$animate = $animate;
28304 this.$$timeout = $timeout;
28305 this.$$parse = $parse;
28307 this.$$exceptionHandler = $exceptionHandler;
28309 setupValidity(this);
28310 setupModelWatcher(this);
28313 NgModelController.prototype = {
28314 $$initGetterSetters: function() {
28315 if (this.$options.getOption('getterSetter')) {
28316 var invokeModelGetter = this.$$parse(this.$$attr.ngModel + '()'),
28317 invokeModelSetter = this.$$parse(this.$$attr.ngModel + '($$$p)');
28319 this.$$ngModelGet = function($scope) {
28320 var modelValue = this.$$parsedNgModel($scope);
28321 if (isFunction(modelValue)) {
28322 modelValue = invokeModelGetter($scope);
28326 this.$$ngModelSet = function($scope, newValue) {
28327 if (isFunction(this.$$parsedNgModel($scope))) {
28328 invokeModelSetter($scope, {$$$p: newValue});
28330 this.$$parsedNgModelAssign($scope, newValue);
28333 } else if (!this.$$parsedNgModel.assign) {
28334 throw ngModelMinErr('nonassign', 'Expression \'{0}\' is non-assignable. Element: {1}',
28335 this.$$attr.ngModel, startingTag(this.$$element));
28342 * @name ngModel.NgModelController#$render
28345 * Called when the view needs to be updated. It is expected that the user of the ng-model
28346 * directive will implement this method.
28348 * The `$render()` method is invoked in the following situations:
28350 * * `$rollbackViewValue()` is called. If we are rolling back the view value to the last
28351 * committed value then `$render()` is called to update the input control.
28352 * * The value referenced by `ng-model` is changed programmatically and both the `$modelValue` and
28353 * the `$viewValue` are different from last time.
28355 * Since `ng-model` does not do a deep watch, `$render()` is only invoked if the values of
28356 * `$modelValue` and `$viewValue` are actually different from their previous values. If `$modelValue`
28357 * or `$viewValue` are objects (rather than a string or number) then `$render()` will not be
28358 * invoked if you only change a property on the objects.
28364 * @name ngModel.NgModelController#$isEmpty
28367 * This is called when we need to determine if the value of an input is empty.
28369 * For instance, the required directive does this to work out if the input has data or not.
28371 * The default `$isEmpty` function checks whether the value is `undefined`, `''`, `null` or `NaN`.
28373 * You can override this for input directives whose concept of being empty is different from the
28374 * default. The `checkboxInputType` directive does this because in its case a value of `false`
28377 * @param {*} value The value of the input to check for emptiness.
28378 * @returns {boolean} True if `value` is "empty".
28380 $isEmpty: function(value) {
28381 // eslint-disable-next-line no-self-compare
28382 return isUndefined(value) || value === '' || value === null || value !== value;
28385 $$updateEmptyClasses: function(value) {
28386 if (this.$isEmpty(value)) {
28387 this.$$animate.removeClass(this.$$element, NOT_EMPTY_CLASS);
28388 this.$$animate.addClass(this.$$element, EMPTY_CLASS);
28390 this.$$animate.removeClass(this.$$element, EMPTY_CLASS);
28391 this.$$animate.addClass(this.$$element, NOT_EMPTY_CLASS);
28397 * @name ngModel.NgModelController#$setPristine
28400 * Sets the control to its pristine state.
28402 * This method can be called to remove the `ng-dirty` class and set the control to its pristine
28403 * state (`ng-pristine` class). A model is considered to be pristine when the control
28404 * has not been changed from when first compiled.
28406 $setPristine: function() {
28407 this.$dirty = false;
28408 this.$pristine = true;
28409 this.$$animate.removeClass(this.$$element, DIRTY_CLASS);
28410 this.$$animate.addClass(this.$$element, PRISTINE_CLASS);
28415 * @name ngModel.NgModelController#$setDirty
28418 * Sets the control to its dirty state.
28420 * This method can be called to remove the `ng-pristine` class and set the control to its dirty
28421 * state (`ng-dirty` class). A model is considered to be dirty when the control has been changed
28422 * from when first compiled.
28424 $setDirty: function() {
28425 this.$dirty = true;
28426 this.$pristine = false;
28427 this.$$animate.removeClass(this.$$element, PRISTINE_CLASS);
28428 this.$$animate.addClass(this.$$element, DIRTY_CLASS);
28429 this.$$parentForm.$setDirty();
28434 * @name ngModel.NgModelController#$setUntouched
28437 * Sets the control to its untouched state.
28439 * This method can be called to remove the `ng-touched` class and set the control to its
28440 * untouched state (`ng-untouched` class). Upon compilation, a model is set as untouched
28441 * by default, however this function can be used to restore that state if the model has
28442 * already been touched by the user.
28444 $setUntouched: function() {
28445 this.$touched = false;
28446 this.$untouched = true;
28447 this.$$animate.setClass(this.$$element, UNTOUCHED_CLASS, TOUCHED_CLASS);
28452 * @name ngModel.NgModelController#$setTouched
28455 * Sets the control to its touched state.
28457 * This method can be called to remove the `ng-untouched` class and set the control to its
28458 * touched state (`ng-touched` class). A model is considered to be touched when the user has
28459 * first focused the control element and then shifted focus away from the control (blur event).
28461 $setTouched: function() {
28462 this.$touched = true;
28463 this.$untouched = false;
28464 this.$$animate.setClass(this.$$element, TOUCHED_CLASS, UNTOUCHED_CLASS);
28469 * @name ngModel.NgModelController#$rollbackViewValue
28472 * Cancel an update and reset the input element's value to prevent an update to the `$modelValue`,
28473 * which may be caused by a pending debounced event or because the input is waiting for some
28476 * If you have an input that uses `ng-model-options` to set up debounced updates or updates that
28477 * depend on special events such as `blur`, there can be a period when the `$viewValue` is out of
28478 * sync with the ngModel's `$modelValue`.
28480 * In this case, you can use `$rollbackViewValue()` to manually cancel the debounced / future update
28481 * and reset the input to the last committed view value.
28483 * It is also possible that you run into difficulties if you try to update the ngModel's `$modelValue`
28484 * programmatically before these debounced/future events have resolved/occurred, because Angular's
28485 * dirty checking mechanism is not able to tell whether the model has actually changed or not.
28487 * The `$rollbackViewValue()` method should be called before programmatically changing the model of an
28488 * input which may have such events pending. This is important in order to make sure that the
28489 * input field will be updated with the new model value and any pending operations are cancelled.
28491 * <example name="ng-model-cancel-update" module="cancel-update-example">
28492 * <file name="app.js">
28493 * angular.module('cancel-update-example', [])
28495 * .controller('CancelUpdateController', ['$scope', function($scope) {
28496 * $scope.model = {value1: '', value2: ''};
28498 * $scope.setEmpty = function(e, value, rollback) {
28499 * if (e.keyCode === 27) {
28500 * e.preventDefault();
28502 * $scope.myForm[value].$rollbackViewValue();
28504 * $scope.model[value] = '';
28509 * <file name="index.html">
28510 * <div ng-controller="CancelUpdateController">
28511 * <p>Both of these inputs are only updated if they are blurred. Hitting escape should
28512 * empty them. Follow these steps and observe the difference:</p>
28514 * <li>Type something in the input. You will see that the model is not yet updated</li>
28515 * <li>Press the Escape key.
28517 * <li> In the first example, nothing happens, because the model is already '', and no
28518 * update is detected. If you blur the input, the model will be set to the current view.
28520 * <li> In the second example, the pending update is cancelled, and the input is set back
28521 * to the last committed view value (''). Blurring the input does nothing.
28527 * <form name="myForm" ng-model-options="{ updateOn: 'blur' }">
28529 * <p id="inputDescription1">Without $rollbackViewValue():</p>
28530 * <input name="value1" aria-describedby="inputDescription1" ng-model="model.value1"
28531 * ng-keydown="setEmpty($event, 'value1')">
28532 * value1: "{{ model.value1 }}"
28536 * <p id="inputDescription2">With $rollbackViewValue():</p>
28537 * <input name="value2" aria-describedby="inputDescription2" ng-model="model.value2"
28538 * ng-keydown="setEmpty($event, 'value2', true)">
28539 * value2: "{{ model.value2 }}"
28544 <file name="style.css">
28546 display: table-cell;
28549 padding-right: 30px;
28555 $rollbackViewValue: function() {
28556 this.$$timeout.cancel(this.$$pendingDebounce);
28557 this.$viewValue = this.$$lastCommittedViewValue;
28563 * @name ngModel.NgModelController#$validate
28566 * Runs each of the registered validators (first synchronous validators and then
28567 * asynchronous validators).
28568 * If the validity changes to invalid, the model will be set to `undefined`,
28569 * unless {@link ngModelOptions `ngModelOptions.allowInvalid`} is `true`.
28570 * If the validity changes to valid, it will set the model to the last available valid
28571 * `$modelValue`, i.e. either the last parsed value or the last value set from the scope.
28573 $validate: function() {
28574 // ignore $validate before model is initialized
28575 if (isNumberNaN(this.$modelValue)) {
28579 var viewValue = this.$$lastCommittedViewValue;
28580 // Note: we use the $$rawModelValue as $modelValue might have been
28581 // set to undefined during a view -> model update that found validation
28582 // errors. We can't parse the view here, since that could change
28583 // the model although neither viewValue nor the model on the scope changed
28584 var modelValue = this.$$rawModelValue;
28586 var prevValid = this.$valid;
28587 var prevModelValue = this.$modelValue;
28589 var allowInvalid = this.$options.getOption('allowInvalid');
28592 this.$$runValidators(modelValue, viewValue, function(allValid) {
28593 // If there was no change in validity, don't update the model
28594 // This prevents changing an invalid modelValue to undefined
28595 if (!allowInvalid && prevValid !== allValid) {
28596 // Note: Don't check this.$valid here, as we could have
28597 // external validators (e.g. calculated on the server),
28598 // that just call $setValidity and need the model value
28599 // to calculate their validity.
28600 that.$modelValue = allValid ? modelValue : undefined;
28602 if (that.$modelValue !== prevModelValue) {
28603 that.$$writeModelToScope();
28609 $$runValidators: function(modelValue, viewValue, doneCallback) {
28610 this.$$currentValidationRunId++;
28611 var localValidationRunId = this.$$currentValidationRunId;
28614 // check parser error
28615 if (!processParseErrors()) {
28616 validationDone(false);
28619 if (!processSyncValidators()) {
28620 validationDone(false);
28623 processAsyncValidators();
28625 function processParseErrors() {
28626 var errorKey = that.$$parserName || 'parse';
28627 if (isUndefined(that.$$parserValid)) {
28628 setValidity(errorKey, null);
28630 if (!that.$$parserValid) {
28631 forEach(that.$validators, function(v, name) {
28632 setValidity(name, null);
28634 forEach(that.$asyncValidators, function(v, name) {
28635 setValidity(name, null);
28638 // Set the parse error last, to prevent unsetting it, should a $validators key == parserName
28639 setValidity(errorKey, that.$$parserValid);
28640 return that.$$parserValid;
28645 function processSyncValidators() {
28646 var syncValidatorsValid = true;
28647 forEach(that.$validators, function(validator, name) {
28648 var result = Boolean(validator(modelValue, viewValue));
28649 syncValidatorsValid = syncValidatorsValid && result;
28650 setValidity(name, result);
28652 if (!syncValidatorsValid) {
28653 forEach(that.$asyncValidators, function(v, name) {
28654 setValidity(name, null);
28661 function processAsyncValidators() {
28662 var validatorPromises = [];
28663 var allValid = true;
28664 forEach(that.$asyncValidators, function(validator, name) {
28665 var promise = validator(modelValue, viewValue);
28666 if (!isPromiseLike(promise)) {
28667 throw ngModelMinErr('nopromise',
28668 'Expected asynchronous validator to return a promise but got \'{0}\' instead.', promise);
28670 setValidity(name, undefined);
28671 validatorPromises.push(promise.then(function() {
28672 setValidity(name, true);
28675 setValidity(name, false);
28678 if (!validatorPromises.length) {
28679 validationDone(true);
28681 that.$$q.all(validatorPromises).then(function() {
28682 validationDone(allValid);
28687 function setValidity(name, isValid) {
28688 if (localValidationRunId === that.$$currentValidationRunId) {
28689 that.$setValidity(name, isValid);
28693 function validationDone(allValid) {
28694 if (localValidationRunId === that.$$currentValidationRunId) {
28696 doneCallback(allValid);
28703 * @name ngModel.NgModelController#$commitViewValue
28706 * Commit a pending update to the `$modelValue`.
28708 * Updates may be pending by a debounced event or because the input is waiting for a some future
28709 * event defined in `ng-model-options`. this method is rarely needed as `NgModelController`
28710 * usually handles calling this in response to input events.
28712 $commitViewValue: function() {
28713 var viewValue = this.$viewValue;
28715 this.$$timeout.cancel(this.$$pendingDebounce);
28717 // If the view value has not changed then we should just exit, except in the case where there is
28718 // a native validator on the element. In this case the validation state may have changed even though
28719 // the viewValue has stayed empty.
28720 if (this.$$lastCommittedViewValue === viewValue && (viewValue !== '' || !this.$$hasNativeValidators)) {
28723 this.$$updateEmptyClasses(viewValue);
28724 this.$$lastCommittedViewValue = viewValue;
28727 if (this.$pristine) {
28730 this.$$parseAndValidate();
28733 $$parseAndValidate: function() {
28734 var viewValue = this.$$lastCommittedViewValue;
28735 var modelValue = viewValue;
28738 this.$$parserValid = isUndefined(modelValue) ? undefined : true;
28740 if (this.$$parserValid) {
28741 for (var i = 0; i < this.$parsers.length; i++) {
28742 modelValue = this.$parsers[i](modelValue);
28743 if (isUndefined(modelValue)) {
28744 this.$$parserValid = false;
28749 if (isNumberNaN(this.$modelValue)) {
28750 // this.$modelValue has not been touched yet...
28751 this.$modelValue = this.$$ngModelGet(this.$$scope);
28753 var prevModelValue = this.$modelValue;
28754 var allowInvalid = this.$options.getOption('allowInvalid');
28755 this.$$rawModelValue = modelValue;
28757 if (allowInvalid) {
28758 this.$modelValue = modelValue;
28759 writeToModelIfNeeded();
28762 // Pass the $$lastCommittedViewValue here, because the cached viewValue might be out of date.
28763 // This can happen if e.g. $setViewValue is called from inside a parser
28764 this.$$runValidators(modelValue, this.$$lastCommittedViewValue, function(allValid) {
28765 if (!allowInvalid) {
28766 // Note: Don't check this.$valid here, as we could have
28767 // external validators (e.g. calculated on the server),
28768 // that just call $setValidity and need the model value
28769 // to calculate their validity.
28770 that.$modelValue = allValid ? modelValue : undefined;
28771 writeToModelIfNeeded();
28775 function writeToModelIfNeeded() {
28776 if (that.$modelValue !== prevModelValue) {
28777 that.$$writeModelToScope();
28782 $$writeModelToScope: function() {
28783 this.$$ngModelSet(this.$$scope, this.$modelValue);
28784 forEach(this.$viewChangeListeners, function(listener) {
28788 // eslint-disable-next-line no-invalid-this
28789 this.$$exceptionHandler(e);
28796 * @name ngModel.NgModelController#$setViewValue
28799 * Update the view value.
28801 * This method should be called when a control wants to change the view value; typically,
28802 * this is done from within a DOM event handler. For example, the {@link ng.directive:input input}
28803 * directive calls it when the value of the input changes and {@link ng.directive:select select}
28804 * calls it when an option is selected.
28806 * When `$setViewValue` is called, the new `value` will be staged for committing through the `$parsers`
28807 * and `$validators` pipelines. If there are no special {@link ngModelOptions} specified then the staged
28808 * value is sent directly for processing through the `$parsers` pipeline. After this, the `$validators` and
28809 * `$asyncValidators` are called and the value is applied to `$modelValue`.
28810 * Finally, the value is set to the **expression** specified in the `ng-model` attribute and
28811 * all the registered change listeners, in the `$viewChangeListeners` list are called.
28813 * In case the {@link ng.directive:ngModelOptions ngModelOptions} directive is used with `updateOn`
28814 * and the `default` trigger is not listed, all those actions will remain pending until one of the
28815 * `updateOn` events is triggered on the DOM element.
28816 * All these actions will be debounced if the {@link ng.directive:ngModelOptions ngModelOptions}
28817 * directive is used with a custom debounce for this particular event.
28818 * Note that a `$digest` is only triggered once the `updateOn` events are fired, or if `debounce`
28819 * is specified, once the timer runs out.
28821 * When used with standard inputs, the view value will always be a string (which is in some cases
28822 * parsed into another type, such as a `Date` object for `input[date]`.)
28823 * However, custom controls might also pass objects to this method. In this case, we should make
28824 * a copy of the object before passing it to `$setViewValue`. This is because `ngModel` does not
28825 * perform a deep watch of objects, it only looks for a change of identity. If you only change
28826 * the property of the object then ngModel will not realize that the object has changed and
28827 * will not invoke the `$parsers` and `$validators` pipelines. For this reason, you should
28828 * not change properties of the copy once it has been passed to `$setViewValue`.
28829 * Otherwise you may cause the model value on the scope to change incorrectly.
28831 * <div class="alert alert-info">
28832 * In any case, the value passed to the method should always reflect the current value
28833 * of the control. For example, if you are calling `$setViewValue` for an input element,
28834 * you should pass the input DOM value. Otherwise, the control and the scope model become
28835 * out of sync. It's also important to note that `$setViewValue` does not call `$render` or change
28836 * the control's DOM value in any way. If we want to change the control's DOM value
28837 * programmatically, we should update the `ngModel` scope expression. Its new value will be
28838 * picked up by the model controller, which will run it through the `$formatters`, `$render` it
28839 * to update the DOM, and finally call `$validate` on it.
28842 * @param {*} value value from the view.
28843 * @param {string} trigger Event that triggered the update.
28845 $setViewValue: function(value, trigger) {
28846 this.$viewValue = value;
28847 if (this.$options.getOption('updateOnDefault')) {
28848 this.$$debounceViewValueCommit(trigger);
28852 $$debounceViewValueCommit: function(trigger) {
28853 var debounceDelay = this.$options.getOption('debounce');
28855 if (isNumber(debounceDelay[trigger])) {
28856 debounceDelay = debounceDelay[trigger];
28857 } else if (isNumber(debounceDelay['default'])) {
28858 debounceDelay = debounceDelay['default'];
28861 this.$$timeout.cancel(this.$$pendingDebounce);
28863 if (debounceDelay > 0) { // this fails if debounceDelay is an object
28864 this.$$pendingDebounce = this.$$timeout(function() {
28865 that.$commitViewValue();
28867 } else if (this.$$scope.$root.$$phase) {
28868 this.$commitViewValue();
28870 this.$$scope.$apply(function() {
28871 that.$commitViewValue();
28879 * @name ngModel.NgModelController#$overrideModelOptions
28883 * Override the current model options settings programmatically.
28885 * The previous `ModelOptions` value will not be modified. Instead, a
28886 * new `ModelOptions` object will inherit from the previous one overriding
28887 * or inheriting settings that are defined in the given parameter.
28889 * See {@link ngModelOptions} for information about what options can be specified
28890 * and how model option inheritance works.
28892 * @param {Object} options a hash of settings to override the previous options
28895 $overrideModelOptions: function(options) {
28896 this.$options = this.$options.createChild(options);
28900 function setupModelWatcher(ctrl) {
28902 // Note: we cannot use a normal scope.$watch as we want to detect the following:
28903 // 1. scope value is 'a'
28904 // 2. user enters 'b'
28905 // 3. ng-change kicks in and reverts scope value to 'a'
28906 // -> scope value did not change since the last digest as
28907 // ng-change executes in apply phase
28908 // 4. view should be changed back to 'a'
28909 ctrl.$$scope.$watch(function ngModelWatch() {
28910 var modelValue = ctrl.$$ngModelGet(ctrl.$$scope);
28912 // if scope model value and ngModel value are out of sync
28913 // TODO(perf): why not move this to the action fn?
28914 if (modelValue !== ctrl.$modelValue &&
28915 // checks for NaN is needed to allow setting the model to NaN when there's an asyncValidator
28916 // eslint-disable-next-line no-self-compare
28917 (ctrl.$modelValue === ctrl.$modelValue || modelValue === modelValue)
28919 ctrl.$modelValue = ctrl.$$rawModelValue = modelValue;
28920 ctrl.$$parserValid = undefined;
28922 var formatters = ctrl.$formatters,
28923 idx = formatters.length;
28925 var viewValue = modelValue;
28927 viewValue = formatters[idx](viewValue);
28929 if (ctrl.$viewValue !== viewValue) {
28930 ctrl.$$updateEmptyClasses(viewValue);
28931 ctrl.$viewValue = ctrl.$$lastCommittedViewValue = viewValue;
28934 // It is possible that model and view value have been updated during render
28935 ctrl.$$runValidators(ctrl.$modelValue, ctrl.$viewValue, noop);
28945 * @name ngModel.NgModelController#$setValidity
28948 * Change the validity state, and notify the form.
28950 * This method can be called within $parsers/$formatters or a custom validation implementation.
28951 * However, in most cases it should be sufficient to use the `ngModel.$validators` and
28952 * `ngModel.$asyncValidators` collections which will call `$setValidity` automatically.
28954 * @param {string} validationErrorKey Name of the validator. The `validationErrorKey` will be assigned
28955 * to either `$error[validationErrorKey]` or `$pending[validationErrorKey]`
28956 * (for unfulfilled `$asyncValidators`), so that it is available for data-binding.
28957 * The `validationErrorKey` should be in camelCase and will get converted into dash-case
28958 * for class name. Example: `myError` will result in `ng-valid-my-error` and `ng-invalid-my-error`
28959 * class and can be bound to as `{{someForm.someControl.$error.myError}}` .
28960 * @param {boolean} isValid Whether the current state is valid (true), invalid (false), pending (undefined),
28961 * or skipped (null). Pending is used for unfulfilled `$asyncValidators`.
28962 * Skipped is used by Angular when validators do not run because of parse errors and
28963 * when `$asyncValidators` do not run because any of the `$validators` failed.
28965 addSetValidityMethod({
28966 clazz: NgModelController,
28967 set: function(object, property) {
28968 object[property] = true;
28970 unset: function(object, property) {
28971 delete object[property];
28984 * The `ngModel` directive binds an `input`,`select`, `textarea` (or custom form control) to a
28985 * property on the scope using {@link ngModel.NgModelController NgModelController},
28986 * which is created and exposed by this directive.
28988 * `ngModel` is responsible for:
28990 * - Binding the view into the model, which other directives such as `input`, `textarea` or `select`
28992 * - Providing validation behavior (i.e. required, number, email, url).
28993 * - Keeping the state of the control (valid/invalid, dirty/pristine, touched/untouched, validation errors).
28994 * - Setting related css classes on the element (`ng-valid`, `ng-invalid`, `ng-dirty`, `ng-pristine`, `ng-touched`,
28995 * `ng-untouched`, `ng-empty`, `ng-not-empty`) including animations.
28996 * - Registering the control with its parent {@link ng.directive:form form}.
28998 * Note: `ngModel` will try to bind to the property given by evaluating the expression on the
28999 * current scope. If the property doesn't already exist on this scope, it will be created
29000 * implicitly and added to the scope.
29002 * For best practices on using `ngModel`, see:
29004 * - [Understanding Scopes](https://github.com/angular/angular.js/wiki/Understanding-Scopes)
29006 * For basic examples, how to use `ngModel`, see:
29008 * - {@link ng.directive:input input}
29009 * - {@link input[text] text}
29010 * - {@link input[checkbox] checkbox}
29011 * - {@link input[radio] radio}
29012 * - {@link input[number] number}
29013 * - {@link input[email] email}
29014 * - {@link input[url] url}
29015 * - {@link input[date] date}
29016 * - {@link input[datetime-local] datetime-local}
29017 * - {@link input[time] time}
29018 * - {@link input[month] month}
29019 * - {@link input[week] week}
29020 * - {@link ng.directive:select select}
29021 * - {@link ng.directive:textarea textarea}
29023 * # Complex Models (objects or collections)
29025 * By default, `ngModel` watches the model by reference, not value. This is important to know when
29026 * binding inputs to models that are objects (e.g. `Date`) or collections (e.g. arrays). If only properties of the
29027 * object or collection change, `ngModel` will not be notified and so the input will not be re-rendered.
29029 * The model must be assigned an entirely new object or collection before a re-rendering will occur.
29031 * Some directives have options that will cause them to use a custom `$watchCollection` on the model expression
29032 * - for example, `ngOptions` will do so when a `track by` clause is included in the comprehension expression or
29033 * if the select is given the `multiple` attribute.
29035 * The `$watchCollection()` method only does a shallow comparison, meaning that changing properties deeper than the
29036 * first level of the object (or only changing the properties of an item in the collection if it's an array) will still
29037 * not trigger a re-rendering of the model.
29040 * The following CSS classes are added and removed on the associated input/select/textarea element
29041 * depending on the validity of the model.
29043 * - `ng-valid`: the model is valid
29044 * - `ng-invalid`: the model is invalid
29045 * - `ng-valid-[key]`: for each valid key added by `$setValidity`
29046 * - `ng-invalid-[key]`: for each invalid key added by `$setValidity`
29047 * - `ng-pristine`: the control hasn't been interacted with yet
29048 * - `ng-dirty`: the control has been interacted with
29049 * - `ng-touched`: the control has been blurred
29050 * - `ng-untouched`: the control hasn't been blurred
29051 * - `ng-pending`: any `$asyncValidators` are unfulfilled
29052 * - `ng-empty`: the view does not contain a value or the value is deemed "empty", as defined
29053 * by the {@link ngModel.NgModelController#$isEmpty} method
29054 * - `ng-not-empty`: the view contains a non-empty value
29056 * Keep in mind that ngAnimate can detect each of these classes when added and removed.
29058 * ## Animation Hooks
29060 * Animations within models are triggered when any of the associated CSS classes are added and removed
29061 * on the input element which is attached to the model. These classes include: `.ng-pristine`, `.ng-dirty`,
29062 * `.ng-invalid` and `.ng-valid` as well as any other validations that are performed on the model itself.
29063 * The animations that are triggered within ngModel are similar to how they work in ngClass and
29064 * animations can be hooked into using CSS transitions, keyframes as well as JS animations.
29066 * The following example shows a simple way to utilize CSS transitions to style an input element
29067 * that has been rendered as invalid after it has been validated:
29070 * //be sure to include ngAnimate as a module to hook into more
29071 * //advanced animations
29073 * transition:0.5s linear all;
29074 * background: white;
29076 * .my-input.ng-invalid {
29083 * <example deps="angular-animate.js" animations="true" fixBase="true" module="inputExample" name="ng-model">
29084 <file name="index.html">
29086 angular.module('inputExample', [])
29087 .controller('ExampleController', ['$scope', function($scope) {
29093 transition:all linear 0.5s;
29094 background: transparent;
29096 .my-input.ng-invalid {
29101 <p id="inputDescription">
29102 Update input to see transitions when valid/invalid.
29103 Integer is a valid value.
29105 <form name="testForm" ng-controller="ExampleController">
29106 <input ng-model="val" ng-pattern="/^\d+$/" name="anim" class="my-input"
29107 aria-describedby="inputDescription" />
29112 * ## Binding to a getter/setter
29114 * Sometimes it's helpful to bind `ngModel` to a getter/setter function. A getter/setter is a
29115 * function that returns a representation of the model when called with zero arguments, and sets
29116 * the internal state of a model when called with an argument. It's sometimes useful to use this
29117 * for models that have an internal representation that's different from what the model exposes
29120 * <div class="alert alert-success">
29121 * **Best Practice:** It's best to keep getters fast because Angular is likely to call them more
29122 * frequently than other parts of your code.
29125 * You use this behavior by adding `ng-model-options="{ getterSetter: true }"` to an element that
29126 * has `ng-model` attached to it. You can also add `ng-model-options="{ getterSetter: true }"` to
29127 * a `<form>`, which will enable this behavior for all `<input>`s within it. See
29128 * {@link ng.directive:ngModelOptions `ngModelOptions`} for more.
29130 * The following example shows how to use `ngModel` with a getter/setter:
29133 * <example name="ngModel-getter-setter" module="getterSetterExample">
29134 <file name="index.html">
29135 <div ng-controller="ExampleController">
29136 <form name="userForm">
29138 <input type="text" name="userName"
29139 ng-model="user.name"
29140 ng-model-options="{ getterSetter: true }" />
29143 <pre>user.name = <span ng-bind="user.name()"></span></pre>
29146 <file name="app.js">
29147 angular.module('getterSetterExample', [])
29148 .controller('ExampleController', ['$scope', function($scope) {
29149 var _name = 'Brian';
29151 name: function(newName) {
29152 // Note that newName can be undefined for two reasons:
29153 // 1. Because it is called as a getter and thus called with no arguments
29154 // 2. Because the property should actually be set to undefined. This happens e.g. if the
29155 // input is invalid
29156 return arguments.length ? (_name = newName) : _name;
29163 var ngModelDirective = ['$rootScope', function($rootScope) {
29166 require: ['ngModel', '^?form', '^?ngModelOptions'],
29167 controller: NgModelController,
29168 // Prelink needs to run before any input directive
29169 // so that we can set the NgModelOptions in NgModelController
29170 // before anyone else uses it.
29172 compile: function ngModelCompile(element) {
29173 // Setup initial state of the control
29174 element.addClass(PRISTINE_CLASS).addClass(UNTOUCHED_CLASS).addClass(VALID_CLASS);
29177 pre: function ngModelPreLink(scope, element, attr, ctrls) {
29178 var modelCtrl = ctrls[0],
29179 formCtrl = ctrls[1] || modelCtrl.$$parentForm,
29180 optionsCtrl = ctrls[2];
29183 modelCtrl.$options = optionsCtrl.$options;
29186 modelCtrl.$$initGetterSetters();
29188 // notify others, especially parent forms
29189 formCtrl.$addControl(modelCtrl);
29191 attr.$observe('name', function(newValue) {
29192 if (modelCtrl.$name !== newValue) {
29193 modelCtrl.$$parentForm.$$renameControl(modelCtrl, newValue);
29197 scope.$on('$destroy', function() {
29198 modelCtrl.$$parentForm.$removeControl(modelCtrl);
29201 post: function ngModelPostLink(scope, element, attr, ctrls) {
29202 var modelCtrl = ctrls[0];
29203 if (modelCtrl.$options.getOption('updateOn')) {
29204 element.on(modelCtrl.$options.getOption('updateOn'), function(ev) {
29205 modelCtrl.$$debounceViewValueCommit(ev && ev.type);
29209 function setTouched() {
29210 modelCtrl.$setTouched();
29213 element.on('blur', function() {
29214 if (modelCtrl.$touched) return;
29216 if ($rootScope.$$phase) {
29217 scope.$evalAsync(setTouched);
29219 scope.$apply(setTouched);
29228 /* exported defaultModelOptions */
29229 var defaultModelOptions;
29230 var DEFAULT_REGEXP = /(\s+|^)default(\s+|$)/;
29234 * @name ModelOptions
29236 * A container for the options set by the {@link ngModelOptions} directive
29238 function ModelOptions(options) {
29239 this.$$options = options;
29242 ModelOptions.prototype = {
29246 * @name ModelOptions#getOption
29247 * @param {string} name the name of the option to retrieve
29248 * @returns {*} the value of the option
29250 * Returns the value of the given option
29252 getOption: function(name) {
29253 return this.$$options[name];
29258 * @name ModelOptions#createChild
29259 * @param {Object} options a hash of options for the new child that will override the parent's options
29260 * @return {ModelOptions} a new `ModelOptions` object initialized with the given options.
29262 createChild: function(options) {
29263 var inheritAll = false;
29265 // make a shallow copy
29266 options = extend({}, options);
29268 // Inherit options from the parent if specified by the value `"$inherit"`
29269 forEach(options, /* @this */ function(option, key) {
29270 if (option === '$inherit') {
29274 options[key] = this.$$options[key];
29275 // `updateOn` is special so we must also inherit the `updateOnDefault` option
29276 if (key === 'updateOn') {
29277 options.updateOnDefault = this.$$options.updateOnDefault;
29281 if (key === 'updateOn') {
29282 // If the `updateOn` property contains the `default` event then we have to remove
29283 // it from the event list and set the `updateOnDefault` flag.
29284 options.updateOnDefault = false;
29285 options[key] = trim(option.replace(DEFAULT_REGEXP, function() {
29286 options.updateOnDefault = true;
29294 // We have a property of the form: `"*": "$inherit"`
29295 delete options['*'];
29296 defaults(options, this.$$options);
29299 // Finally add in any missing defaults
29300 defaults(options, defaultModelOptions.$$options);
29302 return new ModelOptions(options);
29307 defaultModelOptions = new ModelOptions({
29309 updateOnDefault: true,
29311 getterSetter: false,
29312 allowInvalid: false,
29319 * @name ngModelOptions
29322 * This directive allows you to modify the behaviour of {@link ngModel} directives within your
29323 * application. You can specify an `ngModelOptions` directive on any element. All {@link ngModel}
29324 * directives will use the options of their nearest `ngModelOptions` ancestor.
29326 * The `ngModelOptions` settings are found by evaluating the value of the attribute directive as
29327 * an Angular expression. This expression should evaluate to an object, whose properties contain
29328 * the settings. For example: `<div "ng-model-options"="{ debounce: 100 }"`.
29330 * ## Inheriting Options
29332 * You can specify that an `ngModelOptions` setting should be inherited from a parent `ngModelOptions`
29333 * directive by giving it the value of `"$inherit"`.
29334 * Then it will inherit that setting from the first `ngModelOptions` directive found by traversing up the
29335 * DOM tree. If there is no ancestor element containing an `ngModelOptions` directive then default settings
29338 * For example given the following fragment of HTML
29342 * <div ng-model-options="{ allowInvalid: true, debounce: 200 }">
29343 * <form ng-model-options="{ updateOn: 'blur', allowInvalid: '$inherit' }">
29344 * <input ng-model-options="{ updateOn: 'default', allowInvalid: '$inherit' }" />
29349 * the `input` element will have the following settings
29352 * { allowInvalid: true, updateOn: 'default', debounce: 0 }
29355 * Notice that the `debounce` setting was not inherited and used the default value instead.
29357 * You can specify that all undefined settings are automatically inherited from an ancestor by
29358 * including a property with key of `"*"` and value of `"$inherit"`.
29360 * For example given the following fragment of HTML
29364 * <div ng-model-options="{ allowInvalid: true, debounce: 200 }">
29365 * <form ng-model-options="{ updateOn: 'blur', "*": '$inherit' }">
29366 * <input ng-model-options="{ updateOn: 'default', "*": '$inherit' }" />
29371 * the `input` element will have the following settings
29374 * { allowInvalid: true, updateOn: 'default', debounce: 200 }
29377 * Notice that the `debounce` setting now inherits the value from the outer `<div>` element.
29379 * If you are creating a reusable component then you should be careful when using `"*": "$inherit"`
29380 * since you may inadvertently inherit a setting in the future that changes the behavior of your component.
29383 * ## Triggering and debouncing model updates
29385 * The `updateOn` and `debounce` properties allow you to specify a custom list of events that will
29386 * trigger a model update and/or a debouncing delay so that the actual update only takes place when
29387 * a timer expires; this timer will be reset after another change takes place.
29389 * Given the nature of `ngModelOptions`, the value displayed inside input fields in the view might
29390 * be different from the value in the actual model. This means that if you update the model you
29391 * should also invoke {@link ngModel.NgModelController#$rollbackViewValue} on the relevant input field in
29392 * order to make sure it is synchronized with the model and that any debounced action is canceled.
29394 * The easiest way to reference the control's {@link ngModel.NgModelController#$rollbackViewValue}
29395 * method is by making sure the input is placed inside a form that has a `name` attribute. This is
29396 * important because `form` controllers are published to the related scope under the name in their
29397 * `name` attribute.
29399 * Any pending changes will take place immediately when an enclosing form is submitted via the
29400 * `submit` event. Note that `ngClick` events will occur before the model is updated. Use `ngSubmit`
29401 * to have access to the updated model.
29403 * The following example shows how to override immediate updates. Changes on the inputs within the
29404 * form will update the model only when the control loses focus (blur event). If `escape` key is
29405 * pressed while the input field is focused, the value is reset to the value in the current model.
29407 * <example name="ngModelOptions-directive-blur" module="optionsExample">
29408 * <file name="index.html">
29409 * <div ng-controller="ExampleController">
29410 * <form name="userForm">
29413 * <input type="text" name="userName"
29414 * ng-model="user.name"
29415 * ng-model-options="{ updateOn: 'blur' }"
29416 * ng-keyup="cancel($event)" />
29420 * <input type="text" ng-model="user.data" />
29423 * <pre>user.name = <span ng-bind="user.name"></span></pre>
29426 * <file name="app.js">
29427 * angular.module('optionsExample', [])
29428 * .controller('ExampleController', ['$scope', function($scope) {
29429 * $scope.user = { name: 'say', data: '' };
29431 * $scope.cancel = function(e) {
29432 * if (e.keyCode === 27) {
29433 * $scope.userForm.userName.$rollbackViewValue();
29438 * <file name="protractor.js" type="protractor">
29439 * var model = element(by.binding('user.name'));
29440 * var input = element(by.model('user.name'));
29441 * var other = element(by.model('user.data'));
29443 * it('should allow custom events', function() {
29444 * input.sendKeys(' hello');
29446 * expect(model.getText()).toEqual('say');
29448 * expect(model.getText()).toEqual('say hello');
29451 * it('should $rollbackViewValue when model changes', function() {
29452 * input.sendKeys(' hello');
29453 * expect(input.getAttribute('value')).toEqual('say hello');
29454 * input.sendKeys(protractor.Key.ESCAPE);
29455 * expect(input.getAttribute('value')).toEqual('say');
29457 * expect(model.getText()).toEqual('say');
29462 * The next example shows how to debounce model changes. Model will be updated only 1 sec after last change.
29463 * If the `Clear` button is pressed, any debounced action is canceled and the value becomes empty.
29465 * <example name="ngModelOptions-directive-debounce" module="optionsExample">
29466 * <file name="index.html">
29467 * <div ng-controller="ExampleController">
29468 * <form name="userForm">
29470 * <input type="text" name="userName"
29471 * ng-model="user.name"
29472 * ng-model-options="{ debounce: 1000 }" />
29473 * <button ng-click="userForm.userName.$rollbackViewValue(); user.name=''">Clear</button><br />
29475 * <pre>user.name = <span ng-bind="user.name"></span></pre>
29478 * <file name="app.js">
29479 * angular.module('optionsExample', [])
29480 * .controller('ExampleController', ['$scope', function($scope) {
29481 * $scope.user = { name: 'say' };
29486 * ## Model updates and validation
29488 * The default behaviour in `ngModel` is that the model value is set to `undefined` when the
29489 * validation determines that the value is invalid. By setting the `allowInvalid` property to true,
29490 * the model will still be updated even if the value is invalid.
29493 * ## Connecting to the scope
29495 * By setting the `getterSetter` property to true you are telling ngModel that the `ngModel` expression
29496 * on the scope refers to a "getter/setter" function rather than the value itself.
29498 * The following example shows how to bind to getter/setters:
29500 * <example name="ngModelOptions-directive-getter-setter" module="getterSetterExample">
29501 * <file name="index.html">
29502 * <div ng-controller="ExampleController">
29503 * <form name="userForm">
29506 * <input type="text" name="userName"
29507 * ng-model="user.name"
29508 * ng-model-options="{ getterSetter: true }" />
29511 * <pre>user.name = <span ng-bind="user.name()"></span></pre>
29514 * <file name="app.js">
29515 * angular.module('getterSetterExample', [])
29516 * .controller('ExampleController', ['$scope', function($scope) {
29517 * var _name = 'Brian';
29519 * name: function(newName) {
29520 * return angular.isDefined(newName) ? (_name = newName) : _name;
29528 * ## Specifying timezones
29530 * You can specify the timezone that date/time input directives expect by providing its name in the
29531 * `timezone` property.
29533 * @param {Object} ngModelOptions options to apply to {@link ngModel} directives on this element and
29534 * and its descendents. Valid keys are:
29535 * - `updateOn`: string specifying which event should the input be bound to. You can set several
29536 * events using an space delimited list. There is a special event called `default` that
29537 * matches the default events belonging to the control.
29538 * - `debounce`: integer value which contains the debounce model update value in milliseconds. A
29539 * value of 0 triggers an immediate update. If an object is supplied instead, you can specify a
29540 * custom value for each event. For example:
29542 * ng-model-options="{
29543 * updateOn: 'default blur',
29544 * debounce: { 'default': 500, 'blur': 0 }
29547 * - `allowInvalid`: boolean value which indicates that the model can be set with values that did
29548 * not validate correctly instead of the default behavior of setting the model to undefined.
29549 * - `getterSetter`: boolean value which determines whether or not to treat functions bound to
29550 * `ngModel` as getters/setters.
29551 * - `timezone`: Defines the timezone to be used to read/write the `Date` instance in the model for
29552 * `<input type="date" />`, `<input type="time" />`, ... . It understands UTC/GMT and the
29553 * continental US time zone abbreviations, but for general use, use a time zone offset, for
29554 * example, `'+0430'` (4 hours, 30 minutes east of the Greenwich meridian)
29555 * If not specified, the timezone of the browser will be used.
29558 var ngModelOptionsDirective = function() {
29559 NgModelOptionsController.$inject = ['$attrs', '$scope'];
29560 function NgModelOptionsController($attrs, $scope) {
29561 this.$$attrs = $attrs;
29562 this.$$scope = $scope;
29564 NgModelOptionsController.prototype = {
29565 $onInit: function() {
29566 var parentOptions = this.parentCtrl ? this.parentCtrl.$options : defaultModelOptions;
29567 var modelOptionsDefinition = this.$$scope.$eval(this.$$attrs.ngModelOptions);
29569 this.$options = parentOptions.createChild(modelOptionsDefinition);
29575 // ngModelOptions needs to run before ngModel and input directives
29577 require: {parentCtrl: '?^^ngModelOptions'},
29578 bindToController: true,
29579 controller: NgModelOptionsController
29584 // shallow copy over values from `src` that are not already specified on `dst`
29585 function defaults(dst, src) {
29586 forEach(src, function(value, key) {
29587 if (!isDefined(dst[key])) {
29595 * @name ngNonBindable
29600 * The `ngNonBindable` directive tells Angular not to compile or bind the contents of the current
29601 * DOM element. This is useful if the element contains what appears to be Angular directives and
29602 * bindings but which should be ignored by Angular. This could be the case if you have a site that
29603 * displays snippets of code, for instance.
29608 * In this example there are two locations where a simple interpolation binding (`{{}}`) is present,
29609 * but the one wrapped in `ngNonBindable` is left alone.
29612 <example name="ng-non-bindable">
29613 <file name="index.html">
29614 <div>Normal: {{1 + 2}}</div>
29615 <div ng-non-bindable>Ignored: {{1 + 2}}</div>
29617 <file name="protractor.js" type="protractor">
29618 it('should check ng-non-bindable', function() {
29619 expect(element(by.binding('1 + 2')).getText()).toContain('3');
29620 expect(element.all(by.css('div')).last().getText()).toMatch(/1 \+ 2/);
29625 var ngNonBindableDirective = ngDirective({ terminal: true, priority: 1000 });
29627 /* exported ngOptionsDirective */
29629 /* global jqLiteRemove */
29631 var ngOptionsMinErr = minErr('ngOptions');
29640 * The `ngOptions` attribute can be used to dynamically generate a list of `<option>`
29641 * elements for the `<select>` element using the array or object obtained by evaluating the
29642 * `ngOptions` comprehension expression.
29644 * In many cases, {@link ng.directive:ngRepeat ngRepeat} can be used on `<option>` elements instead of
29645 * `ngOptions` to achieve a similar result. However, `ngOptions` provides some benefits:
29646 * - more flexibility in how the `<select>`'s model is assigned via the `select` **`as`** part of the
29647 * comprehension expression
29648 * - reduced memory consumption by not creating a new scope for each repeated instance
29649 * - increased render speed by creating the options in a documentFragment instead of individually
29651 * When an item in the `<select>` menu is selected, the array element or object property
29652 * represented by the selected option will be bound to the model identified by the `ngModel`
29655 * Optionally, a single hard-coded `<option>` element, with the value set to an empty string, can
29656 * be nested into the `<select>` element. This element will then represent the `null` or "not selected"
29657 * option. See example below for demonstration.
29659 * ## Complex Models (objects or collections)
29661 * By default, `ngModel` watches the model by reference, not value. This is important to know when
29662 * binding the select to a model that is an object or a collection.
29664 * One issue occurs if you want to preselect an option. For example, if you set
29665 * the model to an object that is equal to an object in your collection, `ngOptions` won't be able to set the selection,
29666 * because the objects are not identical. So by default, you should always reference the item in your collection
29667 * for preselections, e.g.: `$scope.selected = $scope.collection[3]`.
29669 * Another solution is to use a `track by` clause, because then `ngOptions` will track the identity
29670 * of the item not by reference, but by the result of the `track by` expression. For example, if your
29671 * collection items have an id property, you would `track by item.id`.
29673 * A different issue with objects or collections is that ngModel won't detect if an object property or
29674 * a collection item changes. For that reason, `ngOptions` additionally watches the model using
29675 * `$watchCollection`, when the expression contains a `track by` clause or the the select has the `multiple` attribute.
29676 * This allows ngOptions to trigger a re-rendering of the options even if the actual object/collection
29677 * has not changed identity, but only a property on the object or an item in the collection changes.
29679 * Note that `$watchCollection` does a shallow comparison of the properties of the object (or the items in the collection
29680 * if the model is an array). This means that changing a property deeper than the first level inside the
29681 * object/collection will not trigger a re-rendering.
29683 * ## `select` **`as`**
29685 * Using `select` **`as`** will bind the result of the `select` expression to the model, but
29686 * the value of the `<select>` and `<option>` html elements will be either the index (for array data sources)
29687 * or property name (for object data sources) of the value within the collection. If a **`track by`** expression
29688 * is used, the result of that expression will be set as the value of the `option` and `select` elements.
29691 * ### `select` **`as`** and **`track by`**
29693 * <div class="alert alert-warning">
29694 * Be careful when using `select` **`as`** and **`track by`** in the same expression.
29697 * Given this array of items on the $scope:
29700 * $scope.items = [{
29703 * subItem: { name: 'aSubItem' }
29707 * subItem: { name: 'bSubItem' }
29714 * <select ng-options="item as item.label for item in items track by item.id" ng-model="selected"></select>
29717 * $scope.selected = $scope.items[0];
29720 * but this will not work:
29723 * <select ng-options="item.subItem as item.label for item in items track by item.id" ng-model="selected"></select>
29726 * $scope.selected = $scope.items[0].subItem;
29729 * In both examples, the **`track by`** expression is applied successfully to each `item` in the
29730 * `items` array. Because the selected option has been set programmatically in the controller, the
29731 * **`track by`** expression is also applied to the `ngModel` value. In the first example, the
29732 * `ngModel` value is `items[0]` and the **`track by`** expression evaluates to `items[0].id` with
29733 * no issue. In the second example, the `ngModel` value is `items[0].subItem` and the **`track by`**
29734 * expression evaluates to `items[0].subItem.id` (which is undefined). As a result, the model value
29735 * is not matched against any `<option>` and the `<select>` appears as having no selected value.
29738 * @param {string} ngModel Assignable AngularJS expression to data-bind to.
29739 * @param {comprehension_expression} ngOptions in one of the following forms:
29741 * * for array data sources:
29742 * * `label` **`for`** `value` **`in`** `array`
29743 * * `select` **`as`** `label` **`for`** `value` **`in`** `array`
29744 * * `label` **`group by`** `group` **`for`** `value` **`in`** `array`
29745 * * `label` **`disable when`** `disable` **`for`** `value` **`in`** `array`
29746 * * `label` **`group by`** `group` **`for`** `value` **`in`** `array` **`track by`** `trackexpr`
29747 * * `label` **`disable when`** `disable` **`for`** `value` **`in`** `array` **`track by`** `trackexpr`
29748 * * `label` **`for`** `value` **`in`** `array` | orderBy:`orderexpr` **`track by`** `trackexpr`
29749 * (for including a filter with `track by`)
29750 * * for object data sources:
29751 * * `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
29752 * * `select` **`as`** `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
29753 * * `label` **`group by`** `group` **`for (`**`key`**`,`** `value`**`) in`** `object`
29754 * * `label` **`disable when`** `disable` **`for (`**`key`**`,`** `value`**`) in`** `object`
29755 * * `select` **`as`** `label` **`group by`** `group`
29756 * **`for` `(`**`key`**`,`** `value`**`) in`** `object`
29757 * * `select` **`as`** `label` **`disable when`** `disable`
29758 * **`for` `(`**`key`**`,`** `value`**`) in`** `object`
29762 * * `array` / `object`: an expression which evaluates to an array / object to iterate over.
29763 * * `value`: local variable which will refer to each item in the `array` or each property value
29764 * of `object` during iteration.
29765 * * `key`: local variable which will refer to a property name in `object` during iteration.
29766 * * `label`: The result of this expression will be the label for `<option>` element. The
29767 * `expression` will most likely refer to the `value` variable (e.g. `value.propertyName`).
29768 * * `select`: The result of this expression will be bound to the model of the parent `<select>`
29769 * element. If not specified, `select` expression will default to `value`.
29770 * * `group`: The result of this expression will be used to group options using the `<optgroup>`
29772 * * `disable`: The result of this expression will be used to disable the rendered `<option>`
29773 * element. Return `true` to disable.
29774 * * `trackexpr`: Used when working with an array of objects. The result of this expression will be
29775 * used to identify the objects in the array. The `trackexpr` will most likely refer to the
29776 * `value` variable (e.g. `value.propertyName`). With this the selection is preserved
29777 * even when the options are recreated (e.g. reloaded from the server).
29778 * @param {string=} name Property name of the form under which the control is published.
29779 * @param {string=} required The control is considered valid only if value is entered.
29780 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
29781 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
29782 * `required` when you want to data-bind to the `required` attribute.
29783 * @param {string=} ngAttrSize sets the size of the select element dynamically. Uses the
29784 * {@link guide/interpolation#-ngattr-for-binding-to-arbitrary-attributes ngAttr} directive.
29787 <example module="selectExample" name="select">
29788 <file name="index.html">
29790 angular.module('selectExample', [])
29791 .controller('ExampleController', ['$scope', function($scope) {
29793 {name:'black', shade:'dark'},
29794 {name:'white', shade:'light', notAnOption: true},
29795 {name:'red', shade:'dark'},
29796 {name:'blue', shade:'dark', notAnOption: true},
29797 {name:'yellow', shade:'light', notAnOption: false}
29799 $scope.myColor = $scope.colors[2]; // red
29802 <div ng-controller="ExampleController">
29804 <li ng-repeat="color in colors">
29805 <label>Name: <input ng-model="color.name"></label>
29806 <label><input type="checkbox" ng-model="color.notAnOption"> Disabled?</label>
29807 <button ng-click="colors.splice($index, 1)" aria-label="Remove">X</button>
29810 <button ng-click="colors.push({})">add</button>
29814 <label>Color (null not allowed):
29815 <select ng-model="myColor" ng-options="color.name for color in colors"></select>
29817 <label>Color (null allowed):
29818 <span class="nullable">
29819 <select ng-model="myColor" ng-options="color.name for color in colors">
29820 <option value="">-- choose color --</option>
29822 </span></label><br/>
29824 <label>Color grouped by shade:
29825 <select ng-model="myColor" ng-options="color.name group by color.shade for color in colors">
29829 <label>Color grouped by shade, with some disabled:
29830 <select ng-model="myColor"
29831 ng-options="color.name group by color.shade disable when color.notAnOption for color in colors">
29837 Select <button ng-click="myColor = { name:'not in list', shade: 'other' }">bogus</button>.
29840 Currently selected: {{ {selected_color:myColor} }}
29841 <div style="border:solid 1px black; height:20px"
29842 ng-style="{'background-color':myColor.name}">
29846 <file name="protractor.js" type="protractor">
29847 it('should check ng-options', function() {
29848 expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('red');
29849 element.all(by.model('myColor')).first().click();
29850 element.all(by.css('select[ng-model="myColor"] option')).first().click();
29851 expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('black');
29852 element(by.css('.nullable select[ng-model="myColor"]')).click();
29853 element.all(by.css('.nullable select[ng-model="myColor"] option')).first().click();
29854 expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('null');
29860 /* eslint-disable max-len */
29861 // //00001111111111000000000002222222222000000000000000000000333333333300000000000000000000000004444444444400000000000005555555555555000000000666666666666600000007777777777777000000000000000888888888800000000000000000009999999999
29862 var 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]+?))?$/;
29863 // 1: value expression (valueFn)
29864 // 2: label expression (displayFn)
29865 // 3: group by expression (groupByFn)
29866 // 4: disable when expression (disableWhenFn)
29867 // 5: array item variable name
29868 // 6: object item key variable name
29869 // 7: object item value variable name
29870 // 8: collection expression
29871 // 9: track by expression
29872 /* eslint-enable */
29875 var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile, $document, $parse) {
29877 function parseOptionsExpression(optionsExp, selectElement, scope) {
29879 var match = optionsExp.match(NG_OPTIONS_REGEXP);
29881 throw ngOptionsMinErr('iexp',
29882 'Expected expression in form of ' +
29883 '\'_select_ (as _label_)? for (_key_,)?_value_ in _collection_\'' +
29884 ' but got \'{0}\'. Element: {1}',
29885 optionsExp, startingTag(selectElement));
29888 // Extract the parts from the ngOptions expression
29890 // The variable name for the value of the item in the collection
29891 var valueName = match[5] || match[7];
29892 // The variable name for the key of the item in the collection
29893 var keyName = match[6];
29895 // An expression that generates the viewValue for an option if there is a label expression
29896 var selectAs = / as /.test(match[0]) && match[1];
29897 // An expression that is used to track the id of each object in the options collection
29898 var trackBy = match[9];
29899 // An expression that generates the viewValue for an option if there is no label expression
29900 var valueFn = $parse(match[2] ? match[1] : valueName);
29901 var selectAsFn = selectAs && $parse(selectAs);
29902 var viewValueFn = selectAsFn || valueFn;
29903 var trackByFn = trackBy && $parse(trackBy);
29905 // Get the value by which we are going to track the option
29906 // if we have a trackFn then use that (passing scope and locals)
29907 // otherwise just hash the given viewValue
29908 var getTrackByValueFn = trackBy ?
29909 function(value, locals) { return trackByFn(scope, locals); } :
29910 function getHashOfValue(value) { return hashKey(value); };
29911 var getTrackByValue = function(value, key) {
29912 return getTrackByValueFn(value, getLocals(value, key));
29915 var displayFn = $parse(match[2] || match[1]);
29916 var groupByFn = $parse(match[3] || '');
29917 var disableWhenFn = $parse(match[4] || '');
29918 var valuesFn = $parse(match[8]);
29921 var getLocals = keyName ? function(value, key) {
29922 locals[keyName] = key;
29923 locals[valueName] = value;
29925 } : function(value) {
29926 locals[valueName] = value;
29931 function Option(selectValue, viewValue, label, group, disabled) {
29932 this.selectValue = selectValue;
29933 this.viewValue = viewValue;
29934 this.label = label;
29935 this.group = group;
29936 this.disabled = disabled;
29939 function getOptionValuesKeys(optionValues) {
29940 var optionValuesKeys;
29942 if (!keyName && isArrayLike(optionValues)) {
29943 optionValuesKeys = optionValues;
29945 // if object, extract keys, in enumeration order, unsorted
29946 optionValuesKeys = [];
29947 for (var itemKey in optionValues) {
29948 if (optionValues.hasOwnProperty(itemKey) && itemKey.charAt(0) !== '$') {
29949 optionValuesKeys.push(itemKey);
29953 return optionValuesKeys;
29958 getTrackByValue: getTrackByValue,
29959 getWatchables: $parse(valuesFn, function(optionValues) {
29960 // Create a collection of things that we would like to watch (watchedArray)
29961 // so that they can all be watched using a single $watchCollection
29962 // that only runs the handler once if anything changes
29963 var watchedArray = [];
29964 optionValues = optionValues || [];
29966 var optionValuesKeys = getOptionValuesKeys(optionValues);
29967 var optionValuesLength = optionValuesKeys.length;
29968 for (var index = 0; index < optionValuesLength; index++) {
29969 var key = (optionValues === optionValuesKeys) ? index : optionValuesKeys[index];
29970 var value = optionValues[key];
29972 var locals = getLocals(value, key);
29973 var selectValue = getTrackByValueFn(value, locals);
29974 watchedArray.push(selectValue);
29976 // Only need to watch the displayFn if there is a specific label expression
29977 if (match[2] || match[1]) {
29978 var label = displayFn(scope, locals);
29979 watchedArray.push(label);
29982 // Only need to watch the disableWhenFn if there is a specific disable expression
29984 var disableWhen = disableWhenFn(scope, locals);
29985 watchedArray.push(disableWhen);
29988 return watchedArray;
29991 getOptions: function() {
29993 var optionItems = [];
29994 var selectValueMap = {};
29996 // The option values were already computed in the `getWatchables` fn,
29997 // which must have been called to trigger `getOptions`
29998 var optionValues = valuesFn(scope) || [];
29999 var optionValuesKeys = getOptionValuesKeys(optionValues);
30000 var optionValuesLength = optionValuesKeys.length;
30002 for (var index = 0; index < optionValuesLength; index++) {
30003 var key = (optionValues === optionValuesKeys) ? index : optionValuesKeys[index];
30004 var value = optionValues[key];
30005 var locals = getLocals(value, key);
30006 var viewValue = viewValueFn(scope, locals);
30007 var selectValue = getTrackByValueFn(viewValue, locals);
30008 var label = displayFn(scope, locals);
30009 var group = groupByFn(scope, locals);
30010 var disabled = disableWhenFn(scope, locals);
30011 var optionItem = new Option(selectValue, viewValue, label, group, disabled);
30013 optionItems.push(optionItem);
30014 selectValueMap[selectValue] = optionItem;
30018 items: optionItems,
30019 selectValueMap: selectValueMap,
30020 getOptionFromViewValue: function(value) {
30021 return selectValueMap[getTrackByValue(value)];
30023 getViewValueFromOption: function(option) {
30024 // If the viewValue could be an object that may be mutated by the application,
30025 // we need to make a copy and not return the reference to the value on the option.
30026 return trackBy ? copy(option.viewValue) : option.viewValue;
30034 // we can't just jqLite('<option>') since jqLite is not smart enough
30035 // to create it in <select> and IE barfs otherwise.
30036 var optionTemplate = window.document.createElement('option'),
30037 optGroupTemplate = window.document.createElement('optgroup');
30039 function ngOptionsPostLink(scope, selectElement, attr, ctrls) {
30041 var selectCtrl = ctrls[0];
30042 var ngModelCtrl = ctrls[1];
30043 var multiple = attr.multiple;
30045 // The emptyOption allows the application developer to provide their own custom "empty"
30046 // option when the viewValue does not match any of the option values.
30047 for (var i = 0, children = selectElement.children(), ii = children.length; i < ii; i++) {
30048 if (children[i].value === '') {
30049 selectCtrl.hasEmptyOption = true;
30050 selectCtrl.emptyOption = children.eq(i);
30055 var providedEmptyOption = !!selectCtrl.emptyOption;
30057 var unknownOption = jqLite(optionTemplate.cloneNode(false));
30058 unknownOption.val('?');
30061 var ngOptions = parseOptionsExpression(attr.ngOptions, selectElement, scope);
30062 // This stores the newly created options before they are appended to the select.
30063 // Since the contents are removed from the fragment when it is appended,
30064 // we only need to create it once.
30065 var listFragment = $document[0].createDocumentFragment();
30067 // Overwrite the implementation. ngOptions doesn't use hashes
30068 selectCtrl.generateUnknownOptionValue = function(val) {
30072 // Update the controller methods for multiple selectable options
30075 selectCtrl.writeValue = function writeNgOptionsValue(value) {
30076 var selectedOption = options.selectValueMap[selectElement.val()];
30077 var option = options.getOptionFromViewValue(value);
30079 // Make sure to remove the selected attribute from the previously selected option
30080 // Otherwise, screen readers might get confused
30081 if (selectedOption) selectedOption.element.removeAttribute('selected');
30084 // Don't update the option when it is already selected.
30085 // For example, the browser will select the first option by default. In that case,
30086 // most properties are set automatically - except the `selected` attribute, which we
30089 if (selectElement[0].value !== option.selectValue) {
30090 selectCtrl.removeUnknownOption();
30091 selectCtrl.unselectEmptyOption();
30093 selectElement[0].value = option.selectValue;
30094 option.element.selected = true;
30097 option.element.setAttribute('selected', 'selected');
30100 if (providedEmptyOption) {
30101 selectCtrl.selectEmptyOption();
30102 } else if (selectCtrl.unknownOption.parent().length) {
30103 selectCtrl.updateUnknownOption(value);
30105 selectCtrl.renderUnknownOption(value);
30110 selectCtrl.readValue = function readNgOptionsValue() {
30112 var selectedOption = options.selectValueMap[selectElement.val()];
30114 if (selectedOption && !selectedOption.disabled) {
30115 selectCtrl.unselectEmptyOption();
30116 selectCtrl.removeUnknownOption();
30117 return options.getViewValueFromOption(selectedOption);
30122 // If we are using `track by` then we must watch the tracked value on the model
30123 // since ngModel only watches for object identity change
30124 // FIXME: When a user selects an option, this watch will fire needlessly
30125 if (ngOptions.trackBy) {
30127 function() { return ngOptions.getTrackByValue(ngModelCtrl.$viewValue); },
30128 function() { ngModelCtrl.$render(); }
30134 selectCtrl.writeValue = function writeNgOptionsMultiple(values) {
30135 // Only set `<option>.selected` if necessary, in order to prevent some browsers from
30136 // scrolling to `<option>` elements that are outside the `<select>` element's viewport.
30138 var selectedOptions = values && values.map(getAndUpdateSelectedOption) || [];
30140 options.items.forEach(function(option) {
30141 if (option.element.selected && !includes(selectedOptions, option)) {
30142 option.element.selected = false;
30148 selectCtrl.readValue = function readNgOptionsMultiple() {
30149 var selectedValues = selectElement.val() || [],
30152 forEach(selectedValues, function(value) {
30153 var option = options.selectValueMap[value];
30154 if (option && !option.disabled) selections.push(options.getViewValueFromOption(option));
30160 // If we are using `track by` then we must watch these tracked values on the model
30161 // since ngModel only watches for object identity change
30162 if (ngOptions.trackBy) {
30164 scope.$watchCollection(function() {
30165 if (isArray(ngModelCtrl.$viewValue)) {
30166 return ngModelCtrl.$viewValue.map(function(value) {
30167 return ngOptions.getTrackByValue(value);
30171 ngModelCtrl.$render();
30177 if (providedEmptyOption) {
30179 // we need to remove it before calling selectElement.empty() because otherwise IE will
30180 // remove the label from the element. wtf?
30181 selectCtrl.emptyOption.remove();
30183 // compile the element since there might be bindings in it
30184 $compile(selectCtrl.emptyOption)(scope);
30186 if (selectCtrl.emptyOption[0].nodeType === NODE_TYPE_COMMENT) {
30187 // This means the empty option has currently no actual DOM node, probably because
30188 // it has been modified by a transclusion directive.
30189 selectCtrl.hasEmptyOption = false;
30191 // Redefine the registerOption function, which will catch
30192 // options that are added by ngIf etc. (rendering of the node is async because of
30193 // lazy transclusion)
30194 selectCtrl.registerOption = function(optionScope, optionEl) {
30195 if (optionEl.val() === '') {
30196 selectCtrl.hasEmptyOption = true;
30197 selectCtrl.emptyOption = optionEl;
30198 selectCtrl.emptyOption.removeClass('ng-scope');
30199 // This ensures the new empty option is selected if previously no option was selected
30200 ngModelCtrl.$render();
30202 optionEl.on('$destroy', function() {
30203 selectCtrl.hasEmptyOption = false;
30204 selectCtrl.emptyOption = undefined;
30210 // remove the class, which is added automatically because we recompile the element and it
30211 // becomes the compilation root
30212 selectCtrl.emptyOption.removeClass('ng-scope');
30217 selectElement.empty();
30219 // We need to do this here to ensure that the options object is defined
30220 // when we first hit it in writeNgOptionsValue
30223 // We will re-render the option elements if the option values or labels change
30224 scope.$watchCollection(ngOptions.getWatchables, updateOptions);
30226 // ------------------------------------------------------------------ //
30228 function addOptionElement(option, parent) {
30229 var optionElement = optionTemplate.cloneNode(false);
30230 parent.appendChild(optionElement);
30231 updateOptionElement(option, optionElement);
30234 function getAndUpdateSelectedOption(viewValue) {
30235 var option = options.getOptionFromViewValue(viewValue);
30236 var element = option && option.element;
30238 if (element && !element.selected) element.selected = true;
30243 function updateOptionElement(option, element) {
30244 option.element = element;
30245 element.disabled = option.disabled;
30246 // NOTE: The label must be set before the value, otherwise IE10/11/EDGE create unresponsive
30247 // selects in certain circumstances when multiple selects are next to each other and display
30248 // the option list in listbox style, i.e. the select is [multiple], or specifies a [size].
30249 // See https://github.com/angular/angular.js/issues/11314 for more info.
30250 // This is unfortunately untestable with unit / e2e tests
30251 if (option.label !== element.label) {
30252 element.label = option.label;
30253 element.textContent = option.label;
30255 element.value = option.selectValue;
30258 function updateOptions() {
30259 var previousValue = options && selectCtrl.readValue();
30261 // We must remove all current options, but cannot simply set innerHTML = null
30262 // since the providedEmptyOption might have an ngIf on it that inserts comments which we
30264 // Instead, iterate over the current option elements and remove them or their optgroup
30268 for (var i = options.items.length - 1; i >= 0; i--) {
30269 var option = options.items[i];
30270 if (isDefined(option.group)) {
30271 jqLiteRemove(option.element.parentNode);
30273 jqLiteRemove(option.element);
30278 options = ngOptions.getOptions();
30280 var groupElementMap = {};
30282 // Ensure that the empty option is always there if it was explicitly provided
30283 if (providedEmptyOption) {
30284 selectElement.prepend(selectCtrl.emptyOption);
30287 options.items.forEach(function addOption(option) {
30290 if (isDefined(option.group)) {
30292 // This option is to live in a group
30293 // See if we have already created this group
30294 groupElement = groupElementMap[option.group];
30296 if (!groupElement) {
30298 groupElement = optGroupTemplate.cloneNode(false);
30299 listFragment.appendChild(groupElement);
30301 // Update the label on the group element
30302 // "null" is special cased because of Safari
30303 groupElement.label = option.group === null ? 'null' : option.group;
30305 // Store it for use later
30306 groupElementMap[option.group] = groupElement;
30309 addOptionElement(option, groupElement);
30313 // This option is not in a group
30314 addOptionElement(option, listFragment);
30318 selectElement[0].appendChild(listFragment);
30320 ngModelCtrl.$render();
30322 // Check to see if the value has changed due to the update to the options
30323 if (!ngModelCtrl.$isEmpty(previousValue)) {
30324 var nextValue = selectCtrl.readValue();
30325 var isNotPrimitive = ngOptions.trackBy || multiple;
30326 if (isNotPrimitive ? !equals(previousValue, nextValue) : previousValue !== nextValue) {
30327 ngModelCtrl.$setViewValue(nextValue);
30328 ngModelCtrl.$render();
30338 require: ['select', 'ngModel'],
30340 pre: function ngOptionsPreLink(scope, selectElement, attr, ctrls) {
30341 // Deactivate the SelectController.register method to prevent
30342 // option directives from accidentally registering themselves
30343 // (and unwanted $destroy handlers etc.)
30344 ctrls[0].registerOption = noop;
30346 post: ngOptionsPostLink
30353 * @name ngPluralize
30357 * `ngPluralize` is a directive that displays messages according to en-US localization rules.
30358 * These rules are bundled with angular.js, but can be overridden
30359 * (see {@link guide/i18n Angular i18n} dev guide). You configure ngPluralize directive
30360 * by specifying the mappings between
30361 * [plural categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html)
30362 * and the strings to be displayed.
30364 * # Plural categories and explicit number rules
30366 * [plural categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html)
30367 * in Angular's default en-US locale: "one" and "other".
30369 * While a plural category may match many numbers (for example, in en-US locale, "other" can match
30370 * any number that is not 1), an explicit number rule can only match one number. For example, the
30371 * explicit number rule for "3" matches the number 3. There are examples of plural categories
30372 * and explicit number rules throughout the rest of this documentation.
30374 * # Configuring ngPluralize
30375 * You configure ngPluralize by providing 2 attributes: `count` and `when`.
30376 * You can also provide an optional attribute, `offset`.
30378 * The value of the `count` attribute can be either a string or an {@link guide/expression
30379 * Angular expression}; these are evaluated on the current scope for its bound value.
30381 * The `when` attribute specifies the mappings between plural categories and the actual
30382 * string to be displayed. The value of the attribute should be a JSON object.
30384 * The following example shows how to configure ngPluralize:
30387 * <ng-pluralize count="personCount"
30388 when="{'0': 'Nobody is viewing.',
30389 * 'one': '1 person is viewing.',
30390 * 'other': '{} people are viewing.'}">
30394 * In the example, `"0: Nobody is viewing."` is an explicit number rule. If you did not
30395 * specify this rule, 0 would be matched to the "other" category and "0 people are viewing"
30396 * would be shown instead of "Nobody is viewing". You can specify an explicit number rule for
30397 * other numbers, for example 12, so that instead of showing "12 people are viewing", you can
30398 * show "a dozen people are viewing".
30400 * You can use a set of closed braces (`{}`) as a placeholder for the number that you want substituted
30401 * into pluralized strings. In the previous example, Angular will replace `{}` with
30402 * <span ng-non-bindable>`{{personCount}}`</span>. The closed braces `{}` is a placeholder
30403 * for <span ng-non-bindable>{{numberExpression}}</span>.
30405 * If no rule is defined for a category, then an empty string is displayed and a warning is generated.
30406 * Note that some locales define more categories than `one` and `other`. For example, fr-fr defines `few` and `many`.
30408 * # Configuring ngPluralize with offset
30409 * The `offset` attribute allows further customization of pluralized text, which can result in
30410 * a better user experience. For example, instead of the message "4 people are viewing this document",
30411 * you might display "John, Kate and 2 others are viewing this document".
30412 * The offset attribute allows you to offset a number by any desired value.
30413 * Let's take a look at an example:
30416 * <ng-pluralize count="personCount" offset=2
30417 * when="{'0': 'Nobody is viewing.',
30418 * '1': '{{person1}} is viewing.',
30419 * '2': '{{person1}} and {{person2}} are viewing.',
30420 * 'one': '{{person1}}, {{person2}} and one other person are viewing.',
30421 * 'other': '{{person1}}, {{person2}} and {} other people are viewing.'}">
30425 * Notice that we are still using two plural categories(one, other), but we added
30426 * three explicit number rules 0, 1 and 2.
30427 * When one person, perhaps John, views the document, "John is viewing" will be shown.
30428 * When three people view the document, no explicit number rule is found, so
30429 * an offset of 2 is taken off 3, and Angular uses 1 to decide the plural category.
30430 * In this case, plural category 'one' is matched and "John, Mary and one other person are viewing"
30433 * Note that when you specify offsets, you must provide explicit number rules for
30434 * numbers from 0 up to and including the offset. If you use an offset of 3, for example,
30435 * you must provide explicit number rules for 0, 1, 2 and 3. You must also provide plural strings for
30436 * plural categories "one" and "other".
30438 * @param {string|expression} count The variable to be bound to.
30439 * @param {string} when The mapping between plural category to its corresponding strings.
30440 * @param {number=} offset Offset to deduct from the total number.
30443 <example module="pluralizeExample" name="ng-pluralize">
30444 <file name="index.html">
30446 angular.module('pluralizeExample', [])
30447 .controller('ExampleController', ['$scope', function($scope) {
30448 $scope.person1 = 'Igor';
30449 $scope.person2 = 'Misko';
30450 $scope.personCount = 1;
30453 <div ng-controller="ExampleController">
30454 <label>Person 1:<input type="text" ng-model="person1" value="Igor" /></label><br/>
30455 <label>Person 2:<input type="text" ng-model="person2" value="Misko" /></label><br/>
30456 <label>Number of People:<input type="text" ng-model="personCount" value="1" /></label><br/>
30458 <!--- Example with simple pluralization rules for en locale --->
30460 <ng-pluralize count="personCount"
30461 when="{'0': 'Nobody is viewing.',
30462 'one': '1 person is viewing.',
30463 'other': '{} people are viewing.'}">
30464 </ng-pluralize><br>
30466 <!--- Example with offset --->
30468 <ng-pluralize count="personCount" offset=2
30469 when="{'0': 'Nobody is viewing.',
30470 '1': '{{person1}} is viewing.',
30471 '2': '{{person1}} and {{person2}} are viewing.',
30472 'one': '{{person1}}, {{person2}} and one other person are viewing.',
30473 'other': '{{person1}}, {{person2}} and {} other people are viewing.'}">
30477 <file name="protractor.js" type="protractor">
30478 it('should show correct pluralized string', function() {
30479 var withoutOffset = element.all(by.css('ng-pluralize')).get(0);
30480 var withOffset = element.all(by.css('ng-pluralize')).get(1);
30481 var countInput = element(by.model('personCount'));
30483 expect(withoutOffset.getText()).toEqual('1 person is viewing.');
30484 expect(withOffset.getText()).toEqual('Igor is viewing.');
30486 countInput.clear();
30487 countInput.sendKeys('0');
30489 expect(withoutOffset.getText()).toEqual('Nobody is viewing.');
30490 expect(withOffset.getText()).toEqual('Nobody is viewing.');
30492 countInput.clear();
30493 countInput.sendKeys('2');
30495 expect(withoutOffset.getText()).toEqual('2 people are viewing.');
30496 expect(withOffset.getText()).toEqual('Igor and Misko are viewing.');
30498 countInput.clear();
30499 countInput.sendKeys('3');
30501 expect(withoutOffset.getText()).toEqual('3 people are viewing.');
30502 expect(withOffset.getText()).toEqual('Igor, Misko and one other person are viewing.');
30504 countInput.clear();
30505 countInput.sendKeys('4');
30507 expect(withoutOffset.getText()).toEqual('4 people are viewing.');
30508 expect(withOffset.getText()).toEqual('Igor, Misko and 2 other people are viewing.');
30510 it('should show data-bound names', function() {
30511 var withOffset = element.all(by.css('ng-pluralize')).get(1);
30512 var personCount = element(by.model('personCount'));
30513 var person1 = element(by.model('person1'));
30514 var person2 = element(by.model('person2'));
30515 personCount.clear();
30516 personCount.sendKeys('4');
30518 person1.sendKeys('Di');
30520 person2.sendKeys('Vojta');
30521 expect(withOffset.getText()).toEqual('Di, Vojta and 2 other people are viewing.');
30526 var ngPluralizeDirective = ['$locale', '$interpolate', '$log', function($locale, $interpolate, $log) {
30528 IS_WHEN = /^when(Minus)?(.+)$/;
30531 link: function(scope, element, attr) {
30532 var numberExp = attr.count,
30533 whenExp = attr.$attr.when && element.attr(attr.$attr.when), // we have {{}} in attrs
30534 offset = attr.offset || 0,
30535 whens = scope.$eval(whenExp) || {},
30537 startSymbol = $interpolate.startSymbol(),
30538 endSymbol = $interpolate.endSymbol(),
30539 braceReplacement = startSymbol + numberExp + '-' + offset + endSymbol,
30540 watchRemover = angular.noop,
30543 forEach(attr, function(expression, attributeName) {
30544 var tmpMatch = IS_WHEN.exec(attributeName);
30546 var whenKey = (tmpMatch[1] ? '-' : '') + lowercase(tmpMatch[2]);
30547 whens[whenKey] = element.attr(attr.$attr[attributeName]);
30550 forEach(whens, function(expression, key) {
30551 whensExpFns[key] = $interpolate(expression.replace(BRACE, braceReplacement));
30555 scope.$watch(numberExp, function ngPluralizeWatchAction(newVal) {
30556 var count = parseFloat(newVal);
30557 var countIsNaN = isNumberNaN(count);
30559 if (!countIsNaN && !(count in whens)) {
30560 // If an explicit number rule such as 1, 2, 3... is defined, just use it.
30561 // Otherwise, check it against pluralization rules in $locale service.
30562 count = $locale.pluralCat(count - offset);
30565 // If both `count` and `lastCount` are NaN, we don't need to re-register a watch.
30566 // In JS `NaN !== NaN`, so we have to explicitly check.
30567 if ((count !== lastCount) && !(countIsNaN && isNumberNaN(lastCount))) {
30569 var whenExpFn = whensExpFns[count];
30570 if (isUndefined(whenExpFn)) {
30571 if (newVal != null) {
30572 $log.debug('ngPluralize: no rule defined for \'' + count + '\' in ' + whenExp);
30574 watchRemover = noop;
30575 updateElementText();
30577 watchRemover = scope.$watch(whenExpFn, updateElementText);
30583 function updateElementText(newText) {
30584 element.text(newText || '');
30590 /* exported ngRepeatDirective */
30599 * The `ngRepeat` directive instantiates a template once per item from a collection. Each template
30600 * instance gets its own scope, where the given loop variable is set to the current collection item,
30601 * and `$index` is set to the item index or key.
30603 * Special properties are exposed on the local scope of each template instance, including:
30605 * | Variable | Type | Details |
30606 * |-----------|-----------------|-----------------------------------------------------------------------------|
30607 * | `$index` | {@type number} | iterator offset of the repeated element (0..length-1) |
30608 * | `$first` | {@type boolean} | true if the repeated element is first in the iterator. |
30609 * | `$middle` | {@type boolean} | true if the repeated element is between the first and last in the iterator. |
30610 * | `$last` | {@type boolean} | true if the repeated element is last in the iterator. |
30611 * | `$even` | {@type boolean} | true if the iterator position `$index` is even (otherwise false). |
30612 * | `$odd` | {@type boolean} | true if the iterator position `$index` is odd (otherwise false). |
30614 * <div class="alert alert-info">
30615 * Creating aliases for these properties is possible with {@link ng.directive:ngInit `ngInit`}.
30616 * This may be useful when, for instance, nesting ngRepeats.
30620 * # Iterating over object properties
30622 * It is possible to get `ngRepeat` to iterate over the properties of an object using the following
30626 * <div ng-repeat="(key, value) in myObj"> ... </div>
30629 * However, there are a few limitations compared to array iteration:
30631 * - The JavaScript specification does not define the order of keys
30632 * returned for an object, so Angular relies on the order returned by the browser
30633 * when running `for key in myObj`. Browsers generally follow the strategy of providing
30634 * keys in the order in which they were defined, although there are exceptions when keys are deleted
30635 * and reinstated. See the
30636 * [MDN page on `delete` for more info](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/delete#Cross-browser_notes).
30638 * - `ngRepeat` will silently *ignore* object keys starting with `$`, because
30639 * it's a prefix used by Angular for public (`$`) and private (`$$`) properties.
30641 * - The built-in filters {@link ng.orderBy orderBy} and {@link ng.filter filter} do not work with
30642 * objects, and will throw an error if used with one.
30644 * If you are hitting any of these limitations, the recommended workaround is to convert your object into an array
30645 * that is sorted into the order that you prefer before providing it to `ngRepeat`. You could
30646 * do this with a filter such as [toArrayFilter](http://ngmodules.org/modules/angular-toArrayFilter)
30647 * or implement a `$watch` on the object yourself.
30650 * # Tracking and Duplicates
30652 * `ngRepeat` uses {@link $rootScope.Scope#$watchCollection $watchCollection} to detect changes in
30653 * the collection. When a change happens, `ngRepeat` then makes the corresponding changes to the DOM:
30655 * * When an item is added, a new instance of the template is added to the DOM.
30656 * * When an item is removed, its template instance is removed from the DOM.
30657 * * When items are reordered, their respective templates are reordered in the DOM.
30659 * To minimize creation of DOM elements, `ngRepeat` uses a function
30660 * to "keep track" of all items in the collection and their corresponding DOM elements.
30661 * For example, if an item is added to the collection, `ngRepeat` will know that all other items
30662 * already have DOM elements, and will not re-render them.
30664 * The default tracking function (which tracks items by their identity) does not allow
30665 * duplicate items in arrays. This is because when there are duplicates, it is not possible
30666 * to maintain a one-to-one mapping between collection items and DOM elements.
30668 * If you do need to repeat duplicate items, you can substitute the default tracking behavior
30669 * with your own using the `track by` expression.
30671 * For example, you may track items by the index of each item in the collection, using the
30672 * special scope property `$index`:
30674 * <div ng-repeat="n in [42, 42, 43, 43] track by $index">
30679 * You may also use arbitrary expressions in `track by`, including references to custom functions
30682 * <div ng-repeat="n in [42, 42, 43, 43] track by myTrackingFunction(n)">
30687 * <div class="alert alert-success">
30688 * If you are working with objects that have a unique identifier property, you should track
30689 * by this identifier instead of the object instance. Should you reload your data later, `ngRepeat`
30690 * will not have to rebuild the DOM elements for items it has already rendered, even if the
30691 * JavaScript objects in the collection have been substituted for new ones. For large collections,
30692 * this significantly improves rendering performance. If you don't have a unique identifier,
30693 * `track by $index` can also provide a performance boost.
30697 * <div ng-repeat="model in collection track by model.id">
30703 * <div class="alert alert-warning">
30704 * Avoid using `track by $index` when the repeated template contains
30705 * {@link guide/expression#one-time-binding one-time bindings}. In such cases, the `nth` DOM
30706 * element will always be matched with the `nth` item of the array, so the bindings on that element
30707 * will not be updated even when the corresponding item changes, essentially causing the view to get
30708 * out-of-sync with the underlying data.
30711 * When no `track by` expression is provided, it is equivalent to tracking by the built-in
30712 * `$id` function, which tracks items by their identity:
30714 * <div ng-repeat="obj in collection track by $id(obj)">
30720 * <div class="alert alert-warning">
30721 * **Note:** `track by` must always be the last expression:
30724 * <div ng-repeat="model in collection | orderBy: 'id' as filtered_result track by model.id">
30730 * # Special repeat start and end points
30731 * To repeat a series of elements instead of just one parent element, ngRepeat (as well as other ng directives) supports extending
30732 * the range of the repeater by defining explicit start and end points by using **ng-repeat-start** and **ng-repeat-end** respectively.
30733 * 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)
30734 * up to and including the ending HTML tag where **ng-repeat-end** is placed.
30736 * The example below makes use of this feature:
30738 * <header ng-repeat-start="item in items">
30739 * Header {{ item }}
30741 * <div class="body">
30744 * <footer ng-repeat-end>
30745 * Footer {{ item }}
30749 * And with an input of {@type ['A','B']} for the items variable in the example above, the output will evaluate to:
30754 * <div class="body">
30763 * <div class="body">
30771 * The custom start and end points for ngRepeat also support all other HTML directive syntax flavors provided in AngularJS (such
30772 * as **data-ng-repeat-start**, **x-ng-repeat-start** and **ng:repeat-start**).
30775 * | Animation | Occurs |
30776 * |----------------------------------|-------------------------------------|
30777 * | {@link ng.$animate#enter enter} | when a new item is added to the list or when an item is revealed after a filter |
30778 * | {@link ng.$animate#leave leave} | when an item is removed from the list or when an item is filtered out |
30779 * | {@link ng.$animate#move move } | when an adjacent item is filtered out causing a reorder or when the item contents are reordered |
30781 * See the example below for defining CSS animations with ngRepeat.
30786 * @param {repeat_expression} ngRepeat The expression indicating how to enumerate a collection. These
30787 * formats are currently supported:
30789 * * `variable in expression` – where variable is the user defined loop variable and `expression`
30790 * is a scope expression giving the collection to enumerate.
30792 * For example: `album in artist.albums`.
30794 * * `(key, value) in expression` – where `key` and `value` can be any user defined identifiers,
30795 * and `expression` is the scope expression giving the collection to enumerate.
30797 * For example: `(name, age) in {'adam':10, 'amalie':12}`.
30799 * * `variable in expression track by tracking_expression` – You can also provide an optional tracking expression
30800 * which can be used to associate the objects in the collection with the DOM elements. If no tracking expression
30801 * is specified, ng-repeat associates elements by identity. It is an error to have
30802 * more than one tracking expression value resolve to the same key. (This would mean that two distinct objects are
30803 * mapped to the same DOM element, which is not possible.)
30805 * Note that the tracking expression must come last, after any filters, and the alias expression.
30807 * For example: `item in items` is equivalent to `item in items track by $id(item)`. This implies that the DOM elements
30808 * will be associated by item identity in the array.
30810 * For example: `item in items track by $id(item)`. A built in `$id()` function can be used to assign a unique
30811 * `$$hashKey` property to each item in the array. This property is then used as a key to associated DOM elements
30812 * with the corresponding item in the array by identity. Moving the same object in array would move the DOM
30813 * element in the same way in the DOM.
30815 * For example: `item in items track by item.id` is a typical pattern when the items come from the database. In this
30816 * case the object identity does not matter. Two objects are considered equivalent as long as their `id`
30817 * property is same.
30819 * For example: `item in items | filter:searchText track by item.id` is a pattern that might be used to apply a filter
30820 * to items in conjunction with a tracking expression.
30822 * * `variable in expression as alias_expression` – You can also provide an optional alias expression which will then store the
30823 * intermediate results of the repeater after the filters have been applied. Typically this is used to render a special message
30824 * when a filter is active on the repeater, but the filtered result set is empty.
30826 * For example: `item in items | filter:x as results` will store the fragment of the repeated items as `results`, but only after
30827 * the items have been processed through the filter.
30829 * Please note that `as [variable name] is not an operator but rather a part of ngRepeat micro-syntax so it can be used only at the end
30830 * (and not as operator, inside an expression).
30832 * For example: `item in items | filter : x | orderBy : order | limitTo : limit as results` .
30835 * This example uses `ngRepeat` to display a list of people. A filter is used to restrict the displayed
30836 * results by name or by age. New (entering) and removed (leaving) items are animated.
30837 <example module="ngRepeat" name="ngRepeat" deps="angular-animate.js" animations="true" name="ng-repeat">
30838 <file name="index.html">
30839 <div ng-controller="repeatController">
30840 I have {{friends.length}} friends. They are:
30841 <input type="search" ng-model="q" placeholder="filter friends..." aria-label="filter friends" />
30842 <ul class="example-animate-container">
30843 <li class="animate-repeat" ng-repeat="friend in friends | filter:q as results">
30844 [{{$index + 1}}] {{friend.name}} who is {{friend.age}} years old.
30846 <li class="animate-repeat" ng-if="results.length === 0">
30847 <strong>No results found...</strong>
30852 <file name="script.js">
30853 angular.module('ngRepeat', ['ngAnimate']).controller('repeatController', function($scope) {
30855 {name:'John', age:25, gender:'boy'},
30856 {name:'Jessie', age:30, gender:'girl'},
30857 {name:'Johanna', age:28, gender:'girl'},
30858 {name:'Joy', age:15, gender:'girl'},
30859 {name:'Mary', age:28, gender:'girl'},
30860 {name:'Peter', age:95, gender:'boy'},
30861 {name:'Sebastian', age:50, gender:'boy'},
30862 {name:'Erika', age:27, gender:'girl'},
30863 {name:'Patrick', age:40, gender:'boy'},
30864 {name:'Samantha', age:60, gender:'girl'}
30868 <file name="animations.css">
30869 .example-animate-container {
30871 border:1px solid black;
30880 box-sizing:border-box;
30883 .animate-repeat.ng-move,
30884 .animate-repeat.ng-enter,
30885 .animate-repeat.ng-leave {
30886 transition:all linear 0.5s;
30889 .animate-repeat.ng-leave.ng-leave-active,
30890 .animate-repeat.ng-move,
30891 .animate-repeat.ng-enter {
30896 .animate-repeat.ng-leave,
30897 .animate-repeat.ng-move.ng-move-active,
30898 .animate-repeat.ng-enter.ng-enter-active {
30903 <file name="protractor.js" type="protractor">
30904 var friends = element.all(by.repeater('friend in friends'));
30906 it('should render initial data set', function() {
30907 expect(friends.count()).toBe(10);
30908 expect(friends.get(0).getText()).toEqual('[1] John who is 25 years old.');
30909 expect(friends.get(1).getText()).toEqual('[2] Jessie who is 30 years old.');
30910 expect(friends.last().getText()).toEqual('[10] Samantha who is 60 years old.');
30911 expect(element(by.binding('friends.length')).getText())
30912 .toMatch("I have 10 friends. They are:");
30915 it('should update repeater when filter predicate changes', function() {
30916 expect(friends.count()).toBe(10);
30918 element(by.model('q')).sendKeys('ma');
30920 expect(friends.count()).toBe(2);
30921 expect(friends.get(0).getText()).toEqual('[1] Mary who is 28 years old.');
30922 expect(friends.last().getText()).toEqual('[2] Samantha who is 60 years old.');
30927 var ngRepeatDirective = ['$parse', '$animate', '$compile', function($parse, $animate, $compile) {
30928 var NG_REMOVED = '$$NG_REMOVED';
30929 var ngRepeatMinErr = minErr('ngRepeat');
30931 var updateScope = function(scope, index, valueIdentifier, value, keyIdentifier, key, arrayLength) {
30932 // TODO(perf): generate setters to shave off ~40ms or 1-1.5%
30933 scope[valueIdentifier] = value;
30934 if (keyIdentifier) scope[keyIdentifier] = key;
30935 scope.$index = index;
30936 scope.$first = (index === 0);
30937 scope.$last = (index === (arrayLength - 1));
30938 scope.$middle = !(scope.$first || scope.$last);
30939 // eslint-disable-next-line no-bitwise
30940 scope.$odd = !(scope.$even = (index & 1) === 0);
30943 var getBlockStart = function(block) {
30944 return block.clone[0];
30947 var getBlockEnd = function(block) {
30948 return block.clone[block.clone.length - 1];
30954 multiElement: true,
30955 transclude: 'element',
30959 compile: function ngRepeatCompile($element, $attr) {
30960 var expression = $attr.ngRepeat;
30961 var ngRepeatEndComment = $compile.$$createComment('end ngRepeat', expression);
30963 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*$/);
30966 throw ngRepeatMinErr('iexp', 'Expected expression in form of \'_item_ in _collection_[ track by _id_]\' but got \'{0}\'.',
30970 var lhs = match[1];
30971 var rhs = match[2];
30972 var aliasAs = match[3];
30973 var trackByExp = match[4];
30975 match = lhs.match(/^(?:(\s*[$\w]+)|\(\s*([$\w]+)\s*,\s*([$\w]+)\s*\))$/);
30978 throw ngRepeatMinErr('iidexp', '\'_item_\' in \'_item_ in _collection_\' should be an identifier or \'(_key_, _value_)\' expression, but got \'{0}\'.',
30981 var valueIdentifier = match[3] || match[1];
30982 var keyIdentifier = match[2];
30984 if (aliasAs && (!/^[$a-zA-Z_][$a-zA-Z0-9_]*$/.test(aliasAs) ||
30985 /^(null|undefined|this|\$index|\$first|\$middle|\$last|\$even|\$odd|\$parent|\$root|\$id)$/.test(aliasAs))) {
30986 throw ngRepeatMinErr('badident', 'alias \'{0}\' is invalid --- must be a valid JS identifier which is not a reserved name.',
30990 var trackByExpGetter, trackByIdExpFn, trackByIdArrayFn, trackByIdObjFn;
30991 var hashFnLocals = {$id: hashKey};
30994 trackByExpGetter = $parse(trackByExp);
30996 trackByIdArrayFn = function(key, value) {
30997 return hashKey(value);
30999 trackByIdObjFn = function(key) {
31004 return function ngRepeatLink($scope, $element, $attr, ctrl, $transclude) {
31006 if (trackByExpGetter) {
31007 trackByIdExpFn = function(key, value, index) {
31008 // assign key, value, and $index to the locals so that they can be used in hash functions
31009 if (keyIdentifier) hashFnLocals[keyIdentifier] = key;
31010 hashFnLocals[valueIdentifier] = value;
31011 hashFnLocals.$index = index;
31012 return trackByExpGetter($scope, hashFnLocals);
31016 // Store a list of elements from previous run. This is a hash where key is the item from the
31017 // iterator, and the value is objects with following properties.
31018 // - scope: bound scope
31019 // - element: previous element.
31020 // - index: position
31022 // We are using no-proto object so that we don't need to guard against inherited props via
31024 var lastBlockMap = createMap();
31027 $scope.$watchCollection(rhs, function ngRepeatAction(collection) {
31029 previousNode = $element[0], // node that cloned nodes should be inserted after
31030 // initialized to the comment node anchor
31032 // Same as lastBlockMap but it has the current state. It will become the
31033 // lastBlockMap on the next iteration.
31034 nextBlockMap = createMap(),
31036 key, value, // key/value of iteration
31040 block, // last object information {scope, element, id}
31045 $scope[aliasAs] = collection;
31048 if (isArrayLike(collection)) {
31049 collectionKeys = collection;
31050 trackByIdFn = trackByIdExpFn || trackByIdArrayFn;
31052 trackByIdFn = trackByIdExpFn || trackByIdObjFn;
31053 // if object, extract keys, in enumeration order, unsorted
31054 collectionKeys = [];
31055 for (var itemKey in collection) {
31056 if (hasOwnProperty.call(collection, itemKey) && itemKey.charAt(0) !== '$') {
31057 collectionKeys.push(itemKey);
31062 collectionLength = collectionKeys.length;
31063 nextBlockOrder = new Array(collectionLength);
31065 // locate existing items
31066 for (index = 0; index < collectionLength; index++) {
31067 key = (collection === collectionKeys) ? index : collectionKeys[index];
31068 value = collection[key];
31069 trackById = trackByIdFn(key, value, index);
31070 if (lastBlockMap[trackById]) {
31071 // found previously seen block
31072 block = lastBlockMap[trackById];
31073 delete lastBlockMap[trackById];
31074 nextBlockMap[trackById] = block;
31075 nextBlockOrder[index] = block;
31076 } else if (nextBlockMap[trackById]) {
31077 // if collision detected. restore lastBlockMap and throw an error
31078 forEach(nextBlockOrder, function(block) {
31079 if (block && block.scope) lastBlockMap[block.id] = block;
31081 throw ngRepeatMinErr('dupes',
31082 'Duplicates in a repeater are not allowed. Use \'track by\' expression to specify unique keys. Repeater: {0}, Duplicate key: {1}, Duplicate value: {2}',
31083 expression, trackById, value);
31085 // new never before seen block
31086 nextBlockOrder[index] = {id: trackById, scope: undefined, clone: undefined};
31087 nextBlockMap[trackById] = true;
31091 // remove leftover items
31092 for (var blockKey in lastBlockMap) {
31093 block = lastBlockMap[blockKey];
31094 elementsToRemove = getBlockNodes(block.clone);
31095 $animate.leave(elementsToRemove);
31096 if (elementsToRemove[0].parentNode) {
31097 // if the element was not removed yet because of pending animation, mark it as deleted
31098 // so that we can ignore it later
31099 for (index = 0, length = elementsToRemove.length; index < length; index++) {
31100 elementsToRemove[index][NG_REMOVED] = true;
31103 block.scope.$destroy();
31106 // we are not using forEach for perf reasons (trying to avoid #call)
31107 for (index = 0; index < collectionLength; index++) {
31108 key = (collection === collectionKeys) ? index : collectionKeys[index];
31109 value = collection[key];
31110 block = nextBlockOrder[index];
31113 // if we have already seen this object, then we need to reuse the
31114 // associated scope/element
31116 nextNode = previousNode;
31118 // skip nodes that are already pending removal via leave animation
31120 nextNode = nextNode.nextSibling;
31121 } while (nextNode && nextNode[NG_REMOVED]);
31123 if (getBlockStart(block) !== nextNode) {
31124 // existing item which got moved
31125 $animate.move(getBlockNodes(block.clone), null, previousNode);
31127 previousNode = getBlockEnd(block);
31128 updateScope(block.scope, index, valueIdentifier, value, keyIdentifier, key, collectionLength);
31130 // new item which we don't know about
31131 $transclude(function ngRepeatTransclude(clone, scope) {
31132 block.scope = scope;
31133 // http://jsperf.com/clone-vs-createcomment
31134 var endNode = ngRepeatEndComment.cloneNode(false);
31135 clone[clone.length++] = endNode;
31137 $animate.enter(clone, null, previousNode);
31138 previousNode = endNode;
31139 // Note: We only need the first/last node of the cloned nodes.
31140 // However, we need to keep the reference to the jqlite wrapper as it might be changed later
31141 // by a directive with templateUrl when its template arrives.
31142 block.clone = clone;
31143 nextBlockMap[block.id] = block;
31144 updateScope(block.scope, index, valueIdentifier, value, keyIdentifier, key, collectionLength);
31148 lastBlockMap = nextBlockMap;
31155 var NG_HIDE_CLASS = 'ng-hide';
31156 var NG_HIDE_IN_PROGRESS_CLASS = 'ng-hide-animate';
31163 * The `ngShow` directive shows or hides the given HTML element based on the expression provided to
31164 * the `ngShow` attribute.
31166 * The element is shown or hidden by removing or adding the `.ng-hide` CSS class onto the element.
31167 * The `.ng-hide` CSS class is predefined in AngularJS and sets the display style to none (using an
31168 * `!important` flag). For CSP mode please add `angular-csp.css` to your HTML file (see
31169 * {@link ng.directive:ngCsp ngCsp}).
31172 * <!-- when $scope.myValue is truthy (element is visible) -->
31173 * <div ng-show="myValue"></div>
31175 * <!-- when $scope.myValue is falsy (element is hidden) -->
31176 * <div ng-show="myValue" class="ng-hide"></div>
31179 * When the `ngShow` expression evaluates to a falsy value then the `.ng-hide` CSS class is added
31180 * to the class attribute on the element causing it to become hidden. When truthy, the `.ng-hide`
31181 * CSS class is removed from the element causing the element not to appear hidden.
31183 * ## Why is `!important` used?
31185 * You may be wondering why `!important` is used for the `.ng-hide` CSS class. This is because the
31186 * `.ng-hide` selector can be easily overridden by heavier selectors. For example, something as
31187 * simple as changing the display style on a HTML list item would make hidden elements appear
31188 * visible. This also becomes a bigger issue when dealing with CSS frameworks.
31190 * By using `!important`, the show and hide behavior will work as expected despite any clash between
31191 * CSS selector specificity (when `!important` isn't used with any conflicting styles). If a
31192 * developer chooses to override the styling to change how to hide an element then it is just a
31193 * matter of using `!important` in their own CSS code.
31195 * ### Overriding `.ng-hide`
31197 * By default, the `.ng-hide` class will style the element with `display: none !important`. If you
31198 * wish to change the hide behavior with `ngShow`/`ngHide`, you can simply overwrite the styles for
31199 * the `.ng-hide` CSS class. Note that the selector that needs to be used is actually
31200 * `.ng-hide:not(.ng-hide-animate)` to cope with extra animation classes that can be added.
31203 * .ng-hide:not(.ng-hide-animate) {
31204 * /* These are just alternative ways of hiding an element */
31205 * display: block!important;
31206 * position: absolute;
31212 * By default you don't need to override anything in CSS and the animations will work around the
31215 * ## A note about animations with `ngShow`
31217 * Animations in `ngShow`/`ngHide` work with the show and hide events that are triggered when the
31218 * directive expression is true and false. This system works like the animation system present with
31219 * `ngClass` except that you must also include the `!important` flag to override the display
31220 * property so that the elements are not actually hidden during the animation.
31223 * /* A working example can be found at the bottom of this page. */
31224 * .my-element.ng-hide-add, .my-element.ng-hide-remove {
31225 * transition: all 0.5s linear;
31228 * .my-element.ng-hide-add { ... }
31229 * .my-element.ng-hide-add.ng-hide-add-active { ... }
31230 * .my-element.ng-hide-remove { ... }
31231 * .my-element.ng-hide-remove.ng-hide-remove-active { ... }
31234 * Keep in mind that, as of AngularJS version 1.3, there is no need to change the display property
31235 * to block during animation states - ngAnimate will automatically handle the style toggling for you.
31238 * | Animation | Occurs |
31239 * |-----------------------------------------------------|---------------------------------------------------------------------------------------------------------------|
31240 * | {@link $animate#addClass addClass} `.ng-hide` | After the `ngShow` expression evaluates to a non truthy value and just before the contents are set to hidden. |
31241 * | {@link $animate#removeClass removeClass} `.ng-hide` | After the `ngShow` expression evaluates to a truthy value and just before contents are set to visible. |
31244 * @param {expression} ngShow If the {@link guide/expression expression} is truthy/falsy then the
31245 * element is shown/hidden respectively.
31248 * A simple example, animating the element's opacity:
31250 <example module="ngAnimate" deps="angular-animate.js" animations="true" name="ng-show-simple">
31251 <file name="index.html">
31252 Show: <input type="checkbox" ng-model="checked" aria-label="Toggle ngShow"><br />
31253 <div class="check-element animate-show-hide" ng-show="checked">
31254 I show up when your checkbox is checked.
31257 <file name="animations.css">
31258 .animate-show-hide.ng-hide {
31262 .animate-show-hide.ng-hide-add,
31263 .animate-show-hide.ng-hide-remove {
31264 transition: all linear 0.5s;
31268 border: 1px solid black;
31273 <file name="protractor.js" type="protractor">
31274 it('should check ngShow', function() {
31275 var checkbox = element(by.model('checked'));
31276 var checkElem = element(by.css('.check-element'));
31278 expect(checkElem.isDisplayed()).toBe(false);
31280 expect(checkElem.isDisplayed()).toBe(true);
31287 * A more complex example, featuring different show/hide animations:
31289 <example module="ngAnimate" deps="angular-animate.js" animations="true" name="ng-show-complex">
31290 <file name="index.html">
31291 Show: <input type="checkbox" ng-model="checked" aria-label="Toggle ngShow"><br />
31292 <div class="check-element funky-show-hide" ng-show="checked">
31293 I show up when your checkbox is checked.
31296 <file name="animations.css">
31299 perspective: 1000px;
31302 .funky-show-hide.ng-hide-add {
31303 transform: rotateZ(0);
31304 transform-origin: right;
31305 transition: all 0.5s ease-in-out;
31308 .funky-show-hide.ng-hide-add.ng-hide-add-active {
31309 transform: rotateZ(-135deg);
31312 .funky-show-hide.ng-hide-remove {
31313 transform: rotateY(90deg);
31314 transform-origin: left;
31315 transition: all 0.5s ease;
31318 .funky-show-hide.ng-hide-remove.ng-hide-remove-active {
31319 transform: rotateY(0);
31323 border: 1px solid black;
31328 <file name="protractor.js" type="protractor">
31329 it('should check ngShow', function() {
31330 var checkbox = element(by.model('checked'));
31331 var checkElem = element(by.css('.check-element'));
31333 expect(checkElem.isDisplayed()).toBe(false);
31335 expect(checkElem.isDisplayed()).toBe(true);
31340 var ngShowDirective = ['$animate', function($animate) {
31343 multiElement: true,
31344 link: function(scope, element, attr) {
31345 scope.$watch(attr.ngShow, function ngShowWatchAction(value) {
31346 // we're adding a temporary, animation-specific class for ng-hide since this way
31347 // we can control when the element is actually displayed on screen without having
31348 // to have a global/greedy CSS selector that breaks when other animations are run.
31349 // Read: https://github.com/angular/angular.js/issues/9103#issuecomment-58335845
31350 $animate[value ? 'removeClass' : 'addClass'](element, NG_HIDE_CLASS, {
31351 tempClasses: NG_HIDE_IN_PROGRESS_CLASS
31365 * The `ngHide` directive shows or hides the given HTML element based on the expression provided to
31366 * the `ngHide` attribute.
31368 * The element is shown or hidden by removing or adding the `.ng-hide` CSS class onto the element.
31369 * The `.ng-hide` CSS class is predefined in AngularJS and sets the display style to none (using an
31370 * `!important` flag). For CSP mode please add `angular-csp.css` to your HTML file (see
31371 * {@link ng.directive:ngCsp ngCsp}).
31374 * <!-- when $scope.myValue is truthy (element is hidden) -->
31375 * <div ng-hide="myValue" class="ng-hide"></div>
31377 * <!-- when $scope.myValue is falsy (element is visible) -->
31378 * <div ng-hide="myValue"></div>
31381 * When the `ngHide` expression evaluates to a truthy value then the `.ng-hide` CSS class is added
31382 * to the class attribute on the element causing it to become hidden. When falsy, the `.ng-hide`
31383 * CSS class is removed from the element causing the element not to appear hidden.
31385 * ## Why is `!important` used?
31387 * You may be wondering why `!important` is used for the `.ng-hide` CSS class. This is because the
31388 * `.ng-hide` selector can be easily overridden by heavier selectors. For example, something as
31389 * simple as changing the display style on a HTML list item would make hidden elements appear
31390 * visible. This also becomes a bigger issue when dealing with CSS frameworks.
31392 * By using `!important`, the show and hide behavior will work as expected despite any clash between
31393 * CSS selector specificity (when `!important` isn't used with any conflicting styles). If a
31394 * developer chooses to override the styling to change how to hide an element then it is just a
31395 * matter of using `!important` in their own CSS code.
31397 * ### Overriding `.ng-hide`
31399 * By default, the `.ng-hide` class will style the element with `display: none !important`. If you
31400 * wish to change the hide behavior with `ngShow`/`ngHide`, you can simply overwrite the styles for
31401 * the `.ng-hide` CSS class. Note that the selector that needs to be used is actually
31402 * `.ng-hide:not(.ng-hide-animate)` to cope with extra animation classes that can be added.
31405 * .ng-hide:not(.ng-hide-animate) {
31406 * /* These are just alternative ways of hiding an element */
31407 * display: block!important;
31408 * position: absolute;
31414 * By default you don't need to override in CSS anything and the animations will work around the
31417 * ## A note about animations with `ngHide`
31419 * Animations in `ngShow`/`ngHide` work with the show and hide events that are triggered when the
31420 * directive expression is true and false. This system works like the animation system present with
31421 * `ngClass` except that you must also include the `!important` flag to override the display
31422 * property so that the elements are not actually hidden during the animation.
31425 * /* A working example can be found at the bottom of this page. */
31426 * .my-element.ng-hide-add, .my-element.ng-hide-remove {
31427 * transition: all 0.5s linear;
31430 * .my-element.ng-hide-add { ... }
31431 * .my-element.ng-hide-add.ng-hide-add-active { ... }
31432 * .my-element.ng-hide-remove { ... }
31433 * .my-element.ng-hide-remove.ng-hide-remove-active { ... }
31436 * Keep in mind that, as of AngularJS version 1.3, there is no need to change the display property
31437 * to block during animation states - ngAnimate will automatically handle the style toggling for you.
31440 * | Animation | Occurs |
31441 * |-----------------------------------------------------|------------------------------------------------------------------------------------------------------------|
31442 * | {@link $animate#addClass addClass} `.ng-hide` | After the `ngHide` expression evaluates to a truthy value and just before the contents are set to hidden. |
31443 * | {@link $animate#removeClass removeClass} `.ng-hide` | After the `ngHide` expression evaluates to a non truthy value and just before contents are set to visible. |
31447 * @param {expression} ngHide If the {@link guide/expression expression} is truthy/falsy then the
31448 * element is hidden/shown respectively.
31451 * A simple example, animating the element's opacity:
31453 <example module="ngAnimate" deps="angular-animate.js" animations="true" name="ng-hide-simple">
31454 <file name="index.html">
31455 Hide: <input type="checkbox" ng-model="checked" aria-label="Toggle ngHide"><br />
31456 <div class="check-element animate-show-hide" ng-hide="checked">
31457 I hide when your checkbox is checked.
31460 <file name="animations.css">
31461 .animate-show-hide.ng-hide {
31465 .animate-show-hide.ng-hide-add,
31466 .animate-show-hide.ng-hide-remove {
31467 transition: all linear 0.5s;
31471 border: 1px solid black;
31476 <file name="protractor.js" type="protractor">
31477 it('should check ngHide', function() {
31478 var checkbox = element(by.model('checked'));
31479 var checkElem = element(by.css('.check-element'));
31481 expect(checkElem.isDisplayed()).toBe(true);
31483 expect(checkElem.isDisplayed()).toBe(false);
31490 * A more complex example, featuring different show/hide animations:
31492 <example module="ngAnimate" deps="angular-animate.js" animations="true" name="ng-hide-complex">
31493 <file name="index.html">
31494 Hide: <input type="checkbox" ng-model="checked" aria-label="Toggle ngHide"><br />
31495 <div class="check-element funky-show-hide" ng-hide="checked">
31496 I hide when your checkbox is checked.
31499 <file name="animations.css">
31502 perspective: 1000px;
31505 .funky-show-hide.ng-hide-add {
31506 transform: rotateZ(0);
31507 transform-origin: right;
31508 transition: all 0.5s ease-in-out;
31511 .funky-show-hide.ng-hide-add.ng-hide-add-active {
31512 transform: rotateZ(-135deg);
31515 .funky-show-hide.ng-hide-remove {
31516 transform: rotateY(90deg);
31517 transform-origin: left;
31518 transition: all 0.5s ease;
31521 .funky-show-hide.ng-hide-remove.ng-hide-remove-active {
31522 transform: rotateY(0);
31526 border: 1px solid black;
31531 <file name="protractor.js" type="protractor">
31532 it('should check ngHide', function() {
31533 var checkbox = element(by.model('checked'));
31534 var checkElem = element(by.css('.check-element'));
31536 expect(checkElem.isDisplayed()).toBe(true);
31538 expect(checkElem.isDisplayed()).toBe(false);
31543 var ngHideDirective = ['$animate', function($animate) {
31546 multiElement: true,
31547 link: function(scope, element, attr) {
31548 scope.$watch(attr.ngHide, function ngHideWatchAction(value) {
31549 // The comment inside of the ngShowDirective explains why we add and
31550 // remove a temporary class for the show/hide animation
31551 $animate[value ? 'addClass' : 'removeClass'](element,NG_HIDE_CLASS, {
31552 tempClasses: NG_HIDE_IN_PROGRESS_CLASS
31565 * The `ngStyle` directive allows you to set CSS style on an HTML element conditionally.
31568 * You should not use {@link guide/interpolation interpolation} in the value of the `style`
31569 * attribute, when using the `ngStyle` directive on the same element.
31570 * See {@link guide/interpolation#known-issues here} for more info.
31573 * @param {expression} ngStyle
31575 * {@link guide/expression Expression} which evals to an
31576 * object whose keys are CSS style names and values are corresponding values for those CSS
31579 * Since some CSS style names are not valid keys for an object, they must be quoted.
31580 * See the 'background-color' style in the example below.
31583 <example name="ng-style">
31584 <file name="index.html">
31585 <input type="button" value="set color" ng-click="myStyle={color:'red'}">
31586 <input type="button" value="set background" ng-click="myStyle={'background-color':'blue'}">
31587 <input type="button" value="clear" ng-click="myStyle={}">
31589 <span ng-style="myStyle">Sample Text</span>
31590 <pre>myStyle={{myStyle}}</pre>
31592 <file name="style.css">
31597 <file name="protractor.js" type="protractor">
31598 var colorSpan = element(by.css('span'));
31600 it('should check ng-style', function() {
31601 expect(colorSpan.getCssValue('color')).toBe('rgba(0, 0, 0, 1)');
31602 element(by.css('input[value=\'set color\']')).click();
31603 expect(colorSpan.getCssValue('color')).toBe('rgba(255, 0, 0, 1)');
31604 element(by.css('input[value=clear]')).click();
31605 expect(colorSpan.getCssValue('color')).toBe('rgba(0, 0, 0, 1)');
31610 var ngStyleDirective = ngDirective(function(scope, element, attr) {
31611 scope.$watch(attr.ngStyle, function ngStyleWatchAction(newStyles, oldStyles) {
31612 if (oldStyles && (newStyles !== oldStyles)) {
31613 forEach(oldStyles, function(val, style) { element.css(style, '');});
31615 if (newStyles) element.css(newStyles);
31625 * The `ngSwitch` directive is used to conditionally swap DOM structure on your template based on a scope expression.
31626 * Elements within `ngSwitch` but without `ngSwitchWhen` or `ngSwitchDefault` directives will be preserved at the location
31627 * as specified in the template.
31629 * The directive itself works similar to ngInclude, however, instead of downloading template code (or loading it
31630 * from the template cache), `ngSwitch` simply chooses one of the nested elements and makes it visible based on which element
31631 * matches the value obtained from the evaluated expression. In other words, you define a container element
31632 * (where you place the directive), place an expression on the **`on="..."` attribute**
31633 * (or the **`ng-switch="..."` attribute**), define any inner elements inside of the directive and place
31634 * a when attribute per element. The when attribute is used to inform ngSwitch which element to display when the on
31635 * expression is evaluated. If a matching expression is not found via a when attribute then an element with the default
31636 * attribute is displayed.
31638 * <div class="alert alert-info">
31639 * Be aware that the attribute values to match against cannot be expressions. They are interpreted
31640 * as literal string values to match against.
31641 * For example, **`ng-switch-when="someVal"`** will match against the string `"someVal"` not against the
31642 * value of the expression `$scope.someVal`.
31646 * | Animation | Occurs |
31647 * |----------------------------------|-------------------------------------|
31648 * | {@link ng.$animate#enter enter} | after the ngSwitch contents change and the matched child element is placed inside the container |
31649 * | {@link ng.$animate#leave leave} | after the ngSwitch contents change and just before the former contents are removed from the DOM |
31654 * <ANY ng-switch="expression">
31655 * <ANY ng-switch-when="matchValue1">...</ANY>
31656 * <ANY ng-switch-when="matchValue2">...</ANY>
31657 * <ANY ng-switch-default>...</ANY>
31664 * @param {*} ngSwitch|on expression to match against <code>ng-switch-when</code>.
31665 * On child elements add:
31667 * * `ngSwitchWhen`: the case statement to match against. If match then this
31668 * case will be displayed. If the same match appears multiple times, all the
31669 * elements will be displayed. It is possible to associate multiple values to
31670 * the same `ngSwitchWhen` by defining the optional attribute
31671 * `ngSwitchWhenSeparator`. The separator will be used to split the value of
31672 * the `ngSwitchWhen` attribute into multiple tokens, and the element will show
31673 * if any of the `ngSwitch` evaluates to any of these tokens.
31674 * * `ngSwitchDefault`: the default case when no other case match. If there
31675 * are multiple default cases, all of them will be displayed when no other
31680 <example module="switchExample" deps="angular-animate.js" animations="true" name="ng-switch">
31681 <file name="index.html">
31682 <div ng-controller="ExampleController">
31683 <select ng-model="selection" ng-options="item for item in items">
31685 <code>selection={{selection}}</code>
31687 <div class="animate-switch-container"
31688 ng-switch on="selection">
31689 <div class="animate-switch" ng-switch-when="settings|options" ng-switch-when-separator="|">Settings Div</div>
31690 <div class="animate-switch" ng-switch-when="home">Home Span</div>
31691 <div class="animate-switch" ng-switch-default>default</div>
31695 <file name="script.js">
31696 angular.module('switchExample', ['ngAnimate'])
31697 .controller('ExampleController', ['$scope', function($scope) {
31698 $scope.items = ['settings', 'home', 'options', 'other'];
31699 $scope.selection = $scope.items[0];
31702 <file name="animations.css">
31703 .animate-switch-container {
31706 border:1px solid black;
31715 .animate-switch.ng-animate {
31716 transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
31725 .animate-switch.ng-leave.ng-leave-active,
31726 .animate-switch.ng-enter {
31729 .animate-switch.ng-leave,
31730 .animate-switch.ng-enter.ng-enter-active {
31734 <file name="protractor.js" type="protractor">
31735 var switchElem = element(by.css('[ng-switch]'));
31736 var select = element(by.model('selection'));
31738 it('should start in settings', function() {
31739 expect(switchElem.getText()).toMatch(/Settings Div/);
31741 it('should change to home', function() {
31742 select.all(by.css('option')).get(1).click();
31743 expect(switchElem.getText()).toMatch(/Home Span/);
31745 it('should change to settings via "options"', function() {
31746 select.all(by.css('option')).get(2).click();
31747 expect(switchElem.getText()).toMatch(/Settings Div/);
31749 it('should select default', function() {
31750 select.all(by.css('option')).get(3).click();
31751 expect(switchElem.getText()).toMatch(/default/);
31756 var ngSwitchDirective = ['$animate', '$compile', function($animate, $compile) {
31758 require: 'ngSwitch',
31760 // asks for $scope to fool the BC controller module
31761 controller: ['$scope', function NgSwitchController() {
31764 link: function(scope, element, attr, ngSwitchController) {
31765 var watchExpr = attr.ngSwitch || attr.on,
31766 selectedTranscludes = [],
31767 selectedElements = [],
31768 previousLeaveAnimations = [],
31769 selectedScopes = [];
31771 var spliceFactory = function(array, index) {
31772 return function(response) {
31773 if (response !== false) array.splice(index, 1);
31777 scope.$watch(watchExpr, function ngSwitchWatchAction(value) {
31780 // Start with the last, in case the array is modified during the loop
31781 while (previousLeaveAnimations.length) {
31782 $animate.cancel(previousLeaveAnimations.pop());
31785 for (i = 0, ii = selectedScopes.length; i < ii; ++i) {
31786 var selected = getBlockNodes(selectedElements[i].clone);
31787 selectedScopes[i].$destroy();
31788 var runner = previousLeaveAnimations[i] = $animate.leave(selected);
31789 runner.done(spliceFactory(previousLeaveAnimations, i));
31792 selectedElements.length = 0;
31793 selectedScopes.length = 0;
31795 if ((selectedTranscludes = ngSwitchController.cases['!' + value] || ngSwitchController.cases['?'])) {
31796 forEach(selectedTranscludes, function(selectedTransclude) {
31797 selectedTransclude.transclude(function(caseElement, selectedScope) {
31798 selectedScopes.push(selectedScope);
31799 var anchor = selectedTransclude.element;
31800 caseElement[caseElement.length++] = $compile.$$createComment('end ngSwitchWhen');
31801 var block = { clone: caseElement };
31803 selectedElements.push(block);
31804 $animate.enter(caseElement, anchor.parent(), anchor);
31813 var ngSwitchWhenDirective = ngDirective({
31814 transclude: 'element',
31816 require: '^ngSwitch',
31817 multiElement: true,
31818 link: function(scope, element, attrs, ctrl, $transclude) {
31820 var cases = attrs.ngSwitchWhen.split(attrs.ngSwitchWhenSeparator).sort().filter(
31821 // Filter duplicate cases
31822 function(element, index, array) { return array[index - 1] !== element; }
31825 forEach(cases, function(whenCase) {
31826 ctrl.cases['!' + whenCase] = (ctrl.cases['!' + whenCase] || []);
31827 ctrl.cases['!' + whenCase].push({ transclude: $transclude, element: element });
31832 var ngSwitchDefaultDirective = ngDirective({
31833 transclude: 'element',
31835 require: '^ngSwitch',
31836 multiElement: true,
31837 link: function(scope, element, attr, ctrl, $transclude) {
31838 ctrl.cases['?'] = (ctrl.cases['?'] || []);
31839 ctrl.cases['?'].push({ transclude: $transclude, element: element });
31845 * @name ngTransclude
31849 * Directive that marks the insertion point for the transcluded DOM of the nearest parent directive that uses transclusion.
31851 * You can specify that you want to insert a named transclusion slot, instead of the default slot, by providing the slot name
31852 * as the value of the `ng-transclude` or `ng-transclude-slot` attribute.
31854 * If the transcluded content is not empty (i.e. contains one or more DOM nodes, including whitespace text nodes), any existing
31855 * content of this element will be removed before the transcluded content is inserted.
31856 * If the transcluded content is empty (or only whitespace), the existing content is left intact. This lets you provide fallback
31857 * content in the case that no transcluded content is provided.
31861 * @param {string} ngTransclude|ngTranscludeSlot the name of the slot to insert at this point. If this is not provided, is empty
31862 * or its value is the same as the name of the attribute then the default slot is used.
31865 * ### Basic transclusion
31866 * This example demonstrates basic transclusion of content into a component directive.
31867 * <example name="simpleTranscludeExample" module="transcludeExample">
31868 * <file name="index.html">
31870 * angular.module('transcludeExample', [])
31871 * .directive('pane', function(){
31874 * transclude: true,
31875 * scope: { title:'@' },
31876 * template: '<div style="border: 1px solid black;">' +
31877 * '<div style="background-color: gray">{{title}}</div>' +
31878 * '<ng-transclude></ng-transclude>' +
31882 * .controller('ExampleController', ['$scope', function($scope) {
31883 * $scope.title = 'Lorem Ipsum';
31884 * $scope.text = 'Neque porro quisquam est qui dolorem ipsum quia dolor...';
31887 * <div ng-controller="ExampleController">
31888 * <input ng-model="title" aria-label="title"> <br/>
31889 * <textarea ng-model="text" aria-label="text"></textarea> <br/>
31890 * <pane title="{{title}}"><span>{{text}}</span></pane>
31893 * <file name="protractor.js" type="protractor">
31894 * it('should have transcluded', function() {
31895 * var titleElement = element(by.model('title'));
31896 * titleElement.clear();
31897 * titleElement.sendKeys('TITLE');
31898 * var textElement = element(by.model('text'));
31899 * textElement.clear();
31900 * textElement.sendKeys('TEXT');
31901 * expect(element(by.binding('title')).getText()).toEqual('TITLE');
31902 * expect(element(by.binding('text')).getText()).toEqual('TEXT');
31908 * ### Transclude fallback content
31909 * This example shows how to use `NgTransclude` with fallback content, that
31910 * is displayed if no transcluded content is provided.
31912 * <example module="transcludeFallbackContentExample" name="ng-transclude">
31913 * <file name="index.html">
31915 * angular.module('transcludeFallbackContentExample', [])
31916 * .directive('myButton', function(){
31919 * transclude: true,
31921 * template: '<button style="cursor: pointer;">' +
31922 * '<ng-transclude>' +
31923 * '<b style="color: red;">Button1</b>' +
31924 * '</ng-transclude>' +
31929 * <!-- fallback button content -->
31930 * <my-button id="fallback"></my-button>
31931 * <!-- modified button content -->
31932 * <my-button id="modified">
31933 * <i style="color: green;">Button2</i>
31936 * <file name="protractor.js" type="protractor">
31937 * it('should have different transclude element content', function() {
31938 * expect(element(by.id('fallback')).getText()).toBe('Button1');
31939 * expect(element(by.id('modified')).getText()).toBe('Button2');
31945 * ### Multi-slot transclusion
31946 * This example demonstrates using multi-slot transclusion in a component directive.
31947 * <example name="multiSlotTranscludeExample" module="multiSlotTranscludeExample">
31948 * <file name="index.html">
31950 * .title, .footer {
31951 * background-color: gray
31954 * <div ng-controller="ExampleController">
31955 * <input ng-model="title" aria-label="title"> <br/>
31956 * <textarea ng-model="text" aria-label="text"></textarea> <br/>
31958 * <pane-title><a ng-href="{{link}}">{{title}}</a></pane-title>
31959 * <pane-body><p>{{text}}</p></pane-body>
31963 * <file name="app.js">
31964 * angular.module('multiSlotTranscludeExample', [])
31965 * .directive('pane', function() {
31969 * 'title': '?paneTitle',
31970 * 'body': 'paneBody',
31971 * 'footer': '?paneFooter'
31973 * template: '<div style="border: 1px solid black;">' +
31974 * '<div class="title" ng-transclude="title">Fallback Title</div>' +
31975 * '<div ng-transclude="body"></div>' +
31976 * '<div class="footer" ng-transclude="footer">Fallback Footer</div>' +
31980 * .controller('ExampleController', ['$scope', function($scope) {
31981 * $scope.title = 'Lorem Ipsum';
31982 * $scope.link = 'https://google.com';
31983 * $scope.text = 'Neque porro quisquam est qui dolorem ipsum quia dolor...';
31986 * <file name="protractor.js" type="protractor">
31987 * it('should have transcluded the title and the body', function() {
31988 * var titleElement = element(by.model('title'));
31989 * titleElement.clear();
31990 * titleElement.sendKeys('TITLE');
31991 * var textElement = element(by.model('text'));
31992 * textElement.clear();
31993 * textElement.sendKeys('TEXT');
31994 * expect(element(by.css('.title')).getText()).toEqual('TITLE');
31995 * expect(element(by.binding('text')).getText()).toEqual('TEXT');
31996 * expect(element(by.css('.footer')).getText()).toEqual('Fallback Footer');
32001 var ngTranscludeMinErr = minErr('ngTransclude');
32002 var ngTranscludeDirective = ['$compile', function($compile) {
32006 compile: function ngTranscludeCompile(tElement) {
32008 // Remove and cache any original content to act as a fallback
32009 var fallbackLinkFn = $compile(tElement.contents());
32012 return function ngTranscludePostLink($scope, $element, $attrs, controller, $transclude) {
32014 if (!$transclude) {
32015 throw ngTranscludeMinErr('orphan',
32016 'Illegal use of ngTransclude directive in the template! ' +
32017 'No parent directive that requires a transclusion found. ' +
32019 startingTag($element));
32023 // If the attribute is of the form: `ng-transclude="ng-transclude"` then treat it like the default
32024 if ($attrs.ngTransclude === $attrs.$attr.ngTransclude) {
32025 $attrs.ngTransclude = '';
32027 var slotName = $attrs.ngTransclude || $attrs.ngTranscludeSlot;
32029 // If the slot is required and no transclusion content is provided then this call will throw an error
32030 $transclude(ngTranscludeCloneAttachFn, null, slotName);
32032 // If the slot is optional and no transclusion content is provided then use the fallback content
32033 if (slotName && !$transclude.isSlotFilled(slotName)) {
32034 useFallbackContent();
32037 function ngTranscludeCloneAttachFn(clone, transcludedScope) {
32038 if (clone.length && notWhitespace(clone)) {
32039 $element.append(clone);
32041 useFallbackContent();
32042 // There is nothing linked against the transcluded scope since no content was available,
32043 // so it should be safe to clean up the generated scope.
32044 transcludedScope.$destroy();
32048 function useFallbackContent() {
32049 // Since this is the fallback content rather than the transcluded content,
32050 // we link against the scope of this directive rather than the transcluded scope
32051 fallbackLinkFn($scope, function(clone) {
32052 $element.append(clone);
32056 function notWhitespace(nodes) {
32057 for (var i = 0, ii = nodes.length; i < ii; i++) {
32058 var node = nodes[i];
32059 if (node.nodeType !== NODE_TYPE_TEXT || node.nodeValue.trim()) {
32075 * Load the content of a `<script>` element into {@link ng.$templateCache `$templateCache`}, so that the
32076 * template can be used by {@link ng.directive:ngInclude `ngInclude`},
32077 * {@link ngRoute.directive:ngView `ngView`}, or {@link guide/directive directives}. The type of the
32078 * `<script>` element must be specified as `text/ng-template`, and a cache name for the template must be
32079 * assigned through the element's `id`, which can then be used as a directive's `templateUrl`.
32081 * @param {string} type Must be set to `'text/ng-template'`.
32082 * @param {string} id Cache name of the template.
32085 <example name="script-tag">
32086 <file name="index.html">
32087 <script type="text/ng-template" id="/tpl.html">
32088 Content of the template.
32091 <a ng-click="currentTpl='/tpl.html'" id="tpl-link">Load inlined template</a>
32092 <div id="tpl-content" ng-include src="currentTpl"></div>
32094 <file name="protractor.js" type="protractor">
32095 it('should load template defined inside script tag', function() {
32096 element(by.css('#tpl-link')).click();
32097 expect(element(by.css('#tpl-content')).getText()).toMatch(/Content of the template/);
32102 var scriptDirective = ['$templateCache', function($templateCache) {
32106 compile: function(element, attr) {
32107 if (attr.type === 'text/ng-template') {
32108 var templateUrl = attr.id,
32109 text = element[0].text;
32111 $templateCache.put(templateUrl, text);
32117 /* exported selectDirective, optionDirective */
32119 var noopNgModelController = { $setViewValue: noop, $render: noop };
32121 function setOptionSelectedStatus(optionEl, value) {
32122 optionEl.prop('selected', value); // needed for IE
32124 * When unselecting an option, setting the property to null / false should be enough
32125 * However, screenreaders might react to the selected attribute instead, see
32126 * https://github.com/angular/angular.js/issues/14419
32127 * Note: "selected" is a boolean attr and will be removed when the "value" arg in attr() is false
32130 optionEl.attr('selected', value);
32135 * @name select.SelectController
32137 * The controller for the `<select>` directive. This provides support for reading
32138 * and writing the selected value(s) of the control and also coordinates dynamically
32139 * added `<option>` elements, perhaps by an `ngRepeat` directive.
32141 var SelectController =
32142 ['$element', '$scope', /** @this */ function($element, $scope) {
32145 optionsMap = new NgMap();
32147 self.selectValueMap = {}; // Keys are the hashed values, values the original values
32149 // If the ngModel doesn't get provided then provide a dummy noop version to prevent errors
32150 self.ngModelCtrl = noopNgModelController;
32151 self.multiple = false;
32153 // The "unknown" option is one that is prepended to the list if the viewValue
32154 // does not match any of the options. When it is rendered the value of the unknown
32155 // option is '? XXX ?' where XXX is the hashKey of the value that is not known.
32157 // We can't just jqLite('<option>') since jqLite is not smart enough
32158 // to create it in <select> and IE barfs otherwise.
32159 self.unknownOption = jqLite(window.document.createElement('option'));
32161 // The empty option is an option with the value '' that te application developer can
32162 // provide inside the select. When the model changes to a value that doesn't match an option,
32163 // it is selected - so if an empty option is provided, no unknown option is generated.
32164 // However, the empty option is not removed when the model matches an option. It is always selectable
32165 // and indicates that a "null" selection has been made.
32166 self.hasEmptyOption = false;
32167 self.emptyOption = undefined;
32169 self.renderUnknownOption = function(val) {
32170 var unknownVal = self.generateUnknownOptionValue(val);
32171 self.unknownOption.val(unknownVal);
32172 $element.prepend(self.unknownOption);
32173 setOptionSelectedStatus(self.unknownOption, true);
32174 $element.val(unknownVal);
32177 self.updateUnknownOption = function(val) {
32178 var unknownVal = self.generateUnknownOptionValue(val);
32179 self.unknownOption.val(unknownVal);
32180 setOptionSelectedStatus(self.unknownOption, true);
32181 $element.val(unknownVal);
32184 self.generateUnknownOptionValue = function(val) {
32185 return '? ' + hashKey(val) + ' ?';
32188 self.removeUnknownOption = function() {
32189 if (self.unknownOption.parent()) self.unknownOption.remove();
32192 self.selectEmptyOption = function() {
32193 if (self.emptyOption) {
32195 setOptionSelectedStatus(self.emptyOption, true);
32199 self.unselectEmptyOption = function() {
32200 if (self.hasEmptyOption) {
32201 self.emptyOption.removeAttr('selected');
32205 $scope.$on('$destroy', function() {
32206 // disable unknown option so that we don't do work when the whole select is being destroyed
32207 self.renderUnknownOption = noop;
32210 // Read the value of the select control, the implementation of this changes depending
32211 // upon whether the select can have multiple values and whether ngOptions is at work.
32212 self.readValue = function readSingleValue() {
32213 var val = $element.val();
32214 // ngValue added option values are stored in the selectValueMap, normal interpolations are not
32215 var realVal = val in self.selectValueMap ? self.selectValueMap[val] : val;
32217 if (self.hasOption(realVal)) {
32225 // Write the value to the select control, the implementation of this changes depending
32226 // upon whether the select can have multiple values and whether ngOptions is at work.
32227 self.writeValue = function writeSingleValue(value) {
32228 // Make sure to remove the selected attribute from the previously selected option
32229 // Otherwise, screen readers might get confused
32230 var currentlySelectedOption = $element[0].options[$element[0].selectedIndex];
32231 if (currentlySelectedOption) setOptionSelectedStatus(jqLite(currentlySelectedOption), false);
32233 if (self.hasOption(value)) {
32234 self.removeUnknownOption();
32236 var hashedVal = hashKey(value);
32237 $element.val(hashedVal in self.selectValueMap ? hashedVal : value);
32239 // Set selected attribute and property on selected option for screen readers
32240 var selectedOption = $element[0].options[$element[0].selectedIndex];
32241 setOptionSelectedStatus(jqLite(selectedOption), true);
32243 if (value == null && self.emptyOption) {
32244 self.removeUnknownOption();
32245 self.selectEmptyOption();
32246 } else if (self.unknownOption.parent().length) {
32247 self.updateUnknownOption(value);
32249 self.renderUnknownOption(value);
32255 // Tell the select control that an option, with the given value, has been added
32256 self.addOption = function(value, element) {
32257 // Skip comment nodes, as they only pollute the `optionsMap`
32258 if (element[0].nodeType === NODE_TYPE_COMMENT) return;
32260 assertNotHasOwnProperty(value, '"option value"');
32261 if (value === '') {
32262 self.hasEmptyOption = true;
32263 self.emptyOption = element;
32265 var count = optionsMap.get(value) || 0;
32266 optionsMap.set(value, count + 1);
32267 // Only render at the end of a digest. This improves render performance when many options
32268 // are added during a digest and ensures all relevant options are correctly marked as selected
32272 // Tell the select control that an option, with the given value, has been removed
32273 self.removeOption = function(value) {
32274 var count = optionsMap.get(value);
32277 optionsMap.delete(value);
32278 if (value === '') {
32279 self.hasEmptyOption = false;
32280 self.emptyOption = undefined;
32283 optionsMap.set(value, count - 1);
32288 // Check whether the select control has an option matching the given value
32289 self.hasOption = function(value) {
32290 return !!optionsMap.get(value);
32294 var renderScheduled = false;
32295 function scheduleRender() {
32296 if (renderScheduled) return;
32297 renderScheduled = true;
32298 $scope.$$postDigest(function() {
32299 renderScheduled = false;
32300 self.ngModelCtrl.$render();
32304 var updateScheduled = false;
32305 function scheduleViewValueUpdate(renderAfter) {
32306 if (updateScheduled) return;
32308 updateScheduled = true;
32310 $scope.$$postDigest(function() {
32311 if ($scope.$$destroyed) return;
32313 updateScheduled = false;
32314 self.ngModelCtrl.$setViewValue(self.readValue());
32315 if (renderAfter) self.ngModelCtrl.$render();
32320 self.registerOption = function(optionScope, optionElement, optionAttrs, interpolateValueFn, interpolateTextFn) {
32322 if (optionAttrs.$attr.ngValue) {
32323 // The value attribute is set by ngValue
32324 var oldVal, hashedVal = NaN;
32325 optionAttrs.$observe('value', function valueAttributeObserveAction(newVal) {
32328 var previouslySelected = optionElement.prop('selected');
32330 if (isDefined(hashedVal)) {
32331 self.removeOption(oldVal);
32332 delete self.selectValueMap[hashedVal];
32336 hashedVal = hashKey(newVal);
32338 self.selectValueMap[hashedVal] = newVal;
32339 self.addOption(newVal, optionElement);
32340 // Set the attribute directly instead of using optionAttrs.$set - this stops the observer
32341 // from firing a second time. Other $observers on value will also get the result of the
32342 // ngValue expression, not the hashed value
32343 optionElement.attr('value', hashedVal);
32345 if (removal && previouslySelected) {
32346 scheduleViewValueUpdate();
32350 } else if (interpolateValueFn) {
32351 // The value attribute is interpolated
32352 optionAttrs.$observe('value', function valueAttributeObserveAction(newVal) {
32353 // This method is overwritten in ngOptions and has side-effects!
32357 var previouslySelected = optionElement.prop('selected');
32359 if (isDefined(oldVal)) {
32360 self.removeOption(oldVal);
32364 self.addOption(newVal, optionElement);
32366 if (removal && previouslySelected) {
32367 scheduleViewValueUpdate();
32370 } else if (interpolateTextFn) {
32371 // The text content is interpolated
32372 optionScope.$watch(interpolateTextFn, function interpolateWatchAction(newVal, oldVal) {
32373 optionAttrs.$set('value', newVal);
32374 var previouslySelected = optionElement.prop('selected');
32375 if (oldVal !== newVal) {
32376 self.removeOption(oldVal);
32378 self.addOption(newVal, optionElement);
32380 if (oldVal && previouslySelected) {
32381 scheduleViewValueUpdate();
32385 // The value attribute is static
32386 self.addOption(optionAttrs.value, optionElement);
32390 optionAttrs.$observe('disabled', function(newVal) {
32392 // Since model updates will also select disabled options (like ngOptions),
32393 // we only have to handle options becoming disabled, not enabled
32395 if (newVal === 'true' || newVal && optionElement.prop('selected')) {
32396 if (self.multiple) {
32397 scheduleViewValueUpdate(true);
32399 self.ngModelCtrl.$setViewValue(null);
32400 self.ngModelCtrl.$render();
32405 optionElement.on('$destroy', function() {
32406 var currentValue = self.readValue();
32407 var removeValue = optionAttrs.value;
32409 self.removeOption(removeValue);
32412 if (self.multiple && currentValue && currentValue.indexOf(removeValue) !== -1 ||
32413 currentValue === removeValue
32415 // When multiple (selected) options are destroyed at the same time, we don't want
32416 // to run a model update for each of them. Instead, run a single update in the $$postDigest
32417 scheduleViewValueUpdate(true);
32429 * HTML `select` element with angular data-binding.
32431 * The `select` directive is used together with {@link ngModel `ngModel`} to provide data-binding
32432 * between the scope and the `<select>` control (including setting default values).
32433 * It also handles dynamic `<option>` elements, which can be added using the {@link ngRepeat `ngRepeat}` or
32434 * {@link ngOptions `ngOptions`} directives.
32436 * When an item in the `<select>` menu is selected, the value of the selected option will be bound
32437 * to the model identified by the `ngModel` directive. With static or repeated options, this is
32438 * the content of the `value` attribute or the textContent of the `<option>`, if the value attribute is missing.
32439 * Value and textContent can be interpolated.
32441 * ## Matching model and option values
32443 * In general, the match between the model and an option is evaluated by strictly comparing the model
32444 * value against the value of the available options.
32446 * If you are setting the option value with the option's `value` attribute, or textContent, the
32447 * value will always be a `string` which means that the model value must also be a string.
32448 * Otherwise the `select` directive cannot match them correctly.
32450 * To bind the model to a non-string value, you can use one of the following strategies:
32451 * - the {@link ng.ngOptions `ngOptions`} directive
32452 * ({@link ng.select#using-select-with-ngoptions-and-setting-a-default-value})
32453 * - the {@link ng.ngValue `ngValue`} directive, which allows arbitrary expressions to be
32454 * option values ({@link ng.select#using-ngvalue-to-bind-the-model-to-an-array-of-objects Example})
32455 * - model $parsers / $formatters to convert the string value
32456 * ({@link ng.select#binding-select-to-a-non-string-value-via-ngmodel-parsing-formatting Example})
32458 * If the viewValue of `ngModel` does not match any of the options, then the control
32459 * will automatically add an "unknown" option, which it then removes when the mismatch is resolved.
32461 * Optionally, a single hard-coded `<option>` element, with the value set to an empty string, can
32462 * be nested into the `<select>` element. This element will then represent the `null` or "not selected"
32463 * option. See example below for demonstration.
32465 * ## Choosing between `ngRepeat` and `ngOptions`
32467 * In many cases, `ngRepeat` can be used on `<option>` elements instead of {@link ng.directive:ngOptions
32468 * ngOptions} to achieve a similar result. However, `ngOptions` provides some benefits:
32469 * - more flexibility in how the `<select>`'s model is assigned via the `select` **`as`** part of the
32470 * comprehension expression
32471 * - reduced memory consumption by not creating a new scope for each repeated instance
32472 * - increased render speed by creating the options in a documentFragment instead of individually
32474 * Specifically, select with repeated options slows down significantly starting at 2000 options in
32475 * Chrome and Internet Explorer / Edge.
32478 * @param {string} ngModel Assignable angular expression to data-bind to.
32479 * @param {string=} name Property name of the form under which the control is published.
32480 * @param {string=} multiple Allows multiple options to be selected. The selected values will be
32481 * bound to the model as an array.
32482 * @param {string=} required Sets `required` validation error key if the value is not entered.
32483 * @param {string=} ngRequired Adds required attribute and required validation constraint to
32484 * the element when the ngRequired expression evaluates to true. Use ngRequired instead of required
32485 * when you want to data-bind to the required attribute.
32486 * @param {string=} ngChange Angular expression to be executed when selected option(s) changes due to user
32487 * interaction with the select element.
32488 * @param {string=} ngOptions sets the options that the select is populated with and defines what is
32489 * set on the model on selection. See {@link ngOptions `ngOptions`}.
32490 * @param {string=} ngAttrSize sets the size of the select element dynamically. Uses the
32491 * {@link guide/interpolation#-ngattr-for-binding-to-arbitrary-attributes ngAttr} directive.
32494 * ### Simple `select` elements with static options
32496 * <example name="static-select" module="staticSelect">
32497 * <file name="index.html">
32498 * <div ng-controller="ExampleController">
32499 * <form name="myForm">
32500 * <label for="singleSelect"> Single select: </label><br>
32501 * <select name="singleSelect" ng-model="data.singleSelect">
32502 * <option value="option-1">Option 1</option>
32503 * <option value="option-2">Option 2</option>
32506 * <label for="singleSelect"> Single select with "not selected" option and dynamic option values: </label><br>
32507 * <select name="singleSelect" id="singleSelect" ng-model="data.singleSelect">
32508 * <option value="">---Please select---</option> <!-- not selected / blank option -->
32509 * <option value="{{data.option1}}">Option 1</option> <!-- interpolation -->
32510 * <option value="option-2">Option 2</option>
32512 * <button ng-click="forceUnknownOption()">Force unknown option</button><br>
32513 * <tt>singleSelect = {{data.singleSelect}}</tt>
32516 * <label for="multipleSelect"> Multiple select: </label><br>
32517 * <select name="multipleSelect" id="multipleSelect" ng-model="data.multipleSelect" multiple>
32518 * <option value="option-1">Option 1</option>
32519 * <option value="option-2">Option 2</option>
32520 * <option value="option-3">Option 3</option>
32522 * <tt>multipleSelect = {{data.multipleSelect}}</tt><br/>
32526 * <file name="app.js">
32527 * angular.module('staticSelect', [])
32528 * .controller('ExampleController', ['$scope', function($scope) {
32530 * singleSelect: null,
32531 * multipleSelect: [],
32532 * option1: 'option-1'
32535 * $scope.forceUnknownOption = function() {
32536 * $scope.data.singleSelect = 'nonsense';
32542 * ### Using `ngRepeat` to generate `select` options
32543 * <example name="select-ngrepeat" module="ngrepeatSelect">
32544 * <file name="index.html">
32545 * <div ng-controller="ExampleController">
32546 * <form name="myForm">
32547 * <label for="repeatSelect"> Repeat select: </label>
32548 * <select name="repeatSelect" id="repeatSelect" ng-model="data.model">
32549 * <option ng-repeat="option in data.availableOptions" value="{{option.id}}">{{option.name}}</option>
32553 * <tt>model = {{data.model}}</tt><br/>
32556 * <file name="app.js">
32557 * angular.module('ngrepeatSelect', [])
32558 * .controller('ExampleController', ['$scope', function($scope) {
32561 * availableOptions: [
32562 * {id: '1', name: 'Option A'},
32563 * {id: '2', name: 'Option B'},
32564 * {id: '3', name: 'Option C'}
32571 * ### Using `ngValue` to bind the model to an array of objects
32572 * <example name="select-ngvalue" module="ngvalueSelect">
32573 * <file name="index.html">
32574 * <div ng-controller="ExampleController">
32575 * <form name="myForm">
32576 * <label for="ngvalueselect"> ngvalue select: </label>
32577 * <select size="6" name="ngvalueselect" ng-model="data.model" multiple>
32578 * <option ng-repeat="option in data.availableOptions" ng-value="option.value">{{option.name}}</option>
32582 * <pre>model = {{data.model | json}}</pre><br/>
32585 * <file name="app.js">
32586 * angular.module('ngvalueSelect', [])
32587 * .controller('ExampleController', ['$scope', function($scope) {
32590 * availableOptions: [
32591 {value: 'myString', name: 'string'},
32592 {value: 1, name: 'integer'},
32593 {value: true, name: 'boolean'},
32594 {value: null, name: 'null'},
32595 {value: {prop: 'value'}, name: 'object'},
32596 {value: ['a'], name: 'array'}
32603 * ### Using `select` with `ngOptions` and setting a default value
32604 * See the {@link ngOptions ngOptions documentation} for more `ngOptions` usage examples.
32606 * <example name="select-with-default-values" module="defaultValueSelect">
32607 * <file name="index.html">
32608 * <div ng-controller="ExampleController">
32609 * <form name="myForm">
32610 * <label for="mySelect">Make a choice:</label>
32611 * <select name="mySelect" id="mySelect"
32612 * ng-options="option.name for option in data.availableOptions track by option.id"
32613 * ng-model="data.selectedOption"></select>
32616 * <tt>option = {{data.selectedOption}}</tt><br/>
32619 * <file name="app.js">
32620 * angular.module('defaultValueSelect', [])
32621 * .controller('ExampleController', ['$scope', function($scope) {
32623 * availableOptions: [
32624 * {id: '1', name: 'Option A'},
32625 * {id: '2', name: 'Option B'},
32626 * {id: '3', name: 'Option C'}
32628 * selectedOption: {id: '3', name: 'Option C'} //This sets the default value of the select in the ui
32635 * ### Binding `select` to a non-string value via `ngModel` parsing / formatting
32637 * <example name="select-with-non-string-options" module="nonStringSelect">
32638 * <file name="index.html">
32639 * <select ng-model="model.id" convert-to-number>
32640 * <option value="0">Zero</option>
32641 * <option value="1">One</option>
32642 * <option value="2">Two</option>
32646 * <file name="app.js">
32647 * angular.module('nonStringSelect', [])
32648 * .run(function($rootScope) {
32649 * $rootScope.model = { id: 2 };
32651 * .directive('convertToNumber', function() {
32653 * require: 'ngModel',
32654 * link: function(scope, element, attrs, ngModel) {
32655 * ngModel.$parsers.push(function(val) {
32656 * return parseInt(val, 10);
32658 * ngModel.$formatters.push(function(val) {
32665 * <file name="protractor.js" type="protractor">
32666 * it('should initialize to model', function() {
32667 * expect(element(by.model('model.id')).$('option:checked').getText()).toEqual('Two');
32673 var selectDirective = function() {
32677 require: ['select', '?ngModel'],
32678 controller: SelectController,
32681 pre: selectPreLink,
32682 post: selectPostLink
32686 function selectPreLink(scope, element, attr, ctrls) {
32688 var selectCtrl = ctrls[0];
32689 var ngModelCtrl = ctrls[1];
32691 // if ngModel is not defined, we don't need to do anything but set the registerOption
32692 // function to noop, so options don't get added internally
32693 if (!ngModelCtrl) {
32694 selectCtrl.registerOption = noop;
32699 selectCtrl.ngModelCtrl = ngModelCtrl;
32701 // When the selected item(s) changes we delegate getting the value of the select control
32702 // to the `readValue` method, which can be changed if the select can have multiple
32703 // selected values or if the options are being generated by `ngOptions`
32704 element.on('change', function() {
32705 selectCtrl.removeUnknownOption();
32706 scope.$apply(function() {
32707 ngModelCtrl.$setViewValue(selectCtrl.readValue());
32711 // If the select allows multiple values then we need to modify how we read and write
32712 // values from and to the control; also what it means for the value to be empty and
32713 // we have to add an extra watch since ngModel doesn't work well with arrays - it
32714 // doesn't trigger rendering if only an item in the array changes.
32715 if (attr.multiple) {
32716 selectCtrl.multiple = true;
32718 // Read value now needs to check each option to see if it is selected
32719 selectCtrl.readValue = function readMultipleValue() {
32721 forEach(element.find('option'), function(option) {
32722 if (option.selected && !option.disabled) {
32723 var val = option.value;
32724 array.push(val in selectCtrl.selectValueMap ? selectCtrl.selectValueMap[val] : val);
32730 // Write value now needs to set the selected property of each matching option
32731 selectCtrl.writeValue = function writeMultipleValue(value) {
32732 forEach(element.find('option'), function(option) {
32733 var shouldBeSelected = !!value && (includes(value, option.value) ||
32734 includes(value, selectCtrl.selectValueMap[option.value]));
32735 var currentlySelected = option.selected;
32737 // IE and Edge, adding options to the selection via shift+click/UP/DOWN,
32738 // will de-select already selected options if "selected" on those options was set
32739 // more than once (i.e. when the options were already selected)
32740 // So we only modify the selected property if neccessary.
32741 // Note: this behavior cannot be replicated via unit tests because it only shows in the
32742 // actual user interface.
32743 if (shouldBeSelected !== currentlySelected) {
32744 setOptionSelectedStatus(jqLite(option), shouldBeSelected);
32750 // we have to do it on each watch since ngModel watches reference, but
32751 // we need to work of an array, so we need to see if anything was inserted/removed
32752 var lastView, lastViewRef = NaN;
32753 scope.$watch(function selectMultipleWatch() {
32754 if (lastViewRef === ngModelCtrl.$viewValue && !equals(lastView, ngModelCtrl.$viewValue)) {
32755 lastView = shallowCopy(ngModelCtrl.$viewValue);
32756 ngModelCtrl.$render();
32758 lastViewRef = ngModelCtrl.$viewValue;
32761 // If we are a multiple select then value is now a collection
32762 // so the meaning of $isEmpty changes
32763 ngModelCtrl.$isEmpty = function(value) {
32764 return !value || value.length === 0;
32770 function selectPostLink(scope, element, attrs, ctrls) {
32771 // if ngModel is not defined, we don't need to do anything
32772 var ngModelCtrl = ctrls[1];
32773 if (!ngModelCtrl) return;
32775 var selectCtrl = ctrls[0];
32777 // We delegate rendering to the `writeValue` method, which can be changed
32778 // if the select can have multiple selected values or if the options are being
32779 // generated by `ngOptions`.
32780 // This must be done in the postLink fn to prevent $render to be called before
32781 // all nodes have been linked correctly.
32782 ngModelCtrl.$render = function() {
32783 selectCtrl.writeValue(ngModelCtrl.$viewValue);
32789 // The option directive is purely designed to communicate the existence (or lack of)
32790 // of dynamically created (and destroyed) option elements to their containing select
32791 // directive via its controller.
32792 var optionDirective = ['$interpolate', function($interpolate) {
32796 compile: function(element, attr) {
32797 var interpolateValueFn, interpolateTextFn;
32799 if (isDefined(attr.ngValue)) {
32800 // Will be handled by registerOption
32801 } else if (isDefined(attr.value)) {
32802 // If the value attribute is defined, check if it contains an interpolation
32803 interpolateValueFn = $interpolate(attr.value, true);
32805 // If the value attribute is not defined then we fall back to the
32806 // text content of the option element, which may be interpolated
32807 interpolateTextFn = $interpolate(element.text(), true);
32808 if (!interpolateTextFn) {
32809 attr.$set('value', element.text());
32813 return function(scope, element, attr) {
32814 // This is an optimization over using ^^ since we don't want to have to search
32815 // all the way to the root of the DOM for every single option element
32816 var selectCtrlName = '$selectController',
32817 parent = element.parent(),
32818 selectCtrl = parent.data(selectCtrlName) ||
32819 parent.parent().data(selectCtrlName); // in case we are in optgroup
32822 selectCtrl.registerOption(scope, element, attr, interpolateValueFn, interpolateTextFn);
32836 * ngRequired adds the required {@link ngModel.NgModelController#$validators `validator`} to {@link ngModel `ngModel`}.
32837 * It is most often used for {@link input `input`} and {@link select `select`} controls, but can also be
32838 * applied to custom controls.
32840 * The directive sets the `required` attribute on the element if the Angular expression inside
32841 * `ngRequired` evaluates to true. A special directive for setting `required` is necessary because we
32842 * cannot use interpolation inside `required`. See the {@link guide/interpolation interpolation guide}
32845 * The validator will set the `required` error key to true if the `required` attribute is set and
32846 * calling {@link ngModel.NgModelController#$isEmpty `NgModelController.$isEmpty`} with the
32847 * {@link ngModel.NgModelController#$viewValue `ngModel.$viewValue`} returns `true`. For example, the
32848 * `$isEmpty()` implementation for `input[text]` checks the length of the `$viewValue`. When developing
32849 * custom controls, `$isEmpty()` can be overwritten to account for a $viewValue that is not string-based.
32852 * <example name="ngRequiredDirective" module="ngRequiredExample">
32853 * <file name="index.html">
32855 * angular.module('ngRequiredExample', [])
32856 * .controller('ExampleController', ['$scope', function($scope) {
32857 * $scope.required = true;
32860 * <div ng-controller="ExampleController">
32861 * <form name="form">
32862 * <label for="required">Toggle required: </label>
32863 * <input type="checkbox" ng-model="required" id="required" />
32865 * <label for="input">This input must be filled if `required` is true: </label>
32866 * <input type="text" ng-model="model" id="input" name="input" ng-required="required" /><br>
32868 * required error set? = <code>{{form.input.$error.required}}</code><br>
32869 * model = <code>{{model}}</code>
32873 * <file name="protractor.js" type="protractor">
32874 var required = element(by.binding('form.input.$error.required'));
32875 var model = element(by.binding('model'));
32876 var input = element(by.id('input'));
32878 it('should set the required error', function() {
32879 expect(required.getText()).toContain('true');
32881 input.sendKeys('123');
32882 expect(required.getText()).not.toContain('true');
32883 expect(model.getText()).toContain('123');
32888 var requiredDirective = function() {
32891 require: '?ngModel',
32892 link: function(scope, elm, attr, ctrl) {
32894 attr.required = true; // force truthy in case we are on non input element
32896 ctrl.$validators.required = function(modelValue, viewValue) {
32897 return !attr.required || !ctrl.$isEmpty(viewValue);
32900 attr.$observe('required', function() {
32913 * ngPattern adds the pattern {@link ngModel.NgModelController#$validators `validator`} to {@link ngModel `ngModel`}.
32914 * It is most often used for text-based {@link input `input`} controls, but can also be applied to custom text-based controls.
32916 * The validator sets the `pattern` error key if the {@link ngModel.NgModelController#$viewValue `ngModel.$viewValue`}
32917 * does not match a RegExp which is obtained by evaluating the Angular expression given in the
32918 * `ngPattern` attribute value:
32919 * * If the expression evaluates to a RegExp object, then this is used directly.
32920 * * If the expression evaluates to a string, then it will be converted to a RegExp after wrapping it
32921 * in `^` and `$` characters. For instance, `"abc"` will be converted to `new RegExp('^abc$')`.
32923 * <div class="alert alert-info">
32924 * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
32925 * start at the index of the last search's match, thus not taking the whole input value into
32929 * <div class="alert alert-info">
32930 * **Note:** This directive is also added when the plain `pattern` attribute is used, with two
32934 * `ngPattern` does not set the `pattern` attribute and therefore HTML5 constraint validation is
32938 * The `ngPattern` attribute must be an expression, while the `pattern` value must be
32945 * <example name="ngPatternDirective" module="ngPatternExample">
32946 * <file name="index.html">
32948 * angular.module('ngPatternExample', [])
32949 * .controller('ExampleController', ['$scope', function($scope) {
32950 * $scope.regex = '\\d+';
32953 * <div ng-controller="ExampleController">
32954 * <form name="form">
32955 * <label for="regex">Set a pattern (regex string): </label>
32956 * <input type="text" ng-model="regex" id="regex" />
32958 * <label for="input">This input is restricted by the current pattern: </label>
32959 * <input type="text" ng-model="model" id="input" name="input" ng-pattern="regex" /><br>
32961 * input valid? = <code>{{form.input.$valid}}</code><br>
32962 * model = <code>{{model}}</code>
32966 * <file name="protractor.js" type="protractor">
32967 var model = element(by.binding('model'));
32968 var input = element(by.id('input'));
32970 it('should validate the input with the default pattern', function() {
32971 input.sendKeys('aaa');
32972 expect(model.getText()).not.toContain('aaa');
32974 input.clear().then(function() {
32975 input.sendKeys('123');
32976 expect(model.getText()).toContain('123');
32982 var patternDirective = function() {
32985 require: '?ngModel',
32986 link: function(scope, elm, attr, ctrl) {
32989 var regexp, patternExp = attr.ngPattern || attr.pattern;
32990 attr.$observe('pattern', function(regex) {
32991 if (isString(regex) && regex.length > 0) {
32992 regex = new RegExp('^' + regex + '$');
32995 if (regex && !regex.test) {
32996 throw minErr('ngPattern')('noregexp',
32997 'Expected {0} to be a RegExp but was {1}. Element: {2}', patternExp,
32998 regex, startingTag(elm));
33001 regexp = regex || undefined;
33005 ctrl.$validators.pattern = function(modelValue, viewValue) {
33006 // HTML5 pattern constraint validates the input value, so we validate the viewValue
33007 return ctrl.$isEmpty(viewValue) || isUndefined(regexp) || regexp.test(viewValue);
33015 * @name ngMaxlength
33019 * ngMaxlength adds the maxlength {@link ngModel.NgModelController#$validators `validator`} to {@link ngModel `ngModel`}.
33020 * It is most often used for text-based {@link input `input`} controls, but can also be applied to custom text-based controls.
33022 * The validator sets the `maxlength` error key if the {@link ngModel.NgModelController#$viewValue `ngModel.$viewValue`}
33023 * is longer than the integer obtained by evaluating the Angular expression given in the
33024 * `ngMaxlength` attribute value.
33026 * <div class="alert alert-info">
33027 * **Note:** This directive is also added when the plain `maxlength` attribute is used, with two
33031 * `ngMaxlength` does not set the `maxlength` attribute and therefore HTML5 constraint
33032 * validation is not available.
33035 * The `ngMaxlength` attribute must be an expression, while the `maxlength` value must be
33042 * <example name="ngMaxlengthDirective" module="ngMaxlengthExample">
33043 * <file name="index.html">
33045 * angular.module('ngMaxlengthExample', [])
33046 * .controller('ExampleController', ['$scope', function($scope) {
33047 * $scope.maxlength = 5;
33050 * <div ng-controller="ExampleController">
33051 * <form name="form">
33052 * <label for="maxlength">Set a maxlength: </label>
33053 * <input type="number" ng-model="maxlength" id="maxlength" />
33055 * <label for="input">This input is restricted by the current maxlength: </label>
33056 * <input type="text" ng-model="model" id="input" name="input" ng-maxlength="maxlength" /><br>
33058 * input valid? = <code>{{form.input.$valid}}</code><br>
33059 * model = <code>{{model}}</code>
33063 * <file name="protractor.js" type="protractor">
33064 var model = element(by.binding('model'));
33065 var input = element(by.id('input'));
33067 it('should validate the input with the default maxlength', function() {
33068 input.sendKeys('abcdef');
33069 expect(model.getText()).not.toContain('abcdef');
33071 input.clear().then(function() {
33072 input.sendKeys('abcde');
33073 expect(model.getText()).toContain('abcde');
33079 var maxlengthDirective = function() {
33082 require: '?ngModel',
33083 link: function(scope, elm, attr, ctrl) {
33086 var maxlength = -1;
33087 attr.$observe('maxlength', function(value) {
33088 var intVal = toInt(value);
33089 maxlength = isNumberNaN(intVal) ? -1 : intVal;
33092 ctrl.$validators.maxlength = function(modelValue, viewValue) {
33093 return (maxlength < 0) || ctrl.$isEmpty(viewValue) || (viewValue.length <= maxlength);
33101 * @name ngMinlength
33105 * ngMinlength adds the minlength {@link ngModel.NgModelController#$validators `validator`} to {@link ngModel `ngModel`}.
33106 * It is most often used for text-based {@link input `input`} controls, but can also be applied to custom text-based controls.
33108 * The validator sets the `minlength` error key if the {@link ngModel.NgModelController#$viewValue `ngModel.$viewValue`}
33109 * is shorter than the integer obtained by evaluating the Angular expression given in the
33110 * `ngMinlength` attribute value.
33112 * <div class="alert alert-info">
33113 * **Note:** This directive is also added when the plain `minlength` attribute is used, with two
33117 * `ngMinlength` does not set the `minlength` attribute and therefore HTML5 constraint
33118 * validation is not available.
33121 * The `ngMinlength` value must be an expression, while the `minlength` value must be
33128 * <example name="ngMinlengthDirective" module="ngMinlengthExample">
33129 * <file name="index.html">
33131 * angular.module('ngMinlengthExample', [])
33132 * .controller('ExampleController', ['$scope', function($scope) {
33133 * $scope.minlength = 3;
33136 * <div ng-controller="ExampleController">
33137 * <form name="form">
33138 * <label for="minlength">Set a minlength: </label>
33139 * <input type="number" ng-model="minlength" id="minlength" />
33141 * <label for="input">This input is restricted by the current minlength: </label>
33142 * <input type="text" ng-model="model" id="input" name="input" ng-minlength="minlength" /><br>
33144 * input valid? = <code>{{form.input.$valid}}</code><br>
33145 * model = <code>{{model}}</code>
33149 * <file name="protractor.js" type="protractor">
33150 var model = element(by.binding('model'));
33151 var input = element(by.id('input'));
33153 it('should validate the input with the default minlength', function() {
33154 input.sendKeys('ab');
33155 expect(model.getText()).not.toContain('ab');
33157 input.sendKeys('abc');
33158 expect(model.getText()).toContain('abc');
33163 var minlengthDirective = function() {
33166 require: '?ngModel',
33167 link: function(scope, elm, attr, ctrl) {
33171 attr.$observe('minlength', function(value) {
33172 minlength = toInt(value) || 0;
33175 ctrl.$validators.minlength = function(modelValue, viewValue) {
33176 return ctrl.$isEmpty(viewValue) || viewValue.length >= minlength;
33182 if (window.angular.bootstrap) {
33183 // AngularJS is already loaded, so we can return here...
33184 if (window.console) {
33185 console.log('WARNING: Tried to load angular more than once.');
33190 // try to bind to jquery now so that one can write jqLite(fn)
33191 // but we will rebind on bootstrap again.
33194 publishExternalAPI(angular);
33196 angular.module("ngLocale", [], ["$provide", function($provide) {
33197 var PLURAL_CATEGORY = {ZERO: "zero", ONE: "one", TWO: "two", FEW: "few", MANY: "many", OTHER: "other"};
33198 function getDecimals(n) {
33200 var i = n.indexOf('.');
33201 return (i == -1) ? 0 : n.length - i - 1;
33204 function getVF(n, opt_precision) {
33205 var v = opt_precision;
33207 if (undefined === v) {
33208 v = Math.min(getDecimals(n), 3);
33211 var base = Math.pow(10, v);
33212 var f = ((n * base) | 0) % base;
33213 return {v: v, f: f};
33216 $provide.value("$locale", {
33217 "DATETIME_FORMATS": {
33239 "FIRSTDAYOFWEEK": 6,
33277 "STANDALONEMONTH": [
33295 "fullDate": "EEEE, MMMM d, y",
33296 "longDate": "MMMM d, y",
33297 "medium": "MMM d, y h:mm:ss a",
33298 "mediumDate": "MMM d, y",
33299 "mediumTime": "h:mm:ss a",
33300 "short": "M/d/yy h:mm a",
33301 "shortDate": "M/d/yy",
33302 "shortTime": "h:mm a"
33304 "NUMBER_FORMATS": {
33305 "CURRENCY_SYM": "$",
33306 "DECIMAL_SEP": ".",
33326 "negPre": "-\u00a4",
33328 "posPre": "\u00a4",
33334 "localeID": "en_US",
33335 "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;}
33339 jqLite(function() {
33340 angularInit(window.document, bootstrap);
33345 !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>');