2 * @license AngularJS v1.4.10
3 * (c) 2010-2015 Google, Inc. http://angularjs.org
6 (function(window, document, undefined) {'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;
43 var templateArgs = arguments,
44 code = templateArgs[0],
45 message = '[' + (module ? module + ':' : '') + code + '] ',
46 template = templateArgs[1],
49 message += template.replace(/\{\d+\}/g, function(match) {
50 var index = +match.slice(1, -1),
51 shiftedIndex = index + SKIP_INDEXES;
53 if (shiftedIndex < templateArgs.length) {
54 return toDebugString(templateArgs[shiftedIndex]);
60 message += '\nhttp://errors.angularjs.org/1.4.10/' +
61 (module ? module + '/' : '') + code;
63 for (i = SKIP_INDEXES, paramPrefix = '?'; i < templateArgs.length; i++, paramPrefix = '&') {
64 message += paramPrefix + 'p' + (i - SKIP_INDEXES) + '=' +
65 encodeURIComponent(toDebugString(templateArgs[i]));
68 return new ErrorConstructor(message);
72 /* We need to tell jshint what variables are being exported */
73 /* global angular: true,
84 REGEX_STRING_REGEXP: true,
85 VALIDITY_STATE_PROPERTY: true,
89 manualLowercase: true,
90 manualUppercase: true,
123 escapeForRegexp: true,
136 toJsonReplacer: true,
139 convertTimezoneToLocal: true,
140 timezoneToOffset: true,
142 tryDecodeURIComponent: true,
145 encodeUriSegment: true,
146 encodeUriQuery: true,
149 getTestability: true,
154 assertNotHasOwnProperty: true,
157 hasOwnProperty: true,
160 NODE_TYPE_ELEMENT: true,
161 NODE_TYPE_ATTRIBUTE: true,
162 NODE_TYPE_TEXT: true,
163 NODE_TYPE_COMMENT: true,
164 NODE_TYPE_DOCUMENT: true,
165 NODE_TYPE_DOCUMENT_FRAGMENT: true,
168 ////////////////////////////////////
177 * The ng module is loaded by default when an AngularJS application is started. The module itself
178 * contains the essential components for an AngularJS application to function. The table below
179 * lists a high level breakdown of each of the services/factories, filters, directives and testing
180 * components available within this core module.
182 * <div doc-module-components="ng"></div>
185 var REGEX_STRING_REGEXP = /^\/(.+)\/([a-z]*)$/;
187 // The name of a form control's ValidityState property.
188 // This is used so that it's possible for internal tests to create mock ValidityStates.
189 var VALIDITY_STATE_PROPERTY = 'validity';
193 * @name angular.lowercase
197 * @description Converts the specified string to lowercase.
198 * @param {string} string String to be converted to lowercase.
199 * @returns {string} Lowercased string.
201 var lowercase = function(string) {return isString(string) ? string.toLowerCase() : string;};
202 var hasOwnProperty = Object.prototype.hasOwnProperty;
206 * @name angular.uppercase
210 * @description Converts the specified string to uppercase.
211 * @param {string} string String to be converted to uppercase.
212 * @returns {string} Uppercased string.
214 var uppercase = function(string) {return isString(string) ? string.toUpperCase() : string;};
217 var manualLowercase = function(s) {
218 /* jshint bitwise: false */
220 ? s.replace(/[A-Z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) | 32);})
223 var manualUppercase = function(s) {
224 /* jshint bitwise: false */
226 ? s.replace(/[a-z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) & ~32);})
231 // String#toLowerCase and String#toUpperCase don't produce correct results in browsers with Turkish
232 // locale, for this reason we need to detect this case and redefine lowercase/uppercase methods
233 // with correct but slower alternatives.
234 if ('i' !== 'I'.toLowerCase()) {
235 lowercase = manualLowercase;
236 uppercase = manualUppercase;
241 msie, // holds major version number for IE, or NaN if UA is not IE.
242 jqLite, // delay binding since jQuery could be loaded after us.
243 jQuery, // delay binding
247 toString = Object.prototype.toString,
248 getPrototypeOf = Object.getPrototypeOf,
249 ngMinErr = minErr('ng'),
252 angular = window.angular || (window.angular = {}),
257 * documentMode is an IE-only property
258 * http://msdn.microsoft.com/en-us/library/ie/cc196988(v=vs.85).aspx
260 msie = document.documentMode;
266 * @return {boolean} Returns true if `obj` is an array or array-like object (NodeList, Arguments,
269 function isArrayLike(obj) {
271 // `null`, `undefined` and `window` are not array-like
272 if (obj == null || isWindow(obj)) return false;
274 // arrays, strings and jQuery/jqLite objects are array like
275 // * jqLite is either the jQuery or jqLite constructor function
276 // * we have to check the existance of jqLite first as this method is called
277 // via the forEach method when constructing the jqLite object in the first place
278 if (isArray(obj) || isString(obj) || (jqLite && obj instanceof jqLite)) return true;
280 // Support: iOS 8.2 (not reproducible in simulator)
281 // "length" in obj used to prevent JIT error (gh-11508)
282 var length = "length" in Object(obj) && obj.length;
284 // NodeList objects (with `item` method) and
285 // other objects with suitable length characteristics are array-like
286 return isNumber(length) &&
287 (length >= 0 && ((length - 1) in obj || obj instanceof Array) || typeof obj.item == 'function');
293 * @name angular.forEach
298 * Invokes the `iterator` function once for each item in `obj` collection, which can be either an
299 * object or an array. The `iterator` function is invoked with `iterator(value, key, obj)`, where `value`
300 * is the value of an object property or an array element, `key` is the object property key or
301 * array element index and obj is the `obj` itself. Specifying a `context` for the function is optional.
303 * It is worth noting that `.forEach` does not iterate over inherited properties because it filters
304 * using the `hasOwnProperty` method.
307 * [Array.prototype.forEach](http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.18),
308 * providing 'undefined' or 'null' values for `obj` will not throw a TypeError, but rather just
309 * return the value provided.
312 var values = {name: 'misko', gender: 'male'};
314 angular.forEach(values, function(value, key) {
315 this.push(key + ': ' + value);
317 expect(log).toEqual(['name: misko', 'gender: male']);
320 * @param {Object|Array} obj Object to iterate over.
321 * @param {Function} iterator Iterator function.
322 * @param {Object=} context Object to become context (`this`) for the iterator function.
323 * @returns {Object|Array} Reference to `obj`.
326 function forEach(obj, iterator, context) {
329 if (isFunction(obj)) {
331 // Need to check if hasOwnProperty exists,
332 // as on IE8 the result of querySelectorAll is an object without a hasOwnProperty function
333 if (key != 'prototype' && key != 'length' && key != 'name' && (!obj.hasOwnProperty || obj.hasOwnProperty(key))) {
334 iterator.call(context, obj[key], key, obj);
337 } else if (isArray(obj) || isArrayLike(obj)) {
338 var isPrimitive = typeof obj !== 'object';
339 for (key = 0, length = obj.length; key < length; key++) {
340 if (isPrimitive || key in obj) {
341 iterator.call(context, obj[key], key, obj);
344 } else if (obj.forEach && obj.forEach !== forEach) {
345 obj.forEach(iterator, context, obj);
346 } else if (isBlankObject(obj)) {
347 // createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty
349 iterator.call(context, obj[key], key, obj);
351 } else if (typeof obj.hasOwnProperty === 'function') {
352 // Slow path for objects inheriting Object.prototype, hasOwnProperty check needed
354 if (obj.hasOwnProperty(key)) {
355 iterator.call(context, obj[key], key, obj);
359 // Slow path for objects which do not have a method `hasOwnProperty`
361 if (hasOwnProperty.call(obj, key)) {
362 iterator.call(context, obj[key], key, obj);
370 function forEachSorted(obj, iterator, context) {
371 var keys = Object.keys(obj).sort();
372 for (var i = 0; i < keys.length; i++) {
373 iterator.call(context, obj[keys[i]], keys[i]);
380 * when using forEach the params are value, key, but it is often useful to have key, value.
381 * @param {function(string, *)} iteratorFn
382 * @returns {function(*, string)}
384 function reverseParams(iteratorFn) {
385 return function(value, key) {iteratorFn(key, value);};
389 * A consistent way of creating unique IDs in angular.
391 * Using simple numbers allows us to generate 28.6 million unique ids per second for 10 years before
392 * we hit number precision issues in JavaScript.
394 * Math.pow(2,53) / 60 / 60 / 24 / 365 / 10 = 28.6M
396 * @returns {number} an unique alpha-numeric string
404 * Set or clear the hashkey for an object.
406 * @param h the hashkey (!truthy to delete the hashkey)
408 function setHashKey(obj, h) {
412 delete obj.$$hashKey;
417 function baseExtend(dst, objs, deep) {
418 var h = dst.$$hashKey;
420 for (var i = 0, ii = objs.length; i < ii; ++i) {
422 if (!isObject(obj) && !isFunction(obj)) continue;
423 var keys = Object.keys(obj);
424 for (var j = 0, jj = keys.length; j < jj; j++) {
428 if (deep && isObject(src)) {
430 dst[key] = new Date(src.valueOf());
431 } else if (isRegExp(src)) {
432 dst[key] = new RegExp(src);
433 } else if (src.nodeName) {
434 dst[key] = src.cloneNode(true);
435 } else if (isElement(src)) {
436 dst[key] = src.clone();
438 if (!isObject(dst[key])) dst[key] = isArray(src) ? [] : {};
439 baseExtend(dst[key], [src], true);
453 * @name angular.extend
458 * Extends the destination object `dst` by copying own enumerable properties from the `src` object(s)
459 * to `dst`. You can specify multiple `src` objects. If you want to preserve original objects, you can do so
460 * by passing an empty object as the target: `var object = angular.extend({}, object1, object2)`.
462 * **Note:** Keep in mind that `angular.extend` does not support recursive merge (deep copy). Use
463 * {@link angular.merge} for this.
465 * @param {Object} dst Destination object.
466 * @param {...Object} src Source object(s).
467 * @returns {Object} Reference to `dst`.
469 function extend(dst) {
470 return baseExtend(dst, slice.call(arguments, 1), false);
476 * @name angular.merge
481 * Deeply extends the destination object `dst` by copying own enumerable properties from the `src` object(s)
482 * to `dst`. You can specify multiple `src` objects. If you want to preserve original objects, you can do so
483 * by passing an empty object as the target: `var object = angular.merge({}, object1, object2)`.
485 * Unlike {@link angular.extend extend()}, `merge()` recursively descends into object properties of source
486 * objects, performing a deep copy.
488 * @param {Object} dst Destination object.
489 * @param {...Object} src Source object(s).
490 * @returns {Object} Reference to `dst`.
492 function merge(dst) {
493 return baseExtend(dst, slice.call(arguments, 1), true);
498 function toInt(str) {
499 return parseInt(str, 10);
503 function inherit(parent, extra) {
504 return extend(Object.create(parent), extra);
514 * A function that performs no operations. This function can be useful when writing code in the
517 function foo(callback) {
518 var result = calculateResult();
519 (callback || angular.noop)(result);
529 * @name angular.identity
534 * A function that returns its first argument. This function is useful when writing code in the
538 function transformer(transformationFn, value) {
539 return (transformationFn || angular.identity)(value);
542 * @param {*} value to be returned.
543 * @returns {*} the value passed in.
545 function identity($) {return $;}
546 identity.$inject = [];
549 function valueFn(value) {return function() {return value;};}
551 function hasCustomToString(obj) {
552 return isFunction(obj.toString) && obj.toString !== toString;
558 * @name angular.isUndefined
563 * Determines if a reference is undefined.
565 * @param {*} value Reference to check.
566 * @returns {boolean} True if `value` is undefined.
568 function isUndefined(value) {return typeof value === 'undefined';}
573 * @name angular.isDefined
578 * Determines if a reference is defined.
580 * @param {*} value Reference to check.
581 * @returns {boolean} True if `value` is defined.
583 function isDefined(value) {return typeof value !== 'undefined';}
588 * @name angular.isObject
593 * Determines if a reference is an `Object`. Unlike `typeof` in JavaScript, `null`s are not
594 * considered to be objects. Note that JavaScript arrays are objects.
596 * @param {*} value Reference to check.
597 * @returns {boolean} True if `value` is an `Object` but not `null`.
599 function isObject(value) {
600 // http://jsperf.com/isobject4
601 return value !== null && typeof value === 'object';
606 * Determine if a value is an object with a null prototype
608 * @returns {boolean} True if `value` is an `Object` with a null prototype
610 function isBlankObject(value) {
611 return value !== null && typeof value === 'object' && !getPrototypeOf(value);
617 * @name angular.isString
622 * Determines if a reference is a `String`.
624 * @param {*} value Reference to check.
625 * @returns {boolean} True if `value` is a `String`.
627 function isString(value) {return typeof value === 'string';}
632 * @name angular.isNumber
637 * Determines if a reference is a `Number`.
639 * This includes the "special" numbers `NaN`, `+Infinity` and `-Infinity`.
641 * If you wish to exclude these then you can use the native
642 * [`isFinite'](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/isFinite)
645 * @param {*} value Reference to check.
646 * @returns {boolean} True if `value` is a `Number`.
648 function isNumber(value) {return typeof value === 'number';}
653 * @name angular.isDate
658 * Determines if a value is a date.
660 * @param {*} value Reference to check.
661 * @returns {boolean} True if `value` is a `Date`.
663 function isDate(value) {
664 return toString.call(value) === '[object Date]';
670 * @name angular.isArray
675 * Determines if a reference is an `Array`.
677 * @param {*} value Reference to check.
678 * @returns {boolean} True if `value` is an `Array`.
680 var isArray = Array.isArray;
684 * @name angular.isFunction
689 * Determines if a reference is a `Function`.
691 * @param {*} value Reference to check.
692 * @returns {boolean} True if `value` is a `Function`.
694 function isFunction(value) {return typeof value === 'function';}
698 * Determines if a value is a regular expression object.
701 * @param {*} value Reference to check.
702 * @returns {boolean} True if `value` is a `RegExp`.
704 function isRegExp(value) {
705 return toString.call(value) === '[object RegExp]';
710 * Checks if `obj` is a window object.
713 * @param {*} obj Object to check
714 * @returns {boolean} True if `obj` is a window obj.
716 function isWindow(obj) {
717 return obj && obj.window === obj;
721 function isScope(obj) {
722 return obj && obj.$evalAsync && obj.$watch;
726 function isFile(obj) {
727 return toString.call(obj) === '[object File]';
731 function isFormData(obj) {
732 return toString.call(obj) === '[object FormData]';
736 function isBlob(obj) {
737 return toString.call(obj) === '[object Blob]';
741 function isBoolean(value) {
742 return typeof value === 'boolean';
746 function isPromiseLike(obj) {
747 return obj && isFunction(obj.then);
751 var TYPED_ARRAY_REGEXP = /^\[object (?:Uint8|Uint8Clamped|Uint16|Uint32|Int8|Int16|Int32|Float32|Float64)Array\]$/;
752 function isTypedArray(value) {
753 return value && isNumber(value.length) && TYPED_ARRAY_REGEXP.test(toString.call(value));
757 var trim = function(value) {
758 return isString(value) ? value.trim() : value;
762 // http://docs.closure-library.googlecode.com/git/local_closure_goog_string_string.js.source.html#line1021
763 // Prereq: s is a string.
764 var escapeForRegexp = function(s) {
765 return s.replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g, '\\$1').
766 replace(/\x08/g, '\\x08');
772 * @name angular.isElement
777 * Determines if a reference is a DOM element (or wrapped jQuery element).
779 * @param {*} value Reference to check.
780 * @returns {boolean} True if `value` is a DOM element (or wrapped jQuery element).
782 function isElement(node) {
784 (node.nodeName // we are a direct element
785 || (node.prop && node.attr && node.find))); // we have an on and find method part of jQuery API
789 * @param str 'key1,key2,...'
790 * @returns {object} in the form of {key1:true, key2:true, ...}
792 function makeMap(str) {
793 var obj = {}, items = str.split(','), i;
794 for (i = 0; i < items.length; i++) {
795 obj[items[i]] = true;
801 function nodeName_(element) {
802 return lowercase(element.nodeName || (element[0] && element[0].nodeName));
805 function includes(array, obj) {
806 return Array.prototype.indexOf.call(array, obj) != -1;
809 function arrayRemove(array, value) {
810 var index = array.indexOf(value);
812 array.splice(index, 1);
824 * Creates a deep copy of `source`, which should be an object or an array.
826 * * If no destination is supplied, a copy of the object or array is created.
827 * * If a destination is provided, all of its elements (for arrays) or properties (for objects)
828 * are deleted and then all elements/properties from the source are copied to it.
829 * * If `source` is not an object or array (inc. `null` and `undefined`), `source` is returned.
830 * * If `source` is identical to 'destination' an exception will be thrown.
832 * @param {*} source The source that will be used to make a copy.
833 * Can be any type, including primitives, `null`, and `undefined`.
834 * @param {(Object|Array)=} destination Destination into which the source is copied. If
835 * provided, must be of the same type as `source`.
836 * @returns {*} The copy or updated `destination`, if `destination` was specified.
839 <example module="copyExample">
840 <file name="index.html">
841 <div ng-controller="ExampleController">
842 <form novalidate class="simple-form">
843 Name: <input type="text" ng-model="user.name" /><br />
844 E-mail: <input type="email" ng-model="user.email" /><br />
845 Gender: <input type="radio" ng-model="user.gender" value="male" />male
846 <input type="radio" ng-model="user.gender" value="female" />female<br />
847 <button ng-click="reset()">RESET</button>
848 <button ng-click="update(user)">SAVE</button>
850 <pre>form = {{user | json}}</pre>
851 <pre>master = {{master | json}}</pre>
855 angular.module('copyExample', [])
856 .controller('ExampleController', ['$scope', function($scope) {
859 $scope.update = function(user) {
860 // Example with 1 argument
861 $scope.master= angular.copy(user);
864 $scope.reset = function() {
865 // Example with 2 arguments
866 angular.copy($scope.master, $scope.user);
875 function copy(source, destination) {
876 var stackSource = [];
880 if (isTypedArray(destination)) {
881 throw ngMinErr('cpta', "Can't copy! TypedArray destination cannot be mutated.");
883 if (source === destination) {
884 throw ngMinErr('cpi', "Can't copy! Source and destination are identical.");
887 // Empty the destination object
888 if (isArray(destination)) {
889 destination.length = 0;
891 forEach(destination, function(value, key) {
892 if (key !== '$$hashKey') {
893 delete destination[key];
898 stackSource.push(source);
899 stackDest.push(destination);
900 return copyRecurse(source, destination);
903 return copyElement(source);
905 function copyRecurse(source, destination) {
906 var h = destination.$$hashKey;
908 if (isArray(source)) {
909 for (var i = 0, ii = source.length; i < ii; i++) {
910 destination.push(copyElement(source[i]));
912 } else if (isBlankObject(source)) {
913 // createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty
914 for (key in source) {
915 destination[key] = copyElement(source[key]);
917 } else if (source && typeof source.hasOwnProperty === 'function') {
918 // Slow path, which must rely on hasOwnProperty
919 for (key in source) {
920 if (source.hasOwnProperty(key)) {
921 destination[key] = copyElement(source[key]);
925 // Slowest path --- hasOwnProperty can't be called as a method
926 for (key in source) {
927 if (hasOwnProperty.call(source, key)) {
928 destination[key] = copyElement(source[key]);
932 setHashKey(destination, h);
936 function copyElement(source) {
938 if (!isObject(source)) {
942 // Already copied values
943 var index = stackSource.indexOf(source);
945 return stackDest[index];
948 if (isWindow(source) || isScope(source)) {
949 throw ngMinErr('cpws',
950 "Can't copy! Making copies of Window or Scope instances is not supported.");
953 var needsRecurse = false;
956 if (isArray(source)) {
959 } else if (isTypedArray(source)) {
960 destination = new source.constructor(source);
961 } else if (isDate(source)) {
962 destination = new Date(source.getTime());
963 } else if (isRegExp(source)) {
964 destination = new RegExp(source.source, source.toString().match(/[^\/]*$/)[0]);
965 destination.lastIndex = source.lastIndex;
966 } else if (isBlob(source)) {
967 destination = new source.constructor([source], {type: source.type});
968 } else if (isFunction(source.cloneNode)) {
969 destination = source.cloneNode(true);
971 destination = Object.create(getPrototypeOf(source));
975 stackSource.push(source);
976 stackDest.push(destination);
979 ? copyRecurse(source, destination)
985 * Creates a shallow copy of an object, an array or a primitive.
987 * Assumes that there are no proto properties for objects.
989 function shallowCopy(src, dst) {
993 for (var i = 0, ii = src.length; i < ii; i++) {
996 } else if (isObject(src)) {
999 for (var key in src) {
1000 if (!(key.charAt(0) === '$' && key.charAt(1) === '$')) {
1001 dst[key] = src[key];
1012 * @name angular.equals
1017 * Determines if two objects or two values are equivalent. Supports value types, regular
1018 * expressions, arrays and objects.
1020 * Two objects or values are considered equivalent if at least one of the following is true:
1022 * * Both objects or values pass `===` comparison.
1023 * * Both objects or values are of the same type and all of their properties are equal by
1024 * comparing them with `angular.equals`.
1025 * * Both values are NaN. (In JavaScript, NaN == NaN => false. But we consider two NaN as equal)
1026 * * Both values represent the same regular expression (In JavaScript,
1027 * /abc/ == /abc/ => false. But we consider two regular expressions as equal when their textual
1028 * representation matches).
1030 * During a property comparison, properties of `function` type and properties with names
1031 * that begin with `$` are ignored.
1033 * Scope and DOMWindow objects are being compared only by identify (`===`).
1035 * @param {*} o1 Object or value to compare.
1036 * @param {*} o2 Object or value to compare.
1037 * @returns {boolean} True if arguments are equal.
1039 function equals(o1, o2) {
1040 if (o1 === o2) return true;
1041 if (o1 === null || o2 === null) return false;
1042 if (o1 !== o1 && o2 !== o2) return true; // NaN === NaN
1043 var t1 = typeof o1, t2 = typeof o2, length, key, keySet;
1045 if (t1 == 'object') {
1047 if (!isArray(o2)) return false;
1048 if ((length = o1.length) == o2.length) {
1049 for (key = 0; key < length; key++) {
1050 if (!equals(o1[key], o2[key])) return false;
1054 } else if (isDate(o1)) {
1055 if (!isDate(o2)) return false;
1056 return equals(o1.getTime(), o2.getTime());
1057 } else if (isRegExp(o1)) {
1058 return isRegExp(o2) ? o1.toString() == o2.toString() : false;
1060 if (isScope(o1) || isScope(o2) || isWindow(o1) || isWindow(o2) ||
1061 isArray(o2) || isDate(o2) || isRegExp(o2)) return false;
1062 keySet = createMap();
1064 if (key.charAt(0) === '$' || isFunction(o1[key])) continue;
1065 if (!equals(o1[key], o2[key])) return false;
1069 if (!(key in keySet) &&
1070 key.charAt(0) !== '$' &&
1071 isDefined(o2[key]) &&
1072 !isFunction(o2[key])) return false;
1081 var csp = function() {
1082 if (!isDefined(csp.rules)) {
1085 var ngCspElement = (document.querySelector('[ng-csp]') ||
1086 document.querySelector('[data-ng-csp]'));
1089 var ngCspAttribute = ngCspElement.getAttribute('ng-csp') ||
1090 ngCspElement.getAttribute('data-ng-csp');
1092 noUnsafeEval: !ngCspAttribute || (ngCspAttribute.indexOf('no-unsafe-eval') !== -1),
1093 noInlineStyle: !ngCspAttribute || (ngCspAttribute.indexOf('no-inline-style') !== -1)
1097 noUnsafeEval: noUnsafeEval(),
1098 noInlineStyle: false
1105 function noUnsafeEval() {
1107 /* jshint -W031, -W054 */
1109 /* jshint +W031, +W054 */
1123 * @param {string=} ngJq the name of the library available under `window`
1124 * to be used for angular.element
1126 * Use this directive to force the angular.element library. This should be
1127 * used to force either jqLite by leaving ng-jq blank or setting the name of
1128 * the jquery variable under window (eg. jQuery).
1130 * Since angular looks for this directive when it is loaded (doesn't wait for the
1131 * DOMContentLoaded event), it must be placed on an element that comes before the script
1132 * which loads angular. Also, only the first instance of `ng-jq` will be used and all
1136 * This example shows how to force jqLite using the `ngJq` directive to the `html` tag.
1145 * This example shows how to use a jQuery based library of a different name.
1146 * The library name must be available at the top most 'window'.
1149 <html ng-app ng-jq="jQueryLib">
1155 var jq = function() {
1156 if (isDefined(jq.name_)) return jq.name_;
1158 var i, ii = ngAttrPrefixes.length, prefix, name;
1159 for (i = 0; i < ii; ++i) {
1160 prefix = ngAttrPrefixes[i];
1161 if (el = document.querySelector('[' + prefix.replace(':', '\\:') + 'jq]')) {
1162 name = el.getAttribute(prefix + 'jq');
1167 return (jq.name_ = name);
1170 function concat(array1, array2, index) {
1171 return array1.concat(slice.call(array2, index));
1174 function sliceArgs(args, startIndex) {
1175 return slice.call(args, startIndex || 0);
1182 * @name angular.bind
1187 * Returns a function which calls function `fn` bound to `self` (`self` becomes the `this` for
1188 * `fn`). You can supply optional `args` that are prebound to the function. This feature is also
1189 * known as [partial application](http://en.wikipedia.org/wiki/Partial_application), as
1190 * distinguished from [function currying](http://en.wikipedia.org/wiki/Currying#Contrast_with_partial_function_application).
1192 * @param {Object} self Context which `fn` should be evaluated in.
1193 * @param {function()} fn Function to be bound.
1194 * @param {...*} args Optional arguments to be prebound to the `fn` function call.
1195 * @returns {function()} Function that wraps the `fn` with all the specified bindings.
1198 function bind(self, fn) {
1199 var curryArgs = arguments.length > 2 ? sliceArgs(arguments, 2) : [];
1200 if (isFunction(fn) && !(fn instanceof RegExp)) {
1201 return curryArgs.length
1203 return arguments.length
1204 ? fn.apply(self, concat(curryArgs, arguments, 0))
1205 : fn.apply(self, curryArgs);
1208 return arguments.length
1209 ? fn.apply(self, arguments)
1213 // in IE, native methods are not functions so they cannot be bound (note: they don't need to be)
1219 function toJsonReplacer(key, value) {
1222 if (typeof key === 'string' && key.charAt(0) === '$' && key.charAt(1) === '$') {
1224 } else if (isWindow(value)) {
1226 } else if (value && document === value) {
1228 } else if (isScope(value)) {
1238 * @name angular.toJson
1243 * Serializes input into a JSON-formatted string. Properties with leading $$ characters will be
1244 * stripped since angular uses this notation internally.
1246 * @param {Object|Array|Date|string|number} obj Input to be serialized into JSON.
1247 * @param {boolean|number} [pretty=2] If set to true, the JSON output will contain newlines and whitespace.
1248 * If set to an integer, the JSON output will contain that many spaces per indentation.
1249 * @returns {string|undefined} JSON-ified string representing `obj`.
1251 function toJson(obj, pretty) {
1252 if (isUndefined(obj)) return undefined;
1253 if (!isNumber(pretty)) {
1254 pretty = pretty ? 2 : null;
1256 return JSON.stringify(obj, toJsonReplacer, pretty);
1262 * @name angular.fromJson
1267 * Deserializes a JSON string.
1269 * @param {string} json JSON string to deserialize.
1270 * @returns {Object|Array|string|number} Deserialized JSON string.
1272 function fromJson(json) {
1273 return isString(json)
1279 var ALL_COLONS = /:/g;
1280 function timezoneToOffset(timezone, fallback) {
1281 // IE/Edge do not "understand" colon (`:`) in timezone
1282 timezone = timezone.replace(ALL_COLONS, '');
1283 var requestedTimezoneOffset = Date.parse('Jan 01, 1970 00:00:00 ' + timezone) / 60000;
1284 return isNaN(requestedTimezoneOffset) ? fallback : requestedTimezoneOffset;
1288 function addDateMinutes(date, minutes) {
1289 date = new Date(date.getTime());
1290 date.setMinutes(date.getMinutes() + minutes);
1295 function convertTimezoneToLocal(date, timezone, reverse) {
1296 reverse = reverse ? -1 : 1;
1297 var dateTimezoneOffset = date.getTimezoneOffset();
1298 var timezoneOffset = timezoneToOffset(timezone, dateTimezoneOffset);
1299 return addDateMinutes(date, reverse * (timezoneOffset - dateTimezoneOffset));
1304 * @returns {string} Returns the string representation of the element.
1306 function startingTag(element) {
1307 element = jqLite(element).clone();
1309 // turns out IE does not let you set .html() on elements which
1310 // are not allowed to have children. So we just ignore it.
1313 var elemHtml = jqLite('<div>').append(element).html();
1315 return element[0].nodeType === NODE_TYPE_TEXT ? lowercase(elemHtml) :
1317 match(/^(<[^>]+>)/)[1].
1318 replace(/^<([\w\-]+)/, function(match, nodeName) {return '<' + lowercase(nodeName);});
1320 return lowercase(elemHtml);
1326 /////////////////////////////////////////////////
1329 * Tries to decode the URI component without throwing an exception.
1332 * @param str value potential URI component to check.
1333 * @returns {boolean} True if `value` can be decoded
1334 * with the decodeURIComponent function.
1336 function tryDecodeURIComponent(value) {
1338 return decodeURIComponent(value);
1340 // Ignore any invalid uri component
1346 * Parses an escaped url query string into key-value pairs.
1347 * @returns {Object.<string,boolean|Array>}
1349 function parseKeyValue(/**string*/keyValue) {
1351 forEach((keyValue || "").split('&'), function(keyValue) {
1352 var splitPoint, key, val;
1354 key = keyValue = keyValue.replace(/\+/g,'%20');
1355 splitPoint = keyValue.indexOf('=');
1356 if (splitPoint !== -1) {
1357 key = keyValue.substring(0, splitPoint);
1358 val = keyValue.substring(splitPoint + 1);
1360 key = tryDecodeURIComponent(key);
1361 if (isDefined(key)) {
1362 val = isDefined(val) ? tryDecodeURIComponent(val) : true;
1363 if (!hasOwnProperty.call(obj, key)) {
1365 } else if (isArray(obj[key])) {
1368 obj[key] = [obj[key],val];
1376 function toKeyValue(obj) {
1378 forEach(obj, function(value, key) {
1379 if (isArray(value)) {
1380 forEach(value, function(arrayValue) {
1381 parts.push(encodeUriQuery(key, true) +
1382 (arrayValue === true ? '' : '=' + encodeUriQuery(arrayValue, true)));
1385 parts.push(encodeUriQuery(key, true) +
1386 (value === true ? '' : '=' + encodeUriQuery(value, true)));
1389 return parts.length ? parts.join('&') : '';
1394 * We need our custom method because encodeURIComponent is too aggressive and doesn't follow
1395 * http://www.ietf.org/rfc/rfc3986.txt with regards to the character set (pchar) allowed in path
1398 * pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
1399 * pct-encoded = "%" HEXDIG HEXDIG
1400 * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
1401 * sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
1402 * / "*" / "+" / "," / ";" / "="
1404 function encodeUriSegment(val) {
1405 return encodeUriQuery(val, true).
1406 replace(/%26/gi, '&').
1407 replace(/%3D/gi, '=').
1408 replace(/%2B/gi, '+');
1413 * This method is intended for encoding *key* or *value* parts of query component. We need a custom
1414 * method because encodeURIComponent is too aggressive and encodes stuff that doesn't have to be
1415 * encoded per http://tools.ietf.org/html/rfc3986:
1416 * query = *( pchar / "/" / "?" )
1417 * pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
1418 * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
1419 * pct-encoded = "%" HEXDIG HEXDIG
1420 * sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
1421 * / "*" / "+" / "," / ";" / "="
1423 function encodeUriQuery(val, pctEncodeSpaces) {
1424 return encodeURIComponent(val).
1425 replace(/%40/gi, '@').
1426 replace(/%3A/gi, ':').
1427 replace(/%24/g, '$').
1428 replace(/%2C/gi, ',').
1429 replace(/%3B/gi, ';').
1430 replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
1433 var ngAttrPrefixes = ['ng-', 'data-ng-', 'ng:', 'x-ng-'];
1435 function getNgAttribute(element, ngAttr) {
1436 var attr, i, ii = ngAttrPrefixes.length;
1437 for (i = 0; i < ii; ++i) {
1438 attr = ngAttrPrefixes[i] + ngAttr;
1439 if (isString(attr = element.getAttribute(attr))) {
1452 * @param {angular.Module} ngApp an optional application
1453 * {@link angular.module module} name to load.
1454 * @param {boolean=} ngStrictDi if this attribute is present on the app element, the injector will be
1455 * created in "strict-di" mode. This means that the application will fail to invoke functions which
1456 * do not use explicit function annotation (and are thus unsuitable for minification), as described
1457 * in {@link guide/di the Dependency Injection guide}, and useful debugging info will assist in
1458 * tracking down the root of these bugs.
1462 * Use this directive to **auto-bootstrap** an AngularJS application. The `ngApp` directive
1463 * designates the **root element** of the application and is typically placed near the root element
1464 * of the page - e.g. on the `<body>` or `<html>` tags.
1466 * Only one AngularJS application can be auto-bootstrapped per HTML document. The first `ngApp`
1467 * found in the document will be used to define the root element to auto-bootstrap as an
1468 * application. To run multiple applications in an HTML document you must manually bootstrap them using
1469 * {@link angular.bootstrap} instead. AngularJS applications cannot be nested within each other.
1471 * You can specify an **AngularJS module** to be used as the root module for the application. This
1472 * module will be loaded into the {@link auto.$injector} when the application is bootstrapped. It
1473 * should contain the application code needed or have dependencies on other modules that will
1474 * contain the code. See {@link angular.module} for more information.
1476 * In the example below if the `ngApp` directive were not placed on the `html` element then the
1477 * document would not be compiled, the `AppController` would not be instantiated and the `{{ a+b }}`
1478 * would not be resolved to `3`.
1480 * `ngApp` is the easiest, and most common way to bootstrap an application.
1482 <example module="ngAppDemo">
1483 <file name="index.html">
1484 <div ng-controller="ngAppDemoController">
1485 I can add: {{a}} + {{b}} = {{ a+b }}
1488 <file name="script.js">
1489 angular.module('ngAppDemo', []).controller('ngAppDemoController', function($scope) {
1496 * Using `ngStrictDi`, you would see something like this:
1498 <example ng-app-included="true">
1499 <file name="index.html">
1500 <div ng-app="ngAppStrictDemo" ng-strict-di>
1501 <div ng-controller="GoodController1">
1502 I can add: {{a}} + {{b}} = {{ a+b }}
1504 <p>This renders because the controller does not fail to
1505 instantiate, by using explicit annotation style (see
1506 script.js for details)
1510 <div ng-controller="GoodController2">
1511 Name: <input ng-model="name"><br />
1514 <p>This renders because the controller does not fail to
1515 instantiate, by using explicit annotation style
1516 (see script.js for details)
1520 <div ng-controller="BadController">
1521 I can add: {{a}} + {{b}} = {{ a+b }}
1523 <p>The controller could not be instantiated, due to relying
1524 on automatic function annotations (which are disabled in
1525 strict mode). As such, the content of this section is not
1526 interpolated, and there should be an error in your web console.
1531 <file name="script.js">
1532 angular.module('ngAppStrictDemo', [])
1533 // BadController will fail to instantiate, due to relying on automatic function annotation,
1534 // rather than an explicit annotation
1535 .controller('BadController', function($scope) {
1539 // Unlike BadController, GoodController1 and GoodController2 will not fail to be instantiated,
1540 // due to using explicit annotations using the array style and $inject property, respectively.
1541 .controller('GoodController1', ['$scope', function($scope) {
1545 .controller('GoodController2', GoodController2);
1546 function GoodController2($scope) {
1547 $scope.name = "World";
1549 GoodController2.$inject = ['$scope'];
1551 <file name="style.css">
1552 div[ng-controller] {
1554 -webkit-border-radius: 4px;
1559 div[ng-controller^=Good] {
1560 border-color: #d6e9c6;
1561 background-color: #dff0d8;
1564 div[ng-controller^=Bad] {
1565 border-color: #ebccd1;
1566 background-color: #f2dede;
1573 function angularInit(element, bootstrap) {
1578 // The element `element` has priority over any other element
1579 forEach(ngAttrPrefixes, function(prefix) {
1580 var name = prefix + 'app';
1582 if (!appElement && element.hasAttribute && element.hasAttribute(name)) {
1583 appElement = element;
1584 module = element.getAttribute(name);
1587 forEach(ngAttrPrefixes, function(prefix) {
1588 var name = prefix + 'app';
1591 if (!appElement && (candidate = element.querySelector('[' + name.replace(':', '\\:') + ']'))) {
1592 appElement = candidate;
1593 module = candidate.getAttribute(name);
1597 config.strictDi = getNgAttribute(appElement, "strict-di") !== null;
1598 bootstrap(appElement, module ? [module] : [], config);
1604 * @name angular.bootstrap
1607 * Use this function to manually start up angular application.
1609 * See: {@link guide/bootstrap Bootstrap}
1611 * Note that Protractor based end-to-end tests cannot use this function to bootstrap manually.
1612 * They must use {@link ng.directive:ngApp ngApp}.
1614 * Angular will detect if it has been loaded into the browser more than once and only allow the
1615 * first loaded script to be bootstrapped and will report a warning to the browser console for
1616 * each of the subsequent scripts. This prevents strange results in applications, where otherwise
1617 * multiple instances of Angular try to work on the DOM.
1623 * <div ng-controller="WelcomeController">
1627 * <script src="angular.js"></script>
1629 * var app = angular.module('demo', [])
1630 * .controller('WelcomeController', function($scope) {
1631 * $scope.greeting = 'Welcome!';
1633 * angular.bootstrap(document, ['demo']);
1639 * @param {DOMElement} element DOM element which is the root of angular application.
1640 * @param {Array<String|Function|Array>=} modules an array of modules to load into the application.
1641 * Each item in the array should be the name of a predefined module or a (DI annotated)
1642 * function that will be invoked by the injector as a `config` block.
1643 * See: {@link angular.module modules}
1644 * @param {Object=} config an object for defining configuration options for the application. The
1645 * following keys are supported:
1647 * * `strictDi` - disable automatic function annotation for the application. This is meant to
1648 * assist in finding bugs which break minified code. Defaults to `false`.
1650 * @returns {auto.$injector} Returns the newly created injector for this app.
1652 function bootstrap(element, modules, config) {
1653 if (!isObject(config)) config = {};
1654 var defaultConfig = {
1657 config = extend(defaultConfig, config);
1658 var doBootstrap = function() {
1659 element = jqLite(element);
1661 if (element.injector()) {
1662 var tag = (element[0] === document) ? 'document' : startingTag(element);
1663 //Encode angle brackets to prevent input from being sanitized to empty string #8683
1666 "App Already Bootstrapped with this Element '{0}'",
1667 tag.replace(/</,'<').replace(/>/,'>'));
1670 modules = modules || [];
1671 modules.unshift(['$provide', function($provide) {
1672 $provide.value('$rootElement', element);
1675 if (config.debugInfoEnabled) {
1676 // Pushing so that this overrides `debugInfoEnabled` setting defined in user's `modules`.
1677 modules.push(['$compileProvider', function($compileProvider) {
1678 $compileProvider.debugInfoEnabled(true);
1682 modules.unshift('ng');
1683 var injector = createInjector(modules, config.strictDi);
1684 injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector',
1685 function bootstrapApply(scope, element, compile, injector) {
1686 scope.$apply(function() {
1687 element.data('$injector', injector);
1688 compile(element)(scope);
1695 var NG_ENABLE_DEBUG_INFO = /^NG_ENABLE_DEBUG_INFO!/;
1696 var NG_DEFER_BOOTSTRAP = /^NG_DEFER_BOOTSTRAP!/;
1698 if (window && NG_ENABLE_DEBUG_INFO.test(window.name)) {
1699 config.debugInfoEnabled = true;
1700 window.name = window.name.replace(NG_ENABLE_DEBUG_INFO, '');
1703 if (window && !NG_DEFER_BOOTSTRAP.test(window.name)) {
1704 return doBootstrap();
1707 window.name = window.name.replace(NG_DEFER_BOOTSTRAP, '');
1708 angular.resumeBootstrap = function(extraModules) {
1709 forEach(extraModules, function(module) {
1710 modules.push(module);
1712 return doBootstrap();
1715 if (isFunction(angular.resumeDeferredBootstrap)) {
1716 angular.resumeDeferredBootstrap();
1722 * @name angular.reloadWithDebugInfo
1725 * Use this function to reload the current application with debug information turned on.
1726 * This takes precedence over a call to `$compileProvider.debugInfoEnabled(false)`.
1728 * See {@link ng.$compileProvider#debugInfoEnabled} for more.
1730 function reloadWithDebugInfo() {
1731 window.name = 'NG_ENABLE_DEBUG_INFO!' + window.name;
1732 window.location.reload();
1736 * @name angular.getTestability
1739 * Get the testability service for the instance of Angular on the given
1741 * @param {DOMElement} element DOM element which is the root of angular application.
1743 function getTestability(rootElement) {
1744 var injector = angular.element(rootElement).injector();
1746 throw ngMinErr('test',
1747 'no injector found for element argument to getTestability');
1749 return injector.get('$$testability');
1752 var SNAKE_CASE_REGEXP = /[A-Z]/g;
1753 function snake_case(name, separator) {
1754 separator = separator || '_';
1755 return name.replace(SNAKE_CASE_REGEXP, function(letter, pos) {
1756 return (pos ? separator : '') + letter.toLowerCase();
1760 var bindJQueryFired = false;
1761 var skipDestroyOnNextJQueryCleanData;
1762 function bindJQuery() {
1763 var originalCleanData;
1765 if (bindJQueryFired) {
1769 // bind to jQuery if present;
1771 jQuery = isUndefined(jqName) ? window.jQuery : // use jQuery (if present)
1772 !jqName ? undefined : // use jqLite
1773 window[jqName]; // use jQuery specified by `ngJq`
1775 // Use jQuery if it exists with proper functionality, otherwise default to us.
1776 // Angular 1.2+ requires jQuery 1.7+ for on()/off() support.
1777 // Angular 1.3+ technically requires at least jQuery 2.1+ but it may work with older
1778 // versions. It will not work for sure with jQuery <1.7, though.
1779 if (jQuery && jQuery.fn.on) {
1782 scope: JQLitePrototype.scope,
1783 isolateScope: JQLitePrototype.isolateScope,
1784 controller: JQLitePrototype.controller,
1785 injector: JQLitePrototype.injector,
1786 inheritedData: JQLitePrototype.inheritedData
1789 // All nodes removed from the DOM via various jQuery APIs like .remove()
1790 // are passed through jQuery.cleanData. Monkey-patch this method to fire
1791 // the $destroy event on all removed nodes.
1792 originalCleanData = jQuery.cleanData;
1793 jQuery.cleanData = function(elems) {
1795 if (!skipDestroyOnNextJQueryCleanData) {
1796 for (var i = 0, elem; (elem = elems[i]) != null; i++) {
1797 events = jQuery._data(elem, "events");
1798 if (events && events.$destroy) {
1799 jQuery(elem).triggerHandler('$destroy');
1803 skipDestroyOnNextJQueryCleanData = false;
1805 originalCleanData(elems);
1811 angular.element = jqLite;
1813 // Prevent double-proxying.
1814 bindJQueryFired = true;
1818 * throw error if the argument is falsy.
1820 function assertArg(arg, name, reason) {
1822 throw ngMinErr('areq', "Argument '{0}' is {1}", (name || '?'), (reason || "required"));
1827 function assertArgFn(arg, name, acceptArrayAnnotation) {
1828 if (acceptArrayAnnotation && isArray(arg)) {
1829 arg = arg[arg.length - 1];
1832 assertArg(isFunction(arg), name, 'not a function, got ' +
1833 (arg && typeof arg === 'object' ? arg.constructor.name || 'Object' : typeof arg));
1838 * throw error if the name given is hasOwnProperty
1839 * @param {String} name the name to test
1840 * @param {String} context the context in which the name is used, such as module or directive
1842 function assertNotHasOwnProperty(name, context) {
1843 if (name === 'hasOwnProperty') {
1844 throw ngMinErr('badname', "hasOwnProperty is not a valid {0} name", context);
1849 * Return the value accessible from the object by path. Any undefined traversals are ignored
1850 * @param {Object} obj starting object
1851 * @param {String} path path to traverse
1852 * @param {boolean} [bindFnToScope=true]
1853 * @returns {Object} value as accessible by path
1855 //TODO(misko): this function needs to be removed
1856 function getter(obj, path, bindFnToScope) {
1857 if (!path) return obj;
1858 var keys = path.split('.');
1860 var lastInstance = obj;
1861 var len = keys.length;
1863 for (var i = 0; i < len; i++) {
1866 obj = (lastInstance = obj)[key];
1869 if (!bindFnToScope && isFunction(obj)) {
1870 return bind(lastInstance, obj);
1876 * Return the DOM siblings between the first and last node in the given array.
1877 * @param {Array} array like object
1878 * @returns {Array} the inputted object or a jqLite collection containing the nodes
1880 function getBlockNodes(nodes) {
1881 // TODO(perf): update `nodes` instead of creating a new object?
1882 var node = nodes[0];
1883 var endNode = nodes[nodes.length - 1];
1886 for (var i = 1; node !== endNode && (node = node.nextSibling); i++) {
1887 if (blockNodes || nodes[i] !== node) {
1889 blockNodes = jqLite(slice.call(nodes, 0, i));
1891 blockNodes.push(node);
1895 return blockNodes || nodes;
1900 * Creates a new object without a prototype. This object is useful for lookup without having to
1901 * guard against prototypically inherited properties via hasOwnProperty.
1903 * Related micro-benchmarks:
1904 * - http://jsperf.com/object-create2
1905 * - http://jsperf.com/proto-map-lookup/2
1906 * - http://jsperf.com/for-in-vs-object-keys2
1910 function createMap() {
1911 return Object.create(null);
1914 var NODE_TYPE_ELEMENT = 1;
1915 var NODE_TYPE_ATTRIBUTE = 2;
1916 var NODE_TYPE_TEXT = 3;
1917 var NODE_TYPE_COMMENT = 8;
1918 var NODE_TYPE_DOCUMENT = 9;
1919 var NODE_TYPE_DOCUMENT_FRAGMENT = 11;
1923 * @name angular.Module
1927 * Interface for configuring angular {@link angular.module modules}.
1930 function setupModuleLoader(window) {
1932 var $injectorMinErr = minErr('$injector');
1933 var ngMinErr = minErr('ng');
1935 function ensure(obj, name, factory) {
1936 return obj[name] || (obj[name] = factory());
1939 var angular = ensure(window, 'angular', Object);
1941 // We need to expose `angular.$$minErr` to modules such as `ngResource` that reference it during bootstrap
1942 angular.$$minErr = angular.$$minErr || minErr;
1944 return ensure(angular, 'module', function() {
1945 /** @type {Object.<string, angular.Module>} */
1950 * @name angular.module
1954 * The `angular.module` is a global place for creating, registering and retrieving Angular
1956 * All modules (angular core or 3rd party) that should be available to an application must be
1957 * registered using this mechanism.
1959 * Passing one argument retrieves an existing {@link angular.Module},
1960 * whereas passing more than one argument creates a new {@link angular.Module}
1965 * A module is a collection of services, directives, controllers, filters, and configuration information.
1966 * `angular.module` is used to configure the {@link auto.$injector $injector}.
1969 * // Create a new module
1970 * var myModule = angular.module('myModule', []);
1972 * // register a new service
1973 * myModule.value('appName', 'MyCoolApp');
1975 * // configure existing services inside initialization blocks.
1976 * myModule.config(['$locationProvider', function($locationProvider) {
1977 * // Configure existing providers
1978 * $locationProvider.hashPrefix('!');
1982 * Then you can create an injector and load your modules like this:
1985 * var injector = angular.injector(['ng', 'myModule'])
1988 * However it's more likely that you'll just use
1989 * {@link ng.directive:ngApp ngApp} or
1990 * {@link angular.bootstrap} to simplify this process for you.
1992 * @param {!string} name The name of the module to create or retrieve.
1993 * @param {!Array.<string>=} requires If specified then new module is being created. If
1994 * unspecified then the module is being retrieved for further configuration.
1995 * @param {Function=} configFn Optional configuration function for the module. Same as
1996 * {@link angular.Module#config Module#config()}.
1997 * @returns {angular.Module} new module with the {@link angular.Module} api.
1999 return function module(name, requires, configFn) {
2000 var assertNotHasOwnProperty = function(name, context) {
2001 if (name === 'hasOwnProperty') {
2002 throw ngMinErr('badname', 'hasOwnProperty is not a valid {0} name', context);
2006 assertNotHasOwnProperty(name, 'module');
2007 if (requires && modules.hasOwnProperty(name)) {
2008 modules[name] = null;
2010 return ensure(modules, name, function() {
2012 throw $injectorMinErr('nomod', "Module '{0}' is not available! You either misspelled " +
2013 "the module name or forgot to load it. If registering a module ensure that you " +
2014 "specify the dependencies as the second argument.", name);
2017 /** @type {!Array.<Array.<*>>} */
2018 var invokeQueue = [];
2020 /** @type {!Array.<Function>} */
2021 var configBlocks = [];
2023 /** @type {!Array.<Function>} */
2026 var config = invokeLater('$injector', 'invoke', 'push', configBlocks);
2028 /** @type {angular.Module} */
2029 var moduleInstance = {
2031 _invokeQueue: invokeQueue,
2032 _configBlocks: configBlocks,
2033 _runBlocks: runBlocks,
2037 * @name angular.Module#requires
2041 * Holds the list of modules which the injector will load before the current module is
2048 * @name angular.Module#name
2052 * Name of the module.
2059 * @name angular.Module#provider
2061 * @param {string} name service name
2062 * @param {Function} providerType Construction function for creating new instance of the
2065 * See {@link auto.$provide#provider $provide.provider()}.
2067 provider: invokeLaterAndSetModuleName('$provide', 'provider'),
2071 * @name angular.Module#factory
2073 * @param {string} name service name
2074 * @param {Function} providerFunction Function for creating new instance of the service.
2076 * See {@link auto.$provide#factory $provide.factory()}.
2078 factory: invokeLaterAndSetModuleName('$provide', 'factory'),
2082 * @name angular.Module#service
2084 * @param {string} name service name
2085 * @param {Function} constructor A constructor function that will be instantiated.
2087 * See {@link auto.$provide#service $provide.service()}.
2089 service: invokeLaterAndSetModuleName('$provide', 'service'),
2093 * @name angular.Module#value
2095 * @param {string} name service name
2096 * @param {*} object Service instance object.
2098 * See {@link auto.$provide#value $provide.value()}.
2100 value: invokeLater('$provide', 'value'),
2104 * @name angular.Module#constant
2106 * @param {string} name constant name
2107 * @param {*} object Constant value.
2109 * Because the constants are fixed, they get applied before other provide methods.
2110 * See {@link auto.$provide#constant $provide.constant()}.
2112 constant: invokeLater('$provide', 'constant', 'unshift'),
2116 * @name angular.Module#decorator
2118 * @param {string} The name of the service to decorate.
2119 * @param {Function} This function will be invoked when the service needs to be
2120 * instantiated and should return the decorated service instance.
2122 * See {@link auto.$provide#decorator $provide.decorator()}.
2124 decorator: invokeLaterAndSetModuleName('$provide', 'decorator'),
2128 * @name angular.Module#animation
2130 * @param {string} name animation name
2131 * @param {Function} animationFactory Factory function for creating new instance of an
2135 * **NOTE**: animations take effect only if the **ngAnimate** module is loaded.
2138 * Defines an animation hook that can be later used with
2139 * {@link $animate $animate} service and directives that use this service.
2142 * module.animation('.animation-name', function($inject1, $inject2) {
2144 * eventName : function(element, done) {
2145 * //code to run the animation
2146 * //once complete, then run done()
2147 * return function cancellationFunction(element) {
2148 * //code to cancel the animation
2155 * See {@link ng.$animateProvider#register $animateProvider.register()} and
2156 * {@link ngAnimate ngAnimate module} for more information.
2158 animation: invokeLaterAndSetModuleName('$animateProvider', 'register'),
2162 * @name angular.Module#filter
2164 * @param {string} name Filter name - this must be a valid angular expression identifier
2165 * @param {Function} filterFactory Factory function for creating new instance of filter.
2167 * See {@link ng.$filterProvider#register $filterProvider.register()}.
2169 * <div class="alert alert-warning">
2170 * **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`.
2171 * Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace
2172 * your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
2173 * (`myapp_subsection_filterx`).
2176 filter: invokeLaterAndSetModuleName('$filterProvider', 'register'),
2180 * @name angular.Module#controller
2182 * @param {string|Object} name Controller name, or an object map of controllers where the
2183 * keys are the names and the values are the constructors.
2184 * @param {Function} constructor Controller constructor function.
2186 * See {@link ng.$controllerProvider#register $controllerProvider.register()}.
2188 controller: invokeLaterAndSetModuleName('$controllerProvider', 'register'),
2192 * @name angular.Module#directive
2194 * @param {string|Object} name Directive name, or an object map of directives where the
2195 * keys are the names and the values are the factories.
2196 * @param {Function} directiveFactory Factory function for creating new instance of
2199 * See {@link ng.$compileProvider#directive $compileProvider.directive()}.
2201 directive: invokeLaterAndSetModuleName('$compileProvider', 'directive'),
2205 * @name angular.Module#config
2207 * @param {Function} configFn Execute this function on module load. Useful for service
2210 * Use this method to register work which needs to be performed on module loading.
2211 * For more about how to configure services, see
2212 * {@link providers#provider-recipe Provider Recipe}.
2218 * @name angular.Module#run
2220 * @param {Function} initializationFn Execute this function after injector creation.
2221 * Useful for application initialization.
2223 * Use this method to register work which should be performed when the injector is done
2224 * loading all modules.
2226 run: function(block) {
2227 runBlocks.push(block);
2236 return moduleInstance;
2239 * @param {string} provider
2240 * @param {string} method
2241 * @param {String=} insertMethod
2242 * @returns {angular.Module}
2244 function invokeLater(provider, method, insertMethod, queue) {
2245 if (!queue) queue = invokeQueue;
2247 queue[insertMethod || 'push']([provider, method, arguments]);
2248 return moduleInstance;
2253 * @param {string} provider
2254 * @param {string} method
2255 * @returns {angular.Module}
2257 function invokeLaterAndSetModuleName(provider, method) {
2258 return function(recipeName, factoryFunction) {
2259 if (factoryFunction && isFunction(factoryFunction)) factoryFunction.$$moduleName = name;
2260 invokeQueue.push([provider, method, arguments]);
2261 return moduleInstance;
2270 /* global: toDebugString: true */
2272 function serializeObject(obj) {
2275 return JSON.stringify(obj, function(key, val) {
2276 val = toJsonReplacer(key, val);
2277 if (isObject(val)) {
2279 if (seen.indexOf(val) >= 0) return '...';
2287 function toDebugString(obj) {
2288 if (typeof obj === 'function') {
2289 return obj.toString().replace(/ \{[\s\S]*$/, '');
2290 } else if (isUndefined(obj)) {
2292 } else if (typeof obj !== 'string') {
2293 return serializeObject(obj);
2298 /* global angularModule: true,
2303 htmlAnchorDirective,
2312 ngBindHtmlDirective,
2313 ngBindTemplateDirective,
2315 ngClassEvenDirective,
2316 ngClassOddDirective,
2318 ngControllerDirective,
2323 ngIncludeFillContentDirective,
2325 ngNonBindableDirective,
2326 ngPluralizeDirective,
2331 ngSwitchWhenDirective,
2332 ngSwitchDefaultDirective,
2334 ngTranscludeDirective,
2347 ngModelOptionsDirective,
2348 ngAttributeAliasDirectives,
2351 $AnchorScrollProvider,
2353 $CoreAnimateCssProvider,
2354 $$CoreAnimateJsProvider,
2355 $$CoreAnimateQueueProvider,
2356 $$AnimateRunnerFactoryProvider,
2357 $$AnimateAsyncRunFactoryProvider,
2359 $CacheFactoryProvider,
2360 $ControllerProvider,
2362 $ExceptionHandlerProvider,
2364 $$ForceReflowProvider,
2365 $InterpolateProvider,
2369 $HttpParamSerializerProvider,
2370 $HttpParamSerializerJQLikeProvider,
2371 $HttpBackendProvider,
2372 $xhrFactoryProvider,
2379 $$SanitizeUriProvider,
2381 $SceDelegateProvider,
2383 $TemplateCacheProvider,
2384 $TemplateRequestProvider,
2385 $$TestabilityProvider,
2390 $$CookieReaderProvider
2396 * @name angular.version
2399 * An object that contains information about the current AngularJS version.
2401 * This object has the following properties:
2403 * - `full` – `{string}` – Full version string, such as "0.9.18".
2404 * - `major` – `{number}` – Major version number, such as "0".
2405 * - `minor` – `{number}` – Minor version number, such as "9".
2406 * - `dot` – `{number}` – Dot version number, such as "18".
2407 * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat".
2410 full: '1.4.10', // all of these placeholder strings will be replaced by grunt's
2411 major: 1, // package task
2414 codeName: 'benignant-oscillation'
2418 function publishExternalAPI(angular) {
2420 'bootstrap': bootstrap,
2427 'injector': createInjector,
2431 'fromJson': fromJson,
2432 'identity': identity,
2433 'isUndefined': isUndefined,
2434 'isDefined': isDefined,
2435 'isString': isString,
2436 'isFunction': isFunction,
2437 'isObject': isObject,
2438 'isNumber': isNumber,
2439 'isElement': isElement,
2443 'lowercase': lowercase,
2444 'uppercase': uppercase,
2445 'callbacks': {counter: 0},
2446 'getTestability': getTestability,
2449 'reloadWithDebugInfo': reloadWithDebugInfo
2452 angularModule = setupModuleLoader(window);
2454 angularModule('ng', ['ngLocale'], ['$provide',
2455 function ngModule($provide) {
2456 // $$sanitizeUriProvider needs to be before $compileProvider as it is used by it.
2458 $$sanitizeUri: $$SanitizeUriProvider
2460 $provide.provider('$compile', $CompileProvider).
2462 a: htmlAnchorDirective,
2463 input: inputDirective,
2464 textarea: inputDirective,
2465 form: formDirective,
2466 script: scriptDirective,
2467 select: selectDirective,
2468 style: styleDirective,
2469 option: optionDirective,
2470 ngBind: ngBindDirective,
2471 ngBindHtml: ngBindHtmlDirective,
2472 ngBindTemplate: ngBindTemplateDirective,
2473 ngClass: ngClassDirective,
2474 ngClassEven: ngClassEvenDirective,
2475 ngClassOdd: ngClassOddDirective,
2476 ngCloak: ngCloakDirective,
2477 ngController: ngControllerDirective,
2478 ngForm: ngFormDirective,
2479 ngHide: ngHideDirective,
2480 ngIf: ngIfDirective,
2481 ngInclude: ngIncludeDirective,
2482 ngInit: ngInitDirective,
2483 ngNonBindable: ngNonBindableDirective,
2484 ngPluralize: ngPluralizeDirective,
2485 ngRepeat: ngRepeatDirective,
2486 ngShow: ngShowDirective,
2487 ngStyle: ngStyleDirective,
2488 ngSwitch: ngSwitchDirective,
2489 ngSwitchWhen: ngSwitchWhenDirective,
2490 ngSwitchDefault: ngSwitchDefaultDirective,
2491 ngOptions: ngOptionsDirective,
2492 ngTransclude: ngTranscludeDirective,
2493 ngModel: ngModelDirective,
2494 ngList: ngListDirective,
2495 ngChange: ngChangeDirective,
2496 pattern: patternDirective,
2497 ngPattern: patternDirective,
2498 required: requiredDirective,
2499 ngRequired: requiredDirective,
2500 minlength: minlengthDirective,
2501 ngMinlength: minlengthDirective,
2502 maxlength: maxlengthDirective,
2503 ngMaxlength: maxlengthDirective,
2504 ngValue: ngValueDirective,
2505 ngModelOptions: ngModelOptionsDirective
2508 ngInclude: ngIncludeFillContentDirective
2510 directive(ngAttributeAliasDirectives).
2511 directive(ngEventDirectives);
2513 $anchorScroll: $AnchorScrollProvider,
2514 $animate: $AnimateProvider,
2515 $animateCss: $CoreAnimateCssProvider,
2516 $$animateJs: $$CoreAnimateJsProvider,
2517 $$animateQueue: $$CoreAnimateQueueProvider,
2518 $$AnimateRunner: $$AnimateRunnerFactoryProvider,
2519 $$animateAsyncRun: $$AnimateAsyncRunFactoryProvider,
2520 $browser: $BrowserProvider,
2521 $cacheFactory: $CacheFactoryProvider,
2522 $controller: $ControllerProvider,
2523 $document: $DocumentProvider,
2524 $exceptionHandler: $ExceptionHandlerProvider,
2525 $filter: $FilterProvider,
2526 $$forceReflow: $$ForceReflowProvider,
2527 $interpolate: $InterpolateProvider,
2528 $interval: $IntervalProvider,
2529 $http: $HttpProvider,
2530 $httpParamSerializer: $HttpParamSerializerProvider,
2531 $httpParamSerializerJQLike: $HttpParamSerializerJQLikeProvider,
2532 $httpBackend: $HttpBackendProvider,
2533 $xhrFactory: $xhrFactoryProvider,
2534 $location: $LocationProvider,
2536 $parse: $ParseProvider,
2537 $rootScope: $RootScopeProvider,
2541 $sceDelegate: $SceDelegateProvider,
2542 $sniffer: $SnifferProvider,
2543 $templateCache: $TemplateCacheProvider,
2544 $templateRequest: $TemplateRequestProvider,
2545 $$testability: $$TestabilityProvider,
2546 $timeout: $TimeoutProvider,
2547 $window: $WindowProvider,
2548 $$rAF: $$RAFProvider,
2549 $$jqLite: $$jqLiteProvider,
2550 $$HashMap: $$HashMapProvider,
2551 $$cookieReader: $$CookieReaderProvider
2557 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2558 * Any commits to this file should be reviewed with security in mind. *
2559 * Changes to this file can potentially create security vulnerabilities. *
2560 * An approval from 2 Core members with history of modifying *
2561 * this file is required. *
2563 * Does the change somehow allow for arbitrary javascript to be executed? *
2564 * Or allows for someone to change the prototype of built-in objects? *
2565 * Or gives undesired access to variables likes document or window? *
2566 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2568 /* global JQLitePrototype: true,
2569 addEventListenerFn: true,
2570 removeEventListenerFn: true,
2575 //////////////////////////////////
2577 //////////////////////////////////
2581 * @name angular.element
2586 * Wraps a raw DOM element or HTML string as a [jQuery](http://jquery.com) element.
2588 * If jQuery is available, `angular.element` is an alias for the
2589 * [jQuery](http://api.jquery.com/jQuery/) function. If jQuery is not available, `angular.element`
2590 * delegates to Angular's built-in subset of jQuery, called "jQuery lite" or **jqLite**.
2592 * jqLite is a tiny, API-compatible subset of jQuery that allows
2593 * Angular to manipulate the DOM in a cross-browser compatible way. jqLite implements only the most
2594 * commonly needed functionality with the goal of having a very small footprint.
2596 * To use `jQuery`, simply ensure it is loaded before the `angular.js` file. You can also use the
2597 * {@link ngJq `ngJq`} directive to specify that jqlite should be used over jQuery, or to use a
2598 * specific version of jQuery if multiple versions exist on the page.
2600 * <div class="alert alert-info">**Note:** All element references in Angular are always wrapped with jQuery or
2601 * jqLite (such as the element argument in a directive's compile / link function). They are never raw DOM references.</div>
2603 * <div class="alert alert-warning">**Note:** Keep in mind that this function will not find elements
2604 * by tag name / CSS selector. For lookups by tag name, try instead `angular.element(document).find(...)`
2605 * or `$document.find()`, or use the standard DOM APIs, e.g. `document.querySelectorAll()`.</div>
2607 * ## Angular's jqLite
2608 * jqLite provides only the following jQuery methods:
2610 * - [`addClass()`](http://api.jquery.com/addClass/)
2611 * - [`after()`](http://api.jquery.com/after/)
2612 * - [`append()`](http://api.jquery.com/append/)
2613 * - [`attr()`](http://api.jquery.com/attr/) - Does not support functions as parameters
2614 * - [`bind()`](http://api.jquery.com/bind/) - Does not support namespaces, selectors or eventData
2615 * - [`children()`](http://api.jquery.com/children/) - Does not support selectors
2616 * - [`clone()`](http://api.jquery.com/clone/)
2617 * - [`contents()`](http://api.jquery.com/contents/)
2618 * - [`css()`](http://api.jquery.com/css/) - Only retrieves inline-styles, does not call `getComputedStyle()`.
2619 * As a setter, does not convert numbers to strings or append 'px', and also does not have automatic property prefixing.
2620 * - [`data()`](http://api.jquery.com/data/)
2621 * - [`detach()`](http://api.jquery.com/detach/)
2622 * - [`empty()`](http://api.jquery.com/empty/)
2623 * - [`eq()`](http://api.jquery.com/eq/)
2624 * - [`find()`](http://api.jquery.com/find/) - Limited to lookups by tag name
2625 * - [`hasClass()`](http://api.jquery.com/hasClass/)
2626 * - [`html()`](http://api.jquery.com/html/)
2627 * - [`next()`](http://api.jquery.com/next/) - Does not support selectors
2628 * - [`on()`](http://api.jquery.com/on/) - Does not support namespaces, selectors or eventData
2629 * - [`off()`](http://api.jquery.com/off/) - Does not support namespaces, selectors or event object as parameter
2630 * - [`one()`](http://api.jquery.com/one/) - Does not support namespaces or selectors
2631 * - [`parent()`](http://api.jquery.com/parent/) - Does not support selectors
2632 * - [`prepend()`](http://api.jquery.com/prepend/)
2633 * - [`prop()`](http://api.jquery.com/prop/)
2634 * - [`ready()`](http://api.jquery.com/ready/)
2635 * - [`remove()`](http://api.jquery.com/remove/)
2636 * - [`removeAttr()`](http://api.jquery.com/removeAttr/)
2637 * - [`removeClass()`](http://api.jquery.com/removeClass/)
2638 * - [`removeData()`](http://api.jquery.com/removeData/)
2639 * - [`replaceWith()`](http://api.jquery.com/replaceWith/)
2640 * - [`text()`](http://api.jquery.com/text/)
2641 * - [`toggleClass()`](http://api.jquery.com/toggleClass/)
2642 * - [`triggerHandler()`](http://api.jquery.com/triggerHandler/) - Passes a dummy event object to handlers.
2643 * - [`unbind()`](http://api.jquery.com/unbind/) - Does not support namespaces or event object as parameter
2644 * - [`val()`](http://api.jquery.com/val/)
2645 * - [`wrap()`](http://api.jquery.com/wrap/)
2647 * ## jQuery/jqLite Extras
2648 * Angular also provides the following additional methods and events to both jQuery and jqLite:
2651 * - `$destroy` - AngularJS intercepts all jqLite/jQuery's DOM destruction apis and fires this event
2652 * on all DOM nodes being removed. This can be used to clean up any 3rd party bindings to the DOM
2653 * element before it is removed.
2656 * - `controller(name)` - retrieves the controller of the current element or its parent. By default
2657 * retrieves controller associated with the `ngController` directive. If `name` is provided as
2658 * camelCase directive name, then the controller for this directive will be retrieved (e.g.
2660 * - `injector()` - retrieves the injector of the current element or its parent.
2661 * - `scope()` - retrieves the {@link ng.$rootScope.Scope scope} of the current
2662 * element or its parent. Requires {@link guide/production#disabling-debug-data Debug Data} to
2664 * - `isolateScope()` - retrieves an isolate {@link ng.$rootScope.Scope scope} if one is attached directly to the
2665 * current element. This getter should be used only on elements that contain a directive which starts a new isolate
2666 * scope. Calling `scope()` on this element always returns the original non-isolate scope.
2667 * Requires {@link guide/production#disabling-debug-data Debug Data} to be enabled.
2668 * - `inheritedData()` - same as `data()`, but walks up the DOM until a value is found or the top
2669 * parent element is reached.
2671 * @param {string|DOMElement} element HTML string or DOMElement to be wrapped into jQuery.
2672 * @returns {Object} jQuery object.
2675 JQLite.expando = 'ng339';
2677 var jqCache = JQLite.cache = {},
2679 addEventListenerFn = function(element, type, fn) {
2680 element.addEventListener(type, fn, false);
2682 removeEventListenerFn = function(element, type, fn) {
2683 element.removeEventListener(type, fn, false);
2687 * !!! This is an undocumented "private" function !!!
2689 JQLite._data = function(node) {
2690 //jQuery always returns an object on cache miss
2691 return this.cache[node[this.expando]] || {};
2694 function jqNextId() { return ++jqId; }
2697 var SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g;
2698 var MOZ_HACK_REGEXP = /^moz([A-Z])/;
2699 var MOUSE_EVENT_MAP= { mouseleave: "mouseout", mouseenter: "mouseover"};
2700 var jqLiteMinErr = minErr('jqLite');
2703 * Converts snake_case to camelCase.
2704 * Also there is special case for Moz prefix starting with upper case letter.
2705 * @param name Name to normalize
2707 function camelCase(name) {
2709 replace(SPECIAL_CHARS_REGEXP, function(_, separator, letter, offset) {
2710 return offset ? letter.toUpperCase() : letter;
2712 replace(MOZ_HACK_REGEXP, 'Moz$1');
2715 var SINGLE_TAG_REGEXP = /^<([\w-]+)\s*\/?>(?:<\/\1>|)$/;
2716 var HTML_REGEXP = /<|&#?\w+;/;
2717 var TAG_NAME_REGEXP = /<([\w:-]+)/;
2718 var XHTML_TAG_REGEXP = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^>]*)\/>/gi;
2721 'option': [1, '<select multiple="multiple">', '</select>'],
2723 'thead': [1, '<table>', '</table>'],
2724 'col': [2, '<table><colgroup>', '</colgroup></table>'],
2725 'tr': [2, '<table><tbody>', '</tbody></table>'],
2726 'td': [3, '<table><tbody><tr>', '</tr></tbody></table>'],
2727 '_default': [0, "", ""]
2730 wrapMap.optgroup = wrapMap.option;
2731 wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
2732 wrapMap.th = wrapMap.td;
2735 function jqLiteIsTextNode(html) {
2736 return !HTML_REGEXP.test(html);
2739 function jqLiteAcceptsData(node) {
2740 // The window object can accept data but has no nodeType
2741 // Otherwise we are only interested in elements (1) and documents (9)
2742 var nodeType = node.nodeType;
2743 return nodeType === NODE_TYPE_ELEMENT || !nodeType || nodeType === NODE_TYPE_DOCUMENT;
2746 function jqLiteHasData(node) {
2747 for (var key in jqCache[node.ng339]) {
2753 function jqLiteBuildFragment(html, context) {
2755 fragment = context.createDocumentFragment(),
2758 if (jqLiteIsTextNode(html)) {
2759 // Convert non-html into a text node
2760 nodes.push(context.createTextNode(html));
2762 // Convert html into DOM nodes
2763 tmp = tmp || fragment.appendChild(context.createElement("div"));
2764 tag = (TAG_NAME_REGEXP.exec(html) || ["", ""])[1].toLowerCase();
2765 wrap = wrapMap[tag] || wrapMap._default;
2766 tmp.innerHTML = wrap[1] + html.replace(XHTML_TAG_REGEXP, "<$1></$2>") + wrap[2];
2768 // Descend through wrappers to the right content
2771 tmp = tmp.lastChild;
2774 nodes = concat(nodes, tmp.childNodes);
2776 tmp = fragment.firstChild;
2777 tmp.textContent = "";
2780 // Remove wrapper from fragment
2781 fragment.textContent = "";
2782 fragment.innerHTML = ""; // Clear inner HTML
2783 forEach(nodes, function(node) {
2784 fragment.appendChild(node);
2790 function jqLiteParseHTML(html, context) {
2791 context = context || document;
2794 if ((parsed = SINGLE_TAG_REGEXP.exec(html))) {
2795 return [context.createElement(parsed[1])];
2798 if ((parsed = jqLiteBuildFragment(html, context))) {
2799 return parsed.childNodes;
2805 function jqLiteWrapNode(node, wrapper) {
2806 var parent = node.parentNode;
2809 parent.replaceChild(wrapper, node);
2812 wrapper.appendChild(node);
2816 // IE9-11 has no method "contains" in SVG element and in Node.prototype. Bug #10259.
2817 var jqLiteContains = Node.prototype.contains || function(arg) {
2818 // jshint bitwise: false
2819 return !!(this.compareDocumentPosition(arg) & 16);
2820 // jshint bitwise: true
2823 /////////////////////////////////////////////
2824 function JQLite(element) {
2825 if (element instanceof JQLite) {
2831 if (isString(element)) {
2832 element = trim(element);
2835 if (!(this instanceof JQLite)) {
2836 if (argIsString && element.charAt(0) != '<') {
2837 throw jqLiteMinErr('nosel', 'Looking up elements via selectors is not supported by jqLite! See: http://docs.angularjs.org/api/angular.element');
2839 return new JQLite(element);
2843 jqLiteAddNodes(this, jqLiteParseHTML(element));
2845 jqLiteAddNodes(this, element);
2849 function jqLiteClone(element) {
2850 return element.cloneNode(true);
2853 function jqLiteDealoc(element, onlyDescendants) {
2854 if (!onlyDescendants) jqLiteRemoveData(element);
2856 if (element.querySelectorAll) {
2857 var descendants = element.querySelectorAll('*');
2858 for (var i = 0, l = descendants.length; i < l; i++) {
2859 jqLiteRemoveData(descendants[i]);
2864 function jqLiteOff(element, type, fn, unsupported) {
2865 if (isDefined(unsupported)) throw jqLiteMinErr('offargs', 'jqLite#off() does not support the `selector` argument');
2867 var expandoStore = jqLiteExpandoStore(element);
2868 var events = expandoStore && expandoStore.events;
2869 var handle = expandoStore && expandoStore.handle;
2871 if (!handle) return; //no listeners registered
2874 for (type in events) {
2875 if (type !== '$destroy') {
2876 removeEventListenerFn(element, type, handle);
2878 delete events[type];
2882 var removeHandler = function(type) {
2883 var listenerFns = events[type];
2884 if (isDefined(fn)) {
2885 arrayRemove(listenerFns || [], fn);
2887 if (!(isDefined(fn) && listenerFns && listenerFns.length > 0)) {
2888 removeEventListenerFn(element, type, handle);
2889 delete events[type];
2893 forEach(type.split(' '), function(type) {
2894 removeHandler(type);
2895 if (MOUSE_EVENT_MAP[type]) {
2896 removeHandler(MOUSE_EVENT_MAP[type]);
2902 function jqLiteRemoveData(element, name) {
2903 var expandoId = element.ng339;
2904 var expandoStore = expandoId && jqCache[expandoId];
2908 delete expandoStore.data[name];
2912 if (expandoStore.handle) {
2913 if (expandoStore.events.$destroy) {
2914 expandoStore.handle({}, '$destroy');
2918 delete jqCache[expandoId];
2919 element.ng339 = undefined; // don't delete DOM expandos. IE and Chrome don't like it
2924 function jqLiteExpandoStore(element, createIfNecessary) {
2925 var expandoId = element.ng339,
2926 expandoStore = expandoId && jqCache[expandoId];
2928 if (createIfNecessary && !expandoStore) {
2929 element.ng339 = expandoId = jqNextId();
2930 expandoStore = jqCache[expandoId] = {events: {}, data: {}, handle: undefined};
2933 return expandoStore;
2937 function jqLiteData(element, key, value) {
2938 if (jqLiteAcceptsData(element)) {
2940 var isSimpleSetter = isDefined(value);
2941 var isSimpleGetter = !isSimpleSetter && key && !isObject(key);
2942 var massGetter = !key;
2943 var expandoStore = jqLiteExpandoStore(element, !isSimpleGetter);
2944 var data = expandoStore && expandoStore.data;
2946 if (isSimpleSetter) { // data('key', value)
2949 if (massGetter) { // data()
2952 if (isSimpleGetter) { // data('key')
2953 // don't force creation of expandoStore if it doesn't exist yet
2954 return data && data[key];
2955 } else { // mass-setter: data({key1: val1, key2: val2})
2963 function jqLiteHasClass(element, selector) {
2964 if (!element.getAttribute) return false;
2965 return ((" " + (element.getAttribute('class') || '') + " ").replace(/[\n\t]/g, " ").
2966 indexOf(" " + selector + " ") > -1);
2969 function jqLiteRemoveClass(element, cssClasses) {
2970 if (cssClasses && element.setAttribute) {
2971 forEach(cssClasses.split(' '), function(cssClass) {
2972 element.setAttribute('class', trim(
2973 (" " + (element.getAttribute('class') || '') + " ")
2974 .replace(/[\n\t]/g, " ")
2975 .replace(" " + trim(cssClass) + " ", " "))
2981 function jqLiteAddClass(element, cssClasses) {
2982 if (cssClasses && element.setAttribute) {
2983 var existingClasses = (' ' + (element.getAttribute('class') || '') + ' ')
2984 .replace(/[\n\t]/g, " ");
2986 forEach(cssClasses.split(' '), function(cssClass) {
2987 cssClass = trim(cssClass);
2988 if (existingClasses.indexOf(' ' + cssClass + ' ') === -1) {
2989 existingClasses += cssClass + ' ';
2993 element.setAttribute('class', trim(existingClasses));
2998 function jqLiteAddNodes(root, elements) {
2999 // THIS CODE IS VERY HOT. Don't make changes without benchmarking.
3003 // if a Node (the most common case)
3004 if (elements.nodeType) {
3005 root[root.length++] = elements;
3007 var length = elements.length;
3009 // if an Array or NodeList and not a Window
3010 if (typeof length === 'number' && elements.window !== elements) {
3012 for (var i = 0; i < length; i++) {
3013 root[root.length++] = elements[i];
3017 root[root.length++] = elements;
3024 function jqLiteController(element, name) {
3025 return jqLiteInheritedData(element, '$' + (name || 'ngController') + 'Controller');
3028 function jqLiteInheritedData(element, name, value) {
3029 // if element is the document object work with the html element instead
3030 // this makes $(document).scope() possible
3031 if (element.nodeType == NODE_TYPE_DOCUMENT) {
3032 element = element.documentElement;
3034 var names = isArray(name) ? name : [name];
3037 for (var i = 0, ii = names.length; i < ii; i++) {
3038 if (isDefined(value = jqLite.data(element, names[i]))) return value;
3041 // If dealing with a document fragment node with a host element, and no parent, use the host
3042 // element as the parent. This enables directives within a Shadow DOM or polyfilled Shadow DOM
3043 // to lookup parent controllers.
3044 element = element.parentNode || (element.nodeType === NODE_TYPE_DOCUMENT_FRAGMENT && element.host);
3048 function jqLiteEmpty(element) {
3049 jqLiteDealoc(element, true);
3050 while (element.firstChild) {
3051 element.removeChild(element.firstChild);
3055 function jqLiteRemove(element, keepData) {
3056 if (!keepData) jqLiteDealoc(element);
3057 var parent = element.parentNode;
3058 if (parent) parent.removeChild(element);
3062 function jqLiteDocumentLoaded(action, win) {
3063 win = win || window;
3064 if (win.document.readyState === 'complete') {
3065 // Force the action to be run async for consistent behaviour
3066 // from the action's point of view
3067 // i.e. it will definitely not be in a $apply
3068 win.setTimeout(action);
3070 // No need to unbind this handler as load is only ever called once
3071 jqLite(win).on('load', action);
3075 //////////////////////////////////////////
3076 // Functions which are declared directly.
3077 //////////////////////////////////////////
3078 var JQLitePrototype = JQLite.prototype = {
3079 ready: function(fn) {
3082 function trigger() {
3088 // check if document is already loaded
3089 if (document.readyState === 'complete') {
3090 setTimeout(trigger);
3092 this.on('DOMContentLoaded', trigger); // works for modern browsers and IE9
3093 // we can not use jqLite since we are not done loading and jQuery could be loaded later.
3095 JQLite(window).on('load', trigger); // fallback to window.onload for others
3099 toString: function() {
3101 forEach(this, function(e) { value.push('' + e);});
3102 return '[' + value.join(', ') + ']';
3105 eq: function(index) {
3106 return (index >= 0) ? jqLite(this[index]) : jqLite(this[this.length + index]);
3115 //////////////////////////////////////////
3116 // Functions iterating getter/setters.
3117 // these functions return self on setter and
3119 //////////////////////////////////////////
3120 var BOOLEAN_ATTR = {};
3121 forEach('multiple,selected,checked,disabled,readOnly,required,open'.split(','), function(value) {
3122 BOOLEAN_ATTR[lowercase(value)] = value;
3124 var BOOLEAN_ELEMENTS = {};
3125 forEach('input,select,option,textarea,button,form,details'.split(','), function(value) {
3126 BOOLEAN_ELEMENTS[value] = true;
3128 var ALIASED_ATTR = {
3129 'ngMinlength': 'minlength',
3130 'ngMaxlength': 'maxlength',
3133 'ngPattern': 'pattern'
3136 function getBooleanAttrName(element, name) {
3137 // check dom last since we will most likely fail on name
3138 var booleanAttr = BOOLEAN_ATTR[name.toLowerCase()];
3140 // booleanAttr is here twice to minimize DOM access
3141 return booleanAttr && BOOLEAN_ELEMENTS[nodeName_(element)] && booleanAttr;
3144 function getAliasedAttrName(name) {
3145 return ALIASED_ATTR[name];
3150 removeData: jqLiteRemoveData,
3151 hasData: jqLiteHasData
3152 }, function(fn, name) {
3158 inheritedData: jqLiteInheritedData,
3160 scope: function(element) {
3161 // Can't use jqLiteData here directly so we stay compatible with jQuery!
3162 return jqLite.data(element, '$scope') || jqLiteInheritedData(element.parentNode || element, ['$isolateScope', '$scope']);
3165 isolateScope: function(element) {
3166 // Can't use jqLiteData here directly so we stay compatible with jQuery!
3167 return jqLite.data(element, '$isolateScope') || jqLite.data(element, '$isolateScopeNoTemplate');
3170 controller: jqLiteController,
3172 injector: function(element) {
3173 return jqLiteInheritedData(element, '$injector');
3176 removeAttr: function(element, name) {
3177 element.removeAttribute(name);
3180 hasClass: jqLiteHasClass,
3182 css: function(element, name, value) {
3183 name = camelCase(name);
3185 if (isDefined(value)) {
3186 element.style[name] = value;
3188 return element.style[name];
3192 attr: function(element, name, value) {
3193 var nodeType = element.nodeType;
3194 if (nodeType === NODE_TYPE_TEXT || nodeType === NODE_TYPE_ATTRIBUTE || nodeType === NODE_TYPE_COMMENT) {
3197 var lowercasedName = lowercase(name);
3198 if (BOOLEAN_ATTR[lowercasedName]) {
3199 if (isDefined(value)) {
3201 element[name] = true;
3202 element.setAttribute(name, lowercasedName);
3204 element[name] = false;
3205 element.removeAttribute(lowercasedName);
3208 return (element[name] ||
3209 (element.attributes.getNamedItem(name) || noop).specified)
3213 } else if (isDefined(value)) {
3214 element.setAttribute(name, value);
3215 } else if (element.getAttribute) {
3216 // the extra argument "2" is to get the right thing for a.href in IE, see jQuery code
3217 // some elements (e.g. Document) don't have get attribute, so return undefined
3218 var ret = element.getAttribute(name, 2);
3219 // normalize non-existing attributes to undefined (as jQuery)
3220 return ret === null ? undefined : ret;
3224 prop: function(element, name, value) {
3225 if (isDefined(value)) {
3226 element[name] = value;
3228 return element[name];
3236 function getText(element, value) {
3237 if (isUndefined(value)) {
3238 var nodeType = element.nodeType;
3239 return (nodeType === NODE_TYPE_ELEMENT || nodeType === NODE_TYPE_TEXT) ? element.textContent : '';
3241 element.textContent = value;
3245 val: function(element, value) {
3246 if (isUndefined(value)) {
3247 if (element.multiple && nodeName_(element) === 'select') {
3249 forEach(element.options, function(option) {
3250 if (option.selected) {
3251 result.push(option.value || option.text);
3254 return result.length === 0 ? null : result;
3256 return element.value;
3258 element.value = value;
3261 html: function(element, value) {
3262 if (isUndefined(value)) {
3263 return element.innerHTML;
3265 jqLiteDealoc(element, true);
3266 element.innerHTML = value;
3270 }, function(fn, name) {
3272 * Properties: writes return selection, reads return first value
3274 JQLite.prototype[name] = function(arg1, arg2) {
3276 var nodeCount = this.length;
3278 // jqLiteHasClass has only two arguments, but is a getter-only fn, so we need to special-case it
3279 // in a way that survives minification.
3280 // jqLiteEmpty takes no arguments but is a setter.
3281 if (fn !== jqLiteEmpty &&
3282 (isUndefined((fn.length == 2 && (fn !== jqLiteHasClass && fn !== jqLiteController)) ? arg1 : arg2))) {
3283 if (isObject(arg1)) {
3285 // we are a write, but the object properties are the key/values
3286 for (i = 0; i < nodeCount; i++) {
3287 if (fn === jqLiteData) {
3288 // data() takes the whole object in jQuery
3292 fn(this[i], key, arg1[key]);
3296 // return self for chaining
3299 // we are a read, so read the first child.
3300 // TODO: do we still need this?
3302 // Only if we have $dv do we iterate over all, otherwise it is just the first element.
3303 var jj = (isUndefined(value)) ? Math.min(nodeCount, 1) : nodeCount;
3304 for (var j = 0; j < jj; j++) {
3305 var nodeValue = fn(this[j], arg1, arg2);
3306 value = value ? value + nodeValue : nodeValue;
3311 // we are a write, so apply to all children
3312 for (i = 0; i < nodeCount; i++) {
3313 fn(this[i], arg1, arg2);
3315 // return self for chaining
3321 function createEventHandler(element, events) {
3322 var eventHandler = function(event, type) {
3323 // jQuery specific api
3324 event.isDefaultPrevented = function() {
3325 return event.defaultPrevented;
3328 var eventFns = events[type || event.type];
3329 var eventFnsLength = eventFns ? eventFns.length : 0;
3331 if (!eventFnsLength) return;
3333 if (isUndefined(event.immediatePropagationStopped)) {
3334 var originalStopImmediatePropagation = event.stopImmediatePropagation;
3335 event.stopImmediatePropagation = function() {
3336 event.immediatePropagationStopped = true;
3338 if (event.stopPropagation) {
3339 event.stopPropagation();
3342 if (originalStopImmediatePropagation) {
3343 originalStopImmediatePropagation.call(event);
3348 event.isImmediatePropagationStopped = function() {
3349 return event.immediatePropagationStopped === true;
3352 // Some events have special handlers that wrap the real handler
3353 var handlerWrapper = eventFns.specialHandlerWrapper || defaultHandlerWrapper;
3355 // Copy event handlers in case event handlers array is modified during execution.
3356 if ((eventFnsLength > 1)) {
3357 eventFns = shallowCopy(eventFns);
3360 for (var i = 0; i < eventFnsLength; i++) {
3361 if (!event.isImmediatePropagationStopped()) {
3362 handlerWrapper(element, event, eventFns[i]);
3367 // TODO: this is a hack for angularMocks/clearDataCache that makes it possible to deregister all
3368 // events on `element`
3369 eventHandler.elem = element;
3370 return eventHandler;
3373 function defaultHandlerWrapper(element, event, handler) {
3374 handler.call(element, event);
3377 function specialMouseHandlerWrapper(target, event, handler) {
3378 // Refer to jQuery's implementation of mouseenter & mouseleave
3379 // Read about mouseenter and mouseleave:
3380 // http://www.quirksmode.org/js/events_mouse.html#link8
3381 var related = event.relatedTarget;
3382 // For mousenter/leave call the handler if related is outside the target.
3383 // NB: No relatedTarget if the mouse left/entered the browser window
3384 if (!related || (related !== target && !jqLiteContains.call(target, related))) {
3385 handler.call(target, event);
3389 //////////////////////////////////////////
3390 // Functions iterating traversal.
3391 // These functions chain results into a single
3393 //////////////////////////////////////////
3395 removeData: jqLiteRemoveData,
3397 on: function jqLiteOn(element, type, fn, unsupported) {
3398 if (isDefined(unsupported)) throw jqLiteMinErr('onargs', 'jqLite#on() does not support the `selector` or `eventData` parameters');
3400 // Do not add event handlers to non-elements because they will not be cleaned up.
3401 if (!jqLiteAcceptsData(element)) {
3405 var expandoStore = jqLiteExpandoStore(element, true);
3406 var events = expandoStore.events;
3407 var handle = expandoStore.handle;
3410 handle = expandoStore.handle = createEventHandler(element, events);
3413 // http://jsperf.com/string-indexof-vs-split
3414 var types = type.indexOf(' ') >= 0 ? type.split(' ') : [type];
3415 var i = types.length;
3417 var addHandler = function(type, specialHandlerWrapper, noEventListener) {
3418 var eventFns = events[type];
3421 eventFns = events[type] = [];
3422 eventFns.specialHandlerWrapper = specialHandlerWrapper;
3423 if (type !== '$destroy' && !noEventListener) {
3424 addEventListenerFn(element, type, handle);
3433 if (MOUSE_EVENT_MAP[type]) {
3434 addHandler(MOUSE_EVENT_MAP[type], specialMouseHandlerWrapper);
3435 addHandler(type, undefined, true);
3444 one: function(element, type, fn) {
3445 element = jqLite(element);
3447 //add the listener twice so that when it is called
3448 //you can remove the original function and still be
3449 //able to call element.off(ev, fn) normally
3450 element.on(type, function onFn() {
3451 element.off(type, fn);
3452 element.off(type, onFn);
3454 element.on(type, fn);
3457 replaceWith: function(element, replaceNode) {
3458 var index, parent = element.parentNode;
3459 jqLiteDealoc(element);
3460 forEach(new JQLite(replaceNode), function(node) {
3462 parent.insertBefore(node, index.nextSibling);
3464 parent.replaceChild(node, element);
3470 children: function(element) {
3472 forEach(element.childNodes, function(element) {
3473 if (element.nodeType === NODE_TYPE_ELEMENT) {
3474 children.push(element);
3480 contents: function(element) {
3481 return element.contentDocument || element.childNodes || [];
3484 append: function(element, node) {
3485 var nodeType = element.nodeType;
3486 if (nodeType !== NODE_TYPE_ELEMENT && nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT) return;
3488 node = new JQLite(node);
3490 for (var i = 0, ii = node.length; i < ii; i++) {
3491 var child = node[i];
3492 element.appendChild(child);
3496 prepend: function(element, node) {
3497 if (element.nodeType === NODE_TYPE_ELEMENT) {
3498 var index = element.firstChild;
3499 forEach(new JQLite(node), function(child) {
3500 element.insertBefore(child, index);
3505 wrap: function(element, wrapNode) {
3506 jqLiteWrapNode(element, jqLite(wrapNode).eq(0).clone()[0]);
3509 remove: jqLiteRemove,
3511 detach: function(element) {
3512 jqLiteRemove(element, true);
3515 after: function(element, newElement) {
3516 var index = element, parent = element.parentNode;
3517 newElement = new JQLite(newElement);
3519 for (var i = 0, ii = newElement.length; i < ii; i++) {
3520 var node = newElement[i];
3521 parent.insertBefore(node, index.nextSibling);
3526 addClass: jqLiteAddClass,
3527 removeClass: jqLiteRemoveClass,
3529 toggleClass: function(element, selector, condition) {
3531 forEach(selector.split(' '), function(className) {
3532 var classCondition = condition;
3533 if (isUndefined(classCondition)) {
3534 classCondition = !jqLiteHasClass(element, className);
3536 (classCondition ? jqLiteAddClass : jqLiteRemoveClass)(element, className);
3541 parent: function(element) {
3542 var parent = element.parentNode;
3543 return parent && parent.nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT ? parent : null;
3546 next: function(element) {
3547 return element.nextElementSibling;
3550 find: function(element, selector) {
3551 if (element.getElementsByTagName) {
3552 return element.getElementsByTagName(selector);
3560 triggerHandler: function(element, event, extraParameters) {
3562 var dummyEvent, eventFnsCopy, handlerArgs;
3563 var eventName = event.type || event;
3564 var expandoStore = jqLiteExpandoStore(element);
3565 var events = expandoStore && expandoStore.events;
3566 var eventFns = events && events[eventName];
3569 // Create a dummy event to pass to the handlers
3571 preventDefault: function() { this.defaultPrevented = true; },
3572 isDefaultPrevented: function() { return this.defaultPrevented === true; },
3573 stopImmediatePropagation: function() { this.immediatePropagationStopped = true; },
3574 isImmediatePropagationStopped: function() { return this.immediatePropagationStopped === true; },
3575 stopPropagation: noop,
3580 // If a custom event was provided then extend our dummy event with it
3582 dummyEvent = extend(dummyEvent, event);
3585 // Copy event handlers in case event handlers array is modified during execution.
3586 eventFnsCopy = shallowCopy(eventFns);
3587 handlerArgs = extraParameters ? [dummyEvent].concat(extraParameters) : [dummyEvent];
3589 forEach(eventFnsCopy, function(fn) {
3590 if (!dummyEvent.isImmediatePropagationStopped()) {
3591 fn.apply(element, handlerArgs);
3596 }, function(fn, name) {
3598 * chaining functions
3600 JQLite.prototype[name] = function(arg1, arg2, arg3) {
3603 for (var i = 0, ii = this.length; i < ii; i++) {
3604 if (isUndefined(value)) {
3605 value = fn(this[i], arg1, arg2, arg3);
3606 if (isDefined(value)) {
3607 // any function which returns a value needs to be wrapped
3608 value = jqLite(value);
3611 jqLiteAddNodes(value, fn(this[i], arg1, arg2, arg3));
3614 return isDefined(value) ? value : this;
3617 // bind legacy bind/unbind to on/off
3618 JQLite.prototype.bind = JQLite.prototype.on;
3619 JQLite.prototype.unbind = JQLite.prototype.off;
3623 // Provider for private $$jqLite service
3624 function $$jqLiteProvider() {
3625 this.$get = function $$jqLite() {
3626 return extend(JQLite, {
3627 hasClass: function(node, classes) {
3628 if (node.attr) node = node[0];
3629 return jqLiteHasClass(node, classes);
3631 addClass: function(node, classes) {
3632 if (node.attr) node = node[0];
3633 return jqLiteAddClass(node, classes);
3635 removeClass: function(node, classes) {
3636 if (node.attr) node = node[0];
3637 return jqLiteRemoveClass(node, classes);
3644 * Computes a hash of an 'obj'.
3647 * number is number as string
3648 * object is either result of calling $$hashKey function on the object or uniquely generated id,
3649 * that is also assigned to the $$hashKey property of the object.
3652 * @returns {string} hash string such that the same input will have the same hash string.
3653 * The resulting string key is in 'type:hashKey' format.
3655 function hashKey(obj, nextUidFn) {
3656 var key = obj && obj.$$hashKey;
3659 if (typeof key === 'function') {
3660 key = obj.$$hashKey();
3665 var objType = typeof obj;
3666 if (objType == 'function' || (objType == 'object' && obj !== null)) {
3667 key = obj.$$hashKey = objType + ':' + (nextUidFn || nextUid)();
3669 key = objType + ':' + obj;
3676 * HashMap which can use objects as keys
3678 function HashMap(array, isolatedUid) {
3681 this.nextUid = function() {
3685 forEach(array, this.put, this);
3687 HashMap.prototype = {
3689 * Store key value pair
3690 * @param key key to store can be any type
3691 * @param value value to store can be any type
3693 put: function(key, value) {
3694 this[hashKey(key, this.nextUid)] = value;
3699 * @returns {Object} the value for the key
3701 get: function(key) {
3702 return this[hashKey(key, this.nextUid)];
3706 * Remove the key/value pair
3709 remove: function(key) {
3710 var value = this[key = hashKey(key, this.nextUid)];
3716 var $$HashMapProvider = [function() {
3717 this.$get = [function() {
3725 * @name angular.injector
3729 * Creates an injector object that can be used for retrieving services as well as for
3730 * dependency injection (see {@link guide/di dependency injection}).
3732 * @param {Array.<string|Function>} modules A list of module functions or their aliases. See
3733 * {@link angular.module}. The `ng` module must be explicitly added.
3734 * @param {boolean=} [strictDi=false] Whether the injector should be in strict mode, which
3735 * disallows argument name annotation inference.
3736 * @returns {injector} Injector object. See {@link auto.$injector $injector}.
3741 * // create an injector
3742 * var $injector = angular.injector(['ng']);
3744 * // use the injector to kick off your application
3745 * // use the type inference to auto inject arguments, or use implicit injection
3746 * $injector.invoke(function($rootScope, $compile, $document) {
3747 * $compile($document)($rootScope);
3748 * $rootScope.$digest();
3752 * Sometimes you want to get access to the injector of a currently running Angular app
3753 * from outside Angular. Perhaps, you want to inject and compile some markup after the
3754 * application has been bootstrapped. You can do this using the extra `injector()` added
3755 * to JQuery/jqLite elements. See {@link angular.element}.
3757 * *This is fairly rare but could be the case if a third party library is injecting the
3760 * In the following example a new block of HTML containing a `ng-controller`
3761 * directive is added to the end of the document body by JQuery. We then compile and link
3762 * it into the current AngularJS scope.
3765 * var $div = $('<div ng-controller="MyCtrl">{{content.label}}</div>');
3766 * $(document.body).append($div);
3768 * angular.element(document).injector().invoke(function($compile) {
3769 * var scope = angular.element($div).scope();
3770 * $compile($div)(scope);
3781 * Implicit module which gets automatically added to each {@link auto.$injector $injector}.
3784 var FN_ARGS = /^[^\(]*\(\s*([^\)]*)\)/m;
3785 var FN_ARG_SPLIT = /,/;
3786 var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/;
3787 var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
3788 var $injectorMinErr = minErr('$injector');
3790 function anonFn(fn) {
3791 // For anonymous functions, showing at the very least the function signature can help in
3793 var fnText = fn.toString().replace(STRIP_COMMENTS, ''),
3794 args = fnText.match(FN_ARGS);
3796 return 'function(' + (args[1] || '').replace(/[\s\r\n]+/, ' ') + ')';
3801 function annotate(fn, strictDi, name) {
3807 if (typeof fn === 'function') {
3808 if (!($inject = fn.$inject)) {
3812 if (!isString(name) || !name) {
3813 name = fn.name || anonFn(fn);
3815 throw $injectorMinErr('strictdi',
3816 '{0} is not using explicit annotation and cannot be invoked in strict mode', name);
3818 fnText = fn.toString().replace(STRIP_COMMENTS, '');
3819 argDecl = fnText.match(FN_ARGS);
3820 forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg) {
3821 arg.replace(FN_ARG, function(all, underscore, name) {
3826 fn.$inject = $inject;
3828 } else if (isArray(fn)) {
3829 last = fn.length - 1;
3830 assertArgFn(fn[last], 'fn');
3831 $inject = fn.slice(0, last);
3833 assertArgFn(fn, 'fn', true);
3838 ///////////////////////////////////////
3846 * `$injector` is used to retrieve object instances as defined by
3847 * {@link auto.$provide provider}, instantiate types, invoke methods,
3850 * The following always holds true:
3853 * var $injector = angular.injector();
3854 * expect($injector.get('$injector')).toBe($injector);
3855 * expect($injector.invoke(function($injector) {
3857 * })).toBe($injector);
3860 * # Injection Function Annotation
3862 * JavaScript does not have annotations, and annotations are needed for dependency injection. The
3863 * following are all valid ways of annotating function with injection arguments and are equivalent.
3866 * // inferred (only works if code not minified/obfuscated)
3867 * $injector.invoke(function(serviceA){});
3870 * function explicit(serviceA) {};
3871 * explicit.$inject = ['serviceA'];
3872 * $injector.invoke(explicit);
3875 * $injector.invoke(['serviceA', function(serviceA){}]);
3880 * In JavaScript calling `toString()` on a function returns the function definition. The definition
3881 * can then be parsed and the function arguments can be extracted. This method of discovering
3882 * annotations is disallowed when the injector is in strict mode.
3883 * *NOTE:* This does not work with minification, and obfuscation tools since these tools change the
3886 * ## `$inject` Annotation
3887 * By adding an `$inject` property onto a function the injection parameters can be specified.
3890 * As an array of injection names, where the last item in the array is the function to call.
3895 * @name $injector#get
3898 * Return an instance of the service.
3900 * @param {string} name The name of the instance to retrieve.
3901 * @param {string=} caller An optional string to provide the origin of the function call for error messages.
3902 * @return {*} The instance.
3907 * @name $injector#invoke
3910 * Invoke the method and supply the method arguments from the `$injector`.
3912 * @param {Function|Array.<string|Function>} fn The injectable function to invoke. Function parameters are
3913 * injected according to the {@link guide/di $inject Annotation} rules.
3914 * @param {Object=} self The `this` for the invoked method.
3915 * @param {Object=} locals Optional object. If preset then any argument names are read from this
3916 * object first, before the `$injector` is consulted.
3917 * @returns {*} the value returned by the invoked `fn` function.
3922 * @name $injector#has
3925 * Allows the user to query if the particular service exists.
3927 * @param {string} name Name of the service to query.
3928 * @returns {boolean} `true` if injector has given service.
3933 * @name $injector#instantiate
3935 * Create a new instance of JS type. The method takes a constructor function, invokes the new
3936 * operator, and supplies all of the arguments to the constructor function as specified by the
3937 * constructor annotation.
3939 * @param {Function} Type Annotated constructor function.
3940 * @param {Object=} locals Optional object. If preset then any argument names are read from this
3941 * object first, before the `$injector` is consulted.
3942 * @returns {Object} new instance of `Type`.
3947 * @name $injector#annotate
3950 * Returns an array of service names which the function is requesting for injection. This API is
3951 * used by the injector to determine which services need to be injected into the function when the
3952 * function is invoked. There are three ways in which the function can be annotated with the needed
3957 * The simplest form is to extract the dependencies from the arguments of the function. This is done
3958 * by converting the function into a string using `toString()` method and extracting the argument
3962 * function MyController($scope, $route) {
3967 * expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
3970 * You can disallow this method by using strict injection mode.
3972 * This method does not work with code minification / obfuscation. For this reason the following
3973 * annotation strategies are supported.
3975 * # The `$inject` property
3977 * If a function has an `$inject` property and its value is an array of strings, then the strings
3978 * represent names of services to be injected into the function.
3981 * var MyController = function(obfuscatedScope, obfuscatedRoute) {
3984 * // Define function dependencies
3985 * MyController['$inject'] = ['$scope', '$route'];
3988 * expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
3991 * # The array notation
3993 * It is often desirable to inline Injected functions and that's when setting the `$inject` property
3994 * is very inconvenient. In these situations using the array notation to specify the dependencies in
3995 * a way that survives minification is a better choice:
3998 * // We wish to write this (not minification / obfuscation safe)
3999 * injector.invoke(function($compile, $rootScope) {
4003 * // We are forced to write break inlining
4004 * var tmpFn = function(obfuscatedCompile, obfuscatedRootScope) {
4007 * tmpFn.$inject = ['$compile', '$rootScope'];
4008 * injector.invoke(tmpFn);
4010 * // To better support inline function the inline annotation is supported
4011 * injector.invoke(['$compile', '$rootScope', function(obfCompile, obfRootScope) {
4016 * expect(injector.annotate(
4017 * ['$compile', '$rootScope', function(obfus_$compile, obfus_$rootScope) {}])
4018 * ).toEqual(['$compile', '$rootScope']);
4021 * @param {Function|Array.<string|Function>} fn Function for which dependent service names need to
4022 * be retrieved as described above.
4024 * @param {boolean=} [strictDi=false] Disallow argument name annotation inference.
4026 * @returns {Array.<string>} The names of the services which the function requires.
4038 * The {@link auto.$provide $provide} service has a number of methods for registering components
4039 * with the {@link auto.$injector $injector}. Many of these functions are also exposed on
4040 * {@link angular.Module}.
4042 * An Angular **service** is a singleton object created by a **service factory**. These **service
4043 * factories** are functions which, in turn, are created by a **service provider**.
4044 * The **service providers** are constructor functions. When instantiated they must contain a
4045 * property called `$get`, which holds the **service factory** function.
4047 * When you request a service, the {@link auto.$injector $injector} is responsible for finding the
4048 * correct **service provider**, instantiating it and then calling its `$get` **service factory**
4049 * function to get the instance of the **service**.
4051 * Often services have no configuration options and there is no need to add methods to the service
4052 * provider. The provider will be no more than a constructor function with a `$get` property. For
4053 * these cases the {@link auto.$provide $provide} service has additional helper methods to register
4054 * services without specifying a provider.
4056 * * {@link auto.$provide#provider provider(provider)} - registers a **service provider** with the
4057 * {@link auto.$injector $injector}
4058 * * {@link auto.$provide#constant constant(obj)} - registers a value/object that can be accessed by
4059 * providers and services.
4060 * * {@link auto.$provide#value value(obj)} - registers a value/object that can only be accessed by
4061 * services, not providers.
4062 * * {@link auto.$provide#factory factory(fn)} - registers a service **factory function**, `fn`,
4063 * that will be wrapped in a **service provider** object, whose `$get` property will contain the
4064 * given factory function.
4065 * * {@link auto.$provide#service service(class)} - registers a **constructor function**, `class`
4066 * that will be wrapped in a **service provider** object, whose `$get` property will instantiate
4067 * a new object using the given constructor function.
4069 * See the individual methods for more information and examples.
4074 * @name $provide#provider
4077 * Register a **provider function** with the {@link auto.$injector $injector}. Provider functions
4078 * are constructor functions, whose instances are responsible for "providing" a factory for a
4081 * Service provider names start with the name of the service they provide followed by `Provider`.
4082 * For example, the {@link ng.$log $log} service has a provider called
4083 * {@link ng.$logProvider $logProvider}.
4085 * Service provider objects can have additional methods which allow configuration of the provider
4086 * and its service. Importantly, you can configure what kind of service is created by the `$get`
4087 * method, or how that service will act. For example, the {@link ng.$logProvider $logProvider} has a
4088 * method {@link ng.$logProvider#debugEnabled debugEnabled}
4089 * which lets you specify whether the {@link ng.$log $log} service will log debug messages to the
4092 * @param {string} name The name of the instance. NOTE: the provider will be available under `name +
4094 * @param {(Object|function())} provider If the provider is:
4096 * - `Object`: then it should have a `$get` method. The `$get` method will be invoked using
4097 * {@link auto.$injector#invoke $injector.invoke()} when an instance needs to be created.
4098 * - `Constructor`: a new instance of the provider will be created using
4099 * {@link auto.$injector#instantiate $injector.instantiate()}, then treated as `object`.
4101 * @returns {Object} registered provider instance
4105 * The following example shows how to create a simple event tracking service and register it using
4106 * {@link auto.$provide#provider $provide.provider()}.
4109 * // Define the eventTracker provider
4110 * function EventTrackerProvider() {
4111 * var trackingUrl = '/track';
4113 * // A provider method for configuring where the tracked events should been saved
4114 * this.setTrackingUrl = function(url) {
4115 * trackingUrl = url;
4118 * // The service factory function
4119 * this.$get = ['$http', function($http) {
4120 * var trackedEvents = {};
4122 * // Call this to track an event
4123 * event: function(event) {
4124 * var count = trackedEvents[event] || 0;
4126 * trackedEvents[event] = count;
4129 * // Call this to save the tracked events to the trackingUrl
4130 * save: function() {
4131 * $http.post(trackingUrl, trackedEvents);
4137 * describe('eventTracker', function() {
4140 * beforeEach(module(function($provide) {
4141 * // Register the eventTracker provider
4142 * $provide.provider('eventTracker', EventTrackerProvider);
4145 * beforeEach(module(function(eventTrackerProvider) {
4146 * // Configure eventTracker provider
4147 * eventTrackerProvider.setTrackingUrl('/custom-track');
4150 * it('tracks events', inject(function(eventTracker) {
4151 * expect(eventTracker.event('login')).toEqual(1);
4152 * expect(eventTracker.event('login')).toEqual(2);
4155 * it('saves to the tracking url', inject(function(eventTracker, $http) {
4156 * postSpy = spyOn($http, 'post');
4157 * eventTracker.event('login');
4158 * eventTracker.save();
4159 * expect(postSpy).toHaveBeenCalled();
4160 * expect(postSpy.mostRecentCall.args[0]).not.toEqual('/track');
4161 * expect(postSpy.mostRecentCall.args[0]).toEqual('/custom-track');
4162 * expect(postSpy.mostRecentCall.args[1]).toEqual({ 'login': 1 });
4170 * @name $provide#factory
4173 * Register a **service factory**, which will be called to return the service instance.
4174 * This is short for registering a service where its provider consists of only a `$get` property,
4175 * which is the given service factory function.
4176 * You should use {@link auto.$provide#factory $provide.factory(getFn)} if you do not need to
4177 * configure your service in a provider.
4179 * @param {string} name The name of the instance.
4180 * @param {Function|Array.<string|Function>} $getFn The injectable $getFn for the instance creation.
4181 * Internally this is a short hand for `$provide.provider(name, {$get: $getFn})`.
4182 * @returns {Object} registered provider instance
4185 * Here is an example of registering a service
4187 * $provide.factory('ping', ['$http', function($http) {
4188 * return function ping() {
4189 * return $http.send('/ping');
4193 * You would then inject and use this service like this:
4195 * someModule.controller('Ctrl', ['ping', function(ping) {
4204 * @name $provide#service
4207 * Register a **service constructor**, which will be invoked with `new` to create the service
4209 * This is short for registering a service where its provider's `$get` property is a factory
4210 * function that returns an instance instantiated by the injector from the service constructor
4213 * Internally it looks a bit like this:
4217 * $get: function() {
4218 * return $injector.instantiate(constructor);
4224 * You should use {@link auto.$provide#service $provide.service(class)} if you define your service
4227 * @param {string} name The name of the instance.
4228 * @param {Function|Array.<string|Function>} constructor An injectable class (constructor function)
4229 * that will be instantiated.
4230 * @returns {Object} registered provider instance
4233 * Here is an example of registering a service using
4234 * {@link auto.$provide#service $provide.service(class)}.
4236 * var Ping = function($http) {
4237 * this.$http = $http;
4240 * Ping.$inject = ['$http'];
4242 * Ping.prototype.send = function() {
4243 * return this.$http.get('/ping');
4245 * $provide.service('ping', Ping);
4247 * You would then inject and use this service like this:
4249 * someModule.controller('Ctrl', ['ping', function(ping) {
4258 * @name $provide#value
4261 * Register a **value service** with the {@link auto.$injector $injector}, such as a string, a
4262 * number, an array, an object or a function. This is short for registering a service where its
4263 * provider's `$get` property is a factory function that takes no arguments and returns the **value
4264 * service**. That also means it is not possible to inject other services into a value service.
4266 * Value services are similar to constant services, except that they cannot be injected into a
4267 * module configuration function (see {@link angular.Module#config}) but they can be overridden by
4268 * an Angular {@link auto.$provide#decorator decorator}.
4270 * @param {string} name The name of the instance.
4271 * @param {*} value The value.
4272 * @returns {Object} registered provider instance
4275 * Here are some examples of creating value services.
4277 * $provide.value('ADMIN_USER', 'admin');
4279 * $provide.value('RoleLookup', { admin: 0, writer: 1, reader: 2 });
4281 * $provide.value('halfOf', function(value) {
4290 * @name $provide#constant
4293 * Register a **constant service** with the {@link auto.$injector $injector}, such as a string,
4294 * a number, an array, an object or a function. Like the {@link auto.$provide#value value}, it is not
4295 * possible to inject other services into a constant.
4297 * But unlike {@link auto.$provide#value value}, a constant can be
4298 * injected into a module configuration function (see {@link angular.Module#config}) and it cannot
4299 * be overridden by an Angular {@link auto.$provide#decorator decorator}.
4301 * @param {string} name The name of the constant.
4302 * @param {*} value The constant value.
4303 * @returns {Object} registered instance
4306 * Here a some examples of creating constants:
4308 * $provide.constant('SHARD_HEIGHT', 306);
4310 * $provide.constant('MY_COLOURS', ['red', 'blue', 'grey']);
4312 * $provide.constant('double', function(value) {
4321 * @name $provide#decorator
4324 * Register a **service decorator** with the {@link auto.$injector $injector}. A service decorator
4325 * intercepts the creation of a service, allowing it to override or modify the behavior of the
4326 * service. The object returned by the decorator may be the original service, or a new service
4327 * object which replaces or wraps and delegates to the original service.
4329 * @param {string} name The name of the service to decorate.
4330 * @param {Function|Array.<string|Function>} decorator This function will be invoked when the service needs to be
4331 * instantiated and should return the decorated service instance. The function is called using
4332 * the {@link auto.$injector#invoke injector.invoke} method and is therefore fully injectable.
4333 * Local injection arguments:
4335 * * `$delegate` - The original service instance, which can be monkey patched, configured,
4336 * decorated or delegated to.
4339 * Here we decorate the {@link ng.$log $log} service to convert warnings to errors by intercepting
4340 * calls to {@link ng.$log#error $log.warn()}.
4342 * $provide.decorator('$log', ['$delegate', function($delegate) {
4343 * $delegate.warn = $delegate.error;
4350 function createInjector(modulesToLoad, strictDi) {
4351 strictDi = (strictDi === true);
4352 var INSTANTIATING = {},
4353 providerSuffix = 'Provider',
4355 loadedModules = new HashMap([], true),
4358 provider: supportObject(provider),
4359 factory: supportObject(factory),
4360 service: supportObject(service),
4361 value: supportObject(value),
4362 constant: supportObject(constant),
4363 decorator: decorator
4366 providerInjector = (providerCache.$injector =
4367 createInternalInjector(providerCache, function(serviceName, caller) {
4368 if (angular.isString(caller)) {
4371 throw $injectorMinErr('unpr', "Unknown provider: {0}", path.join(' <- '));
4374 instanceInjector = (instanceCache.$injector =
4375 createInternalInjector(instanceCache, function(serviceName, caller) {
4376 var provider = providerInjector.get(serviceName + providerSuffix, caller);
4377 return instanceInjector.invoke(provider.$get, provider, undefined, serviceName);
4381 forEach(loadModules(modulesToLoad), function(fn) { if (fn) instanceInjector.invoke(fn); });
4383 return instanceInjector;
4385 ////////////////////////////////////
4387 ////////////////////////////////////
4389 function supportObject(delegate) {
4390 return function(key, value) {
4391 if (isObject(key)) {
4392 forEach(key, reverseParams(delegate));
4394 return delegate(key, value);
4399 function provider(name, provider_) {
4400 assertNotHasOwnProperty(name, 'service');
4401 if (isFunction(provider_) || isArray(provider_)) {
4402 provider_ = providerInjector.instantiate(provider_);
4404 if (!provider_.$get) {
4405 throw $injectorMinErr('pget', "Provider '{0}' must define $get factory method.", name);
4407 return providerCache[name + providerSuffix] = provider_;
4410 function enforceReturnValue(name, factory) {
4411 return function enforcedReturnValue() {
4412 var result = instanceInjector.invoke(factory, this);
4413 if (isUndefined(result)) {
4414 throw $injectorMinErr('undef', "Provider '{0}' must return a value from $get factory method.", name);
4420 function factory(name, factoryFn, enforce) {
4421 return provider(name, {
4422 $get: enforce !== false ? enforceReturnValue(name, factoryFn) : factoryFn
4426 function service(name, constructor) {
4427 return factory(name, ['$injector', function($injector) {
4428 return $injector.instantiate(constructor);
4432 function value(name, val) { return factory(name, valueFn(val), false); }
4434 function constant(name, value) {
4435 assertNotHasOwnProperty(name, 'constant');
4436 providerCache[name] = value;
4437 instanceCache[name] = value;
4440 function decorator(serviceName, decorFn) {
4441 var origProvider = providerInjector.get(serviceName + providerSuffix),
4442 orig$get = origProvider.$get;
4444 origProvider.$get = function() {
4445 var origInstance = instanceInjector.invoke(orig$get, origProvider);
4446 return instanceInjector.invoke(decorFn, null, {$delegate: origInstance});
4450 ////////////////////////////////////
4452 ////////////////////////////////////
4453 function loadModules(modulesToLoad) {
4454 assertArg(isUndefined(modulesToLoad) || isArray(modulesToLoad), 'modulesToLoad', 'not an array');
4455 var runBlocks = [], moduleFn;
4456 forEach(modulesToLoad, function(module) {
4457 if (loadedModules.get(module)) return;
4458 loadedModules.put(module, true);
4460 function runInvokeQueue(queue) {
4462 for (i = 0, ii = queue.length; i < ii; i++) {
4463 var invokeArgs = queue[i],
4464 provider = providerInjector.get(invokeArgs[0]);
4466 provider[invokeArgs[1]].apply(provider, invokeArgs[2]);
4471 if (isString(module)) {
4472 moduleFn = angularModule(module);
4473 runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks);
4474 runInvokeQueue(moduleFn._invokeQueue);
4475 runInvokeQueue(moduleFn._configBlocks);
4476 } else if (isFunction(module)) {
4477 runBlocks.push(providerInjector.invoke(module));
4478 } else if (isArray(module)) {
4479 runBlocks.push(providerInjector.invoke(module));
4481 assertArgFn(module, 'module');
4484 if (isArray(module)) {
4485 module = module[module.length - 1];
4487 if (e.message && e.stack && e.stack.indexOf(e.message) == -1) {
4488 // Safari & FF's stack traces don't contain error.message content
4489 // unlike those of Chrome and IE
4490 // So if stack doesn't contain message, we create a new string that contains both.
4491 // Since error.stack is read-only in Safari, I'm overriding e and not e.stack here.
4493 e = e.message + '\n' + e.stack;
4495 throw $injectorMinErr('modulerr', "Failed to instantiate module {0} due to:\n{1}",
4496 module, e.stack || e.message || e);
4502 ////////////////////////////////////
4503 // internal Injector
4504 ////////////////////////////////////
4506 function createInternalInjector(cache, factory) {
4508 function getService(serviceName, caller) {
4509 if (cache.hasOwnProperty(serviceName)) {
4510 if (cache[serviceName] === INSTANTIATING) {
4511 throw $injectorMinErr('cdep', 'Circular dependency found: {0}',
4512 serviceName + ' <- ' + path.join(' <- '));
4514 return cache[serviceName];
4517 path.unshift(serviceName);
4518 cache[serviceName] = INSTANTIATING;
4519 return cache[serviceName] = factory(serviceName, caller);
4521 if (cache[serviceName] === INSTANTIATING) {
4522 delete cache[serviceName];
4531 function invoke(fn, self, locals, serviceName) {
4532 if (typeof locals === 'string') {
4533 serviceName = locals;
4538 $inject = createInjector.$$annotate(fn, strictDi, serviceName),
4542 for (i = 0, length = $inject.length; i < length; i++) {
4544 if (typeof key !== 'string') {
4545 throw $injectorMinErr('itkn',
4546 'Incorrect injection token! Expected service name as string, got {0}', key);
4549 locals && locals.hasOwnProperty(key)
4551 : getService(key, serviceName)
4558 // http://jsperf.com/angularjs-invoke-apply-vs-switch
4560 return fn.apply(self, args);
4563 function instantiate(Type, locals, serviceName) {
4564 // Check if Type is annotated and use just the given function at n-1 as parameter
4565 // e.g. someModule.factory('greeter', ['$window', function(renamed$window) {}]);
4566 // Object creation: http://jsperf.com/create-constructor/2
4567 var instance = Object.create((isArray(Type) ? Type[Type.length - 1] : Type).prototype || null);
4568 var returnedValue = invoke(Type, instance, locals, serviceName);
4570 return isObject(returnedValue) || isFunction(returnedValue) ? returnedValue : instance;
4575 instantiate: instantiate,
4577 annotate: createInjector.$$annotate,
4578 has: function(name) {
4579 return providerCache.hasOwnProperty(name + providerSuffix) || cache.hasOwnProperty(name);
4585 createInjector.$$annotate = annotate;
4589 * @name $anchorScrollProvider
4592 * Use `$anchorScrollProvider` to disable automatic scrolling whenever
4593 * {@link ng.$location#hash $location.hash()} changes.
4595 function $AnchorScrollProvider() {
4597 var autoScrollingEnabled = true;
4601 * @name $anchorScrollProvider#disableAutoScrolling
4604 * By default, {@link ng.$anchorScroll $anchorScroll()} will automatically detect changes to
4605 * {@link ng.$location#hash $location.hash()} and scroll to the element matching the new hash.<br />
4606 * Use this method to disable automatic scrolling.
4608 * If automatic scrolling is disabled, one must explicitly call
4609 * {@link ng.$anchorScroll $anchorScroll()} in order to scroll to the element related to the
4612 this.disableAutoScrolling = function() {
4613 autoScrollingEnabled = false;
4618 * @name $anchorScroll
4621 * @requires $location
4622 * @requires $rootScope
4625 * When called, it scrolls to the element related to the specified `hash` or (if omitted) to the
4626 * current value of {@link ng.$location#hash $location.hash()}, according to the rules specified
4628 * [HTML5 spec](http://www.w3.org/html/wg/drafts/html/master/browsers.html#the-indicated-part-of-the-document).
4630 * It also watches the {@link ng.$location#hash $location.hash()} and automatically scrolls to
4631 * match any anchor whenever it changes. This can be disabled by calling
4632 * {@link ng.$anchorScrollProvider#disableAutoScrolling $anchorScrollProvider.disableAutoScrolling()}.
4634 * Additionally, you can use its {@link ng.$anchorScroll#yOffset yOffset} property to specify a
4635 * vertical scroll-offset (either fixed or dynamic).
4637 * @param {string=} hash The hash specifying the element to scroll to. If omitted, the value of
4638 * {@link ng.$location#hash $location.hash()} will be used.
4640 * @property {(number|function|jqLite)} yOffset
4641 * If set, specifies a vertical scroll-offset. This is often useful when there are fixed
4642 * positioned elements at the top of the page, such as navbars, headers etc.
4644 * `yOffset` can be specified in various ways:
4645 * - **number**: A fixed number of pixels to be used as offset.<br /><br />
4646 * - **function**: A getter function called everytime `$anchorScroll()` is executed. Must return
4647 * a number representing the offset (in pixels).<br /><br />
4648 * - **jqLite**: A jqLite/jQuery element to be used for specifying the offset. The distance from
4649 * the top of the page to the element's bottom will be used as offset.<br />
4650 * **Note**: The element will be taken into account only as long as its `position` is set to
4651 * `fixed`. This option is useful, when dealing with responsive navbars/headers that adjust
4652 * their height and/or positioning according to the viewport's size.
4655 * <div class="alert alert-warning">
4656 * In order for `yOffset` to work properly, scrolling should take place on the document's root and
4657 * not some child element.
4661 <example module="anchorScrollExample">
4662 <file name="index.html">
4663 <div id="scrollArea" ng-controller="ScrollController">
4664 <a ng-click="gotoBottom()">Go to bottom</a>
4665 <a id="bottom"></a> You're at the bottom!
4668 <file name="script.js">
4669 angular.module('anchorScrollExample', [])
4670 .controller('ScrollController', ['$scope', '$location', '$anchorScroll',
4671 function ($scope, $location, $anchorScroll) {
4672 $scope.gotoBottom = function() {
4673 // set the location.hash to the id of
4674 // the element you wish to scroll to.
4675 $location.hash('bottom');
4677 // call $anchorScroll()
4682 <file name="style.css">
4696 * The example below illustrates the use of a vertical scroll-offset (specified as a fixed value).
4697 * See {@link ng.$anchorScroll#yOffset $anchorScroll.yOffset} for more details.
4700 <example module="anchorScrollOffsetExample">
4701 <file name="index.html">
4702 <div class="fixed-header" ng-controller="headerCtrl">
4703 <a href="" ng-click="gotoAnchor(x)" ng-repeat="x in [1,2,3,4,5]">
4707 <div id="anchor{{x}}" class="anchor" ng-repeat="x in [1,2,3,4,5]">
4711 <file name="script.js">
4712 angular.module('anchorScrollOffsetExample', [])
4713 .run(['$anchorScroll', function($anchorScroll) {
4714 $anchorScroll.yOffset = 50; // always scroll by 50 extra pixels
4716 .controller('headerCtrl', ['$anchorScroll', '$location', '$scope',
4717 function ($anchorScroll, $location, $scope) {
4718 $scope.gotoAnchor = function(x) {
4719 var newHash = 'anchor' + x;
4720 if ($location.hash() !== newHash) {
4721 // set the $location.hash to `newHash` and
4722 // $anchorScroll will automatically scroll to it
4723 $location.hash('anchor' + x);
4725 // call $anchorScroll() explicitly,
4726 // since $location.hash hasn't changed
4733 <file name="style.css">
4739 border: 2px dashed DarkOrchid;
4740 padding: 10px 10px 200px 10px;
4744 background-color: rgba(0, 0, 0, 0.2);
4747 top: 0; left: 0; right: 0;
4751 display: inline-block;
4757 this.$get = ['$window', '$location', '$rootScope', function($window, $location, $rootScope) {
4758 var document = $window.document;
4760 // Helper function to get first anchor from a NodeList
4761 // (using `Array#some()` instead of `angular#forEach()` since it's more performant
4762 // and working in all supported browsers.)
4763 function getFirstAnchor(list) {
4765 Array.prototype.some.call(list, function(element) {
4766 if (nodeName_(element) === 'a') {
4774 function getYOffset() {
4776 var offset = scroll.yOffset;
4778 if (isFunction(offset)) {
4780 } else if (isElement(offset)) {
4781 var elem = offset[0];
4782 var style = $window.getComputedStyle(elem);
4783 if (style.position !== 'fixed') {
4786 offset = elem.getBoundingClientRect().bottom;
4788 } else if (!isNumber(offset)) {
4795 function scrollTo(elem) {
4797 elem.scrollIntoView();
4799 var offset = getYOffset();
4802 // `offset` is the number of pixels we should scroll UP in order to align `elem` properly.
4803 // This is true ONLY if the call to `elem.scrollIntoView()` initially aligns `elem` at the
4804 // top of the viewport.
4806 // IF the number of pixels from the top of `elem` to the end of the page's content is less
4807 // than the height of the viewport, then `elem.scrollIntoView()` will align the `elem` some
4808 // way down the page.
4810 // This is often the case for elements near the bottom of the page.
4812 // In such cases we do not need to scroll the whole `offset` up, just the difference between
4813 // the top of the element and the offset, which is enough to align the top of `elem` at the
4814 // desired position.
4815 var elemTop = elem.getBoundingClientRect().top;
4816 $window.scrollBy(0, elemTop - offset);
4819 $window.scrollTo(0, 0);
4823 function scroll(hash) {
4824 hash = isString(hash) ? hash : $location.hash();
4827 // empty hash, scroll to the top of the page
4828 if (!hash) scrollTo(null);
4830 // element with given id
4831 else if ((elm = document.getElementById(hash))) scrollTo(elm);
4833 // first anchor with given name :-D
4834 else if ((elm = getFirstAnchor(document.getElementsByName(hash)))) scrollTo(elm);
4836 // no element and hash == 'top', scroll to the top of the page
4837 else if (hash === 'top') scrollTo(null);
4840 // does not scroll when user clicks on anchor link that is currently on
4841 // (no url change, no $location.hash() change), browser native does scroll
4842 if (autoScrollingEnabled) {
4843 $rootScope.$watch(function autoScrollWatch() {return $location.hash();},
4844 function autoScrollWatchAction(newVal, oldVal) {
4845 // skip the initial scroll if $location.hash is empty
4846 if (newVal === oldVal && newVal === '') return;
4848 jqLiteDocumentLoaded(function() {
4849 $rootScope.$evalAsync(scroll);
4858 var $animateMinErr = minErr('$animate');
4859 var ELEMENT_NODE = 1;
4860 var NG_ANIMATE_CLASSNAME = 'ng-animate';
4862 function mergeClasses(a,b) {
4863 if (!a && !b) return '';
4866 if (isArray(a)) a = a.join(' ');
4867 if (isArray(b)) b = b.join(' ');
4871 function extractElementNode(element) {
4872 for (var i = 0; i < element.length; i++) {
4873 var elm = element[i];
4874 if (elm.nodeType === ELEMENT_NODE) {
4880 function splitClasses(classes) {
4881 if (isString(classes)) {
4882 classes = classes.split(' ');
4885 // Use createMap() to prevent class assumptions involving property names in
4887 var obj = createMap();
4888 forEach(classes, function(klass) {
4889 // sometimes the split leaves empty string values
4890 // incase extra spaces were applied to the options
4898 // if any other type of options value besides an Object value is
4899 // passed into the $animate.method() animation then this helper code
4900 // will be run which will ignore it. While this patch is not the
4901 // greatest solution to this, a lot of existing plugins depend on
4902 // $animate to either call the callback (< 1.2) or return a promise
4903 // that can be changed. This helper function ensures that the options
4904 // are wiped clean incase a callback function is provided.
4905 function prepareAnimateOptions(options) {
4906 return isObject(options)
4911 var $$CoreAnimateJsProvider = function() {
4912 this.$get = function() {};
4915 // this is prefixed with Core since it conflicts with
4916 // the animateQueueProvider defined in ngAnimate/animateQueue.js
4917 var $$CoreAnimateQueueProvider = function() {
4918 var postDigestQueue = new HashMap();
4919 var postDigestElements = [];
4921 this.$get = ['$$AnimateRunner', '$rootScope',
4922 function($$AnimateRunner, $rootScope) {
4929 push: function(element, event, options, domOperation) {
4930 domOperation && domOperation();
4932 options = options || {};
4933 options.from && element.css(options.from);
4934 options.to && element.css(options.to);
4936 if (options.addClass || options.removeClass) {
4937 addRemoveClassesPostDigest(element, options.addClass, options.removeClass);
4940 var runner = new $$AnimateRunner(); // jshint ignore:line
4942 // since there are no animations to run the runner needs to be
4943 // notified that the animation call is complete.
4950 function updateData(data, classes, value) {
4951 var changed = false;
4953 classes = isString(classes) ? classes.split(' ') :
4954 isArray(classes) ? classes : [];
4955 forEach(classes, function(className) {
4958 data[className] = value;
4965 function handleCSSClassChanges() {
4966 forEach(postDigestElements, function(element) {
4967 var data = postDigestQueue.get(element);
4969 var existing = splitClasses(element.attr('class'));
4972 forEach(data, function(status, className) {
4973 var hasClass = !!existing[className];
4974 if (status !== hasClass) {
4976 toAdd += (toAdd.length ? ' ' : '') + className;
4978 toRemove += (toRemove.length ? ' ' : '') + className;
4983 forEach(element, function(elm) {
4984 toAdd && jqLiteAddClass(elm, toAdd);
4985 toRemove && jqLiteRemoveClass(elm, toRemove);
4987 postDigestQueue.remove(element);
4990 postDigestElements.length = 0;
4994 function addRemoveClassesPostDigest(element, add, remove) {
4995 var data = postDigestQueue.get(element) || {};
4997 var classesAdded = updateData(data, add, true);
4998 var classesRemoved = updateData(data, remove, false);
5000 if (classesAdded || classesRemoved) {
5002 postDigestQueue.put(element, data);
5003 postDigestElements.push(element);
5005 if (postDigestElements.length === 1) {
5006 $rootScope.$$postDigest(handleCSSClassChanges);
5015 * @name $animateProvider
5018 * Default implementation of $animate that doesn't perform any animations, instead just
5019 * synchronously performs DOM updates and resolves the returned runner promise.
5021 * In order to enable animations the `ngAnimate` module has to be loaded.
5023 * To see the functional implementation check out `src/ngAnimate/animate.js`.
5025 var $AnimateProvider = ['$provide', function($provide) {
5026 var provider = this;
5028 this.$$registeredAnimations = Object.create(null);
5032 * @name $animateProvider#register
5035 * Registers a new injectable animation factory function. The factory function produces the
5036 * animation object which contains callback functions for each event that is expected to be
5039 * * `eventFn`: `function(element, ... , doneFunction, options)`
5040 * The element to animate, the `doneFunction` and the options fed into the animation. Depending
5041 * on the type of animation additional arguments will be injected into the animation function. The
5042 * list below explains the function signatures for the different animation methods:
5044 * - setClass: function(element, addedClasses, removedClasses, doneFunction, options)
5045 * - addClass: function(element, addedClasses, doneFunction, options)
5046 * - removeClass: function(element, removedClasses, doneFunction, options)
5047 * - enter, leave, move: function(element, doneFunction, options)
5048 * - animate: function(element, fromStyles, toStyles, doneFunction, options)
5050 * Make sure to trigger the `doneFunction` once the animation is fully complete.
5054 * //enter, leave, move signature
5055 * eventFn : function(element, done, options) {
5056 * //code to run the animation
5057 * //once complete, then run done()
5058 * return function endFunction(wasCancelled) {
5059 * //code to cancel the animation
5065 * @param {string} name The name of the animation (this is what the class-based CSS value will be compared to).
5066 * @param {Function} factory The factory function that will be executed to return the animation
5069 this.register = function(name, factory) {
5070 if (name && name.charAt(0) !== '.') {
5071 throw $animateMinErr('notcsel', "Expecting class selector starting with '.' got '{0}'.", name);
5074 var key = name + '-animation';
5075 provider.$$registeredAnimations[name.substr(1)] = key;
5076 $provide.factory(key, factory);
5081 * @name $animateProvider#classNameFilter
5084 * Sets and/or returns the CSS class regular expression that is checked when performing
5085 * an animation. Upon bootstrap the classNameFilter value is not set at all and will
5086 * therefore enable $animate to attempt to perform an animation on any element that is triggered.
5087 * When setting the `classNameFilter` value, animations will only be performed on elements
5088 * that successfully match the filter expression. This in turn can boost performance
5089 * for low-powered devices as well as applications containing a lot of structural operations.
5090 * @param {RegExp=} expression The className expression which will be checked against all animations
5091 * @return {RegExp} The current CSS className expression value. If null then there is no expression value
5093 this.classNameFilter = function(expression) {
5094 if (arguments.length === 1) {
5095 this.$$classNameFilter = (expression instanceof RegExp) ? expression : null;
5096 if (this.$$classNameFilter) {
5097 var reservedRegex = new RegExp("(\\s+|\\/)" + NG_ANIMATE_CLASSNAME + "(\\s+|\\/)");
5098 if (reservedRegex.test(this.$$classNameFilter.toString())) {
5099 throw $animateMinErr('nongcls','$animateProvider.classNameFilter(regex) prohibits accepting a regex value which matches/contains the "{0}" CSS class.', NG_ANIMATE_CLASSNAME);
5104 return this.$$classNameFilter;
5107 this.$get = ['$$animateQueue', function($$animateQueue) {
5108 function domInsert(element, parentElement, afterElement) {
5109 // if for some reason the previous element was removed
5110 // from the dom sometime before this code runs then let's
5111 // just stick to using the parent element as the anchor
5113 var afterNode = extractElementNode(afterElement);
5114 if (afterNode && !afterNode.parentNode && !afterNode.previousElementSibling) {
5115 afterElement = null;
5118 afterElement ? afterElement.after(element) : parentElement.prepend(element);
5124 * @description The $animate service exposes a series of DOM utility methods that provide support
5125 * for animation hooks. The default behavior is the application of DOM operations, however,
5126 * when an animation is detected (and animations are enabled), $animate will do the heavy lifting
5127 * to ensure that animation runs with the triggered DOM operation.
5129 * By default $animate doesn't trigger any animations. This is because the `ngAnimate` module isn't
5130 * included and only when it is active then the animation hooks that `$animate` triggers will be
5131 * functional. Once active then all structural `ng-` directives will trigger animations as they perform
5132 * their DOM-related operations (enter, leave and move). Other directives such as `ngClass`,
5133 * `ngShow`, `ngHide` and `ngMessages` also provide support for animations.
5135 * It is recommended that the`$animate` service is always used when executing DOM-related procedures within directives.
5137 * To learn more about enabling animation support, click here to visit the
5138 * {@link ngAnimate ngAnimate module page}.
5141 // we don't call it directly since non-existant arguments may
5142 // be interpreted as null within the sub enabled function
5149 * @description Sets up an event listener to fire whenever the animation event (enter, leave, move, etc...)
5150 * has fired on the given element or among any of its children. Once the listener is fired, the provided callback
5151 * is fired with the following params:
5154 * $animate.on('enter', container,
5155 * function callback(element, phase) {
5156 * // cool we detected an enter animation within the container
5161 * @param {string} event the animation event that will be captured (e.g. enter, leave, move, addClass, removeClass, etc...)
5162 * @param {DOMElement} container the container element that will capture each of the animation events that are fired on itself
5163 * as well as among its children
5164 * @param {Function} callback the callback function that will be fired when the listener is triggered
5166 * The arguments present in the callback function are:
5167 * * `element` - The captured DOM element that the animation was fired on.
5168 * * `phase` - The phase of the animation. The two possible phases are **start** (when the animation starts) and **close** (when it ends).
5170 on: $$animateQueue.on,
5175 * @name $animate#off
5177 * @description Deregisters an event listener based on the event which has been associated with the provided element. This method
5178 * can be used in three different ways depending on the arguments:
5181 * // remove all the animation event listeners listening for `enter`
5182 * $animate.off('enter');
5184 * // remove all the animation event listeners listening for `enter` on the given element and its children
5185 * $animate.off('enter', container);
5187 * // remove the event listener function provided by `listenerFn` that is set
5188 * // to listen for `enter` on the given `element` as well as its children
5189 * $animate.off('enter', container, callback);
5192 * @param {string} event the animation event (e.g. enter, leave, move, addClass, removeClass, etc...)
5193 * @param {DOMElement=} container the container element the event listener was placed on
5194 * @param {Function=} callback the callback function that was registered as the listener
5196 off: $$animateQueue.off,
5200 * @name $animate#pin
5202 * @description Associates the provided element with a host parent element to allow the element to be animated even if it exists
5203 * outside of the DOM structure of the Angular application. By doing so, any animation triggered via `$animate` can be issued on the
5204 * element despite being outside the realm of the application or within another application. Say for example if the application
5205 * was bootstrapped on an element that is somewhere inside of the `<body>` tag, but we wanted to allow for an element to be situated
5206 * as a direct child of `document.body`, then this can be achieved by pinning the element via `$animate.pin(element)`. Keep in mind
5207 * that calling `$animate.pin(element, parentElement)` will not actually insert into the DOM anywhere; it will just create the association.
5209 * Note that this feature is only active when the `ngAnimate` module is used.
5211 * @param {DOMElement} element the external element that will be pinned
5212 * @param {DOMElement} parentElement the host parent element that will be associated with the external element
5214 pin: $$animateQueue.pin,
5219 * @name $animate#enabled
5221 * @description Used to get and set whether animations are enabled or not on the entire application or on an element and its children. This
5222 * function can be called in four ways:
5225 * // returns true or false
5226 * $animate.enabled();
5228 * // changes the enabled state for all animations
5229 * $animate.enabled(false);
5230 * $animate.enabled(true);
5232 * // returns true or false if animations are enabled for an element
5233 * $animate.enabled(element);
5235 * // changes the enabled state for an element and its children
5236 * $animate.enabled(element, true);
5237 * $animate.enabled(element, false);
5240 * @param {DOMElement=} element the element that will be considered for checking/setting the enabled state
5241 * @param {boolean=} enabled whether or not the animations will be enabled for the element
5243 * @return {boolean} whether or not animations are enabled
5245 enabled: $$animateQueue.enabled,
5249 * @name $animate#cancel
5251 * @description Cancels the provided animation.
5253 * @param {Promise} animationPromise The animation promise that is returned when an animation is started.
5255 cancel: function(runner) {
5256 runner.end && runner.end();
5262 * @name $animate#enter
5264 * @description Inserts the element into the DOM either after the `after` element (if provided) or
5265 * as the first child within the `parent` element and then triggers an animation.
5266 * A promise is returned that will be resolved during the next digest once the animation
5269 * @param {DOMElement} element the element which will be inserted into the DOM
5270 * @param {DOMElement} parent the parent element which will append the element as
5271 * a child (so long as the after element is not present)
5272 * @param {DOMElement=} after the sibling element after which the element will be appended
5273 * @param {object=} options an optional collection of options/styles that will be applied to the element
5275 * @return {Promise} the animation callback promise
5277 enter: function(element, parent, after, options) {
5278 parent = parent && jqLite(parent);
5279 after = after && jqLite(after);
5280 parent = parent || after.parent();
5281 domInsert(element, parent, after);
5282 return $$animateQueue.push(element, 'enter', prepareAnimateOptions(options));
5288 * @name $animate#move
5290 * @description Inserts (moves) the element into its new position in the DOM either after
5291 * the `after` element (if provided) or as the first child within the `parent` element
5292 * and then triggers an animation. A promise is returned that will be resolved
5293 * during the next digest once the animation has completed.
5295 * @param {DOMElement} element the element which will be moved into the new DOM position
5296 * @param {DOMElement} parent the parent element which will append the element as
5297 * a child (so long as the after element is not present)
5298 * @param {DOMElement=} after the sibling element after which the element will be appended
5299 * @param {object=} options an optional collection of options/styles that will be applied to the element
5301 * @return {Promise} the animation callback promise
5303 move: function(element, parent, after, options) {
5304 parent = parent && jqLite(parent);
5305 after = after && jqLite(after);
5306 parent = parent || after.parent();
5307 domInsert(element, parent, after);
5308 return $$animateQueue.push(element, 'move', prepareAnimateOptions(options));
5313 * @name $animate#leave
5315 * @description Triggers an animation and then removes the element from the DOM.
5316 * When the function is called a promise is returned that will be resolved during the next
5317 * digest once the animation has completed.
5319 * @param {DOMElement} element the element which will be removed from the DOM
5320 * @param {object=} options an optional collection of options/styles that will be applied to the element
5322 * @return {Promise} the animation callback promise
5324 leave: function(element, options) {
5325 return $$animateQueue.push(element, 'leave', prepareAnimateOptions(options), function() {
5332 * @name $animate#addClass
5335 * @description Triggers an addClass animation surrounding the addition of the provided CSS class(es). Upon
5336 * execution, the addClass operation will only be handled after the next digest and it will not trigger an
5337 * animation if element already contains the CSS class or if the class is removed at a later step.
5338 * Note that class-based animations are treated differently compared to structural animations
5339 * (like enter, move and leave) since the CSS classes may be added/removed at different points
5340 * depending if CSS or JavaScript animations are used.
5342 * @param {DOMElement} element the element which the CSS classes will be applied to
5343 * @param {string} className the CSS class(es) that will be added (multiple classes are separated via spaces)
5344 * @param {object=} options an optional collection of options/styles that will be applied to the element
5346 * @return {Promise} the animation callback promise
5348 addClass: function(element, className, options) {
5349 options = prepareAnimateOptions(options);
5350 options.addClass = mergeClasses(options.addclass, className);
5351 return $$animateQueue.push(element, 'addClass', options);
5356 * @name $animate#removeClass
5359 * @description Triggers a removeClass animation surrounding the removal of the provided CSS class(es). Upon
5360 * execution, the removeClass operation will only be handled after the next digest and it will not trigger an
5361 * animation if element does not contain the CSS class or if the class is added at a later step.
5362 * Note that class-based animations are treated differently compared to structural animations
5363 * (like enter, move and leave) since the CSS classes may be added/removed at different points
5364 * depending if CSS or JavaScript animations are used.
5366 * @param {DOMElement} element the element which the CSS classes will be applied to
5367 * @param {string} className the CSS class(es) that will be removed (multiple classes are separated via spaces)
5368 * @param {object=} options an optional collection of options/styles that will be applied to the element
5370 * @return {Promise} the animation callback promise
5372 removeClass: function(element, className, options) {
5373 options = prepareAnimateOptions(options);
5374 options.removeClass = mergeClasses(options.removeClass, className);
5375 return $$animateQueue.push(element, 'removeClass', options);
5380 * @name $animate#setClass
5383 * @description Performs both the addition and removal of a CSS classes on an element and (during the process)
5384 * triggers an animation surrounding the class addition/removal. Much like `$animate.addClass` and
5385 * `$animate.removeClass`, `setClass` will only evaluate the classes being added/removed once a digest has
5386 * passed. Note that class-based animations are treated differently compared to structural animations
5387 * (like enter, move and leave) since the CSS classes may be added/removed at different points
5388 * depending if CSS or JavaScript animations are used.
5390 * @param {DOMElement} element the element which the CSS classes will be applied to
5391 * @param {string} add the CSS class(es) that will be added (multiple classes are separated via spaces)
5392 * @param {string} remove the CSS class(es) that will be removed (multiple classes are separated via spaces)
5393 * @param {object=} options an optional collection of options/styles that will be applied to the element
5395 * @return {Promise} the animation callback promise
5397 setClass: function(element, add, remove, options) {
5398 options = prepareAnimateOptions(options);
5399 options.addClass = mergeClasses(options.addClass, add);
5400 options.removeClass = mergeClasses(options.removeClass, remove);
5401 return $$animateQueue.push(element, 'setClass', options);
5406 * @name $animate#animate
5409 * @description Performs an inline animation on the element which applies the provided to and from CSS styles to the element.
5410 * If any detected CSS transition, keyframe or JavaScript matches the provided className value, then the animation will take
5411 * on the provided styles. For example, if a transition animation is set for the given className, then the provided `from` and
5412 * `to` styles will be applied alongside the given transition. If the CSS style provided in `from` does not have a corresponding
5413 * style in `to`, the style in `from` is applied immediately, and no animation is run.
5414 * If a JavaScript animation is detected then the provided styles will be given in as function parameters into the `animate`
5415 * method (or as part of the `options` parameter):
5418 * ngModule.animation('.my-inline-animation', function() {
5420 * animate : function(element, from, to, done, options) {
5428 * @param {DOMElement} element the element which the CSS styles will be applied to
5429 * @param {object} from the from (starting) CSS styles that will be applied to the element and across the animation.
5430 * @param {object} to the to (destination) CSS styles that will be applied to the element and across the animation.
5431 * @param {string=} className an optional CSS class that will be applied to the element for the duration of the animation. If
5432 * this value is left as empty then a CSS class of `ng-inline-animate` will be applied to the element.
5433 * (Note that if no animation is detected then this value will not be appplied to the element.)
5434 * @param {object=} options an optional collection of options/styles that will be applied to the element
5436 * @return {Promise} the animation callback promise
5438 animate: function(element, from, to, className, options) {
5439 options = prepareAnimateOptions(options);
5440 options.from = options.from ? extend(options.from, from) : from;
5441 options.to = options.to ? extend(options.to, to) : to;
5443 className = className || 'ng-inline-animate';
5444 options.tempClasses = mergeClasses(options.tempClasses, className);
5445 return $$animateQueue.push(element, 'animate', options);
5451 var $$AnimateAsyncRunFactoryProvider = function() {
5452 this.$get = ['$$rAF', function($$rAF) {
5455 function waitForTick(fn) {
5457 if (waitQueue.length > 1) return;
5459 for (var i = 0; i < waitQueue.length; i++) {
5468 waitForTick(function() {
5471 return function(callback) {
5472 passed ? callback() : waitForTick(callback);
5478 var $$AnimateRunnerFactoryProvider = function() {
5479 this.$get = ['$q', '$sniffer', '$$animateAsyncRun', '$document', '$timeout',
5480 function($q, $sniffer, $$animateAsyncRun, $document, $timeout) {
5482 var INITIAL_STATE = 0;
5483 var DONE_PENDING_STATE = 1;
5484 var DONE_COMPLETE_STATE = 2;
5486 AnimateRunner.chain = function(chain, callback) {
5491 if (index === chain.length) {
5496 chain[index](function(response) {
5497 if (response === false) {
5507 AnimateRunner.all = function(runners, callback) {
5510 forEach(runners, function(runner) {
5511 runner.done(onProgress);
5514 function onProgress(response) {
5515 status = status && response;
5516 if (++count === runners.length) {
5522 function AnimateRunner(host) {
5525 var rafTick = $$animateAsyncRun();
5526 var timeoutTick = function(fn) {
5527 $timeout(fn, 0, false);
5530 this._doneCallbacks = [];
5531 this._tick = function(fn) {
5532 var doc = $document[0];
5534 // the document may not be ready or attached
5535 // to the module for some internal tests
5536 if (doc && doc.hidden) {
5545 AnimateRunner.prototype = {
5546 setHost: function(host) {
5547 this.host = host || {};
5550 done: function(fn) {
5551 if (this._state === DONE_COMPLETE_STATE) {
5554 this._doneCallbacks.push(fn);
5560 getPromise: function() {
5561 if (!this.promise) {
5563 this.promise = $q(function(resolve, reject) {
5564 self.done(function(status) {
5565 status === false ? reject() : resolve();
5569 return this.promise;
5572 then: function(resolveHandler, rejectHandler) {
5573 return this.getPromise().then(resolveHandler, rejectHandler);
5576 'catch': function(handler) {
5577 return this.getPromise()['catch'](handler);
5580 'finally': function(handler) {
5581 return this.getPromise()['finally'](handler);
5585 if (this.host.pause) {
5590 resume: function() {
5591 if (this.host.resume) {
5597 if (this.host.end) {
5600 this._resolve(true);
5603 cancel: function() {
5604 if (this.host.cancel) {
5607 this._resolve(false);
5610 complete: function(response) {
5612 if (self._state === INITIAL_STATE) {
5613 self._state = DONE_PENDING_STATE;
5614 self._tick(function() {
5615 self._resolve(response);
5620 _resolve: function(response) {
5621 if (this._state !== DONE_COMPLETE_STATE) {
5622 forEach(this._doneCallbacks, function(fn) {
5625 this._doneCallbacks.length = 0;
5626 this._state = DONE_COMPLETE_STATE;
5631 return AnimateRunner;
5641 * This is the core version of `$animateCss`. By default, only when the `ngAnimate` is included,
5642 * then the `$animateCss` service will actually perform animations.
5644 * Click here {@link ngAnimate.$animateCss to read the documentation for $animateCss}.
5646 var $CoreAnimateCssProvider = function() {
5647 this.$get = ['$$rAF', '$q', '$$AnimateRunner', function($$rAF, $q, $$AnimateRunner) {
5649 return function(element, initialOptions) {
5650 // all of the animation functions should create
5651 // a copy of the options data, however, if a
5652 // parent service has already created a copy then
5653 // we should stick to using that
5654 var options = initialOptions || {};
5655 if (!options.$$prepared) {
5656 options = copy(options);
5659 // there is no point in applying the styles since
5660 // there is no animation that goes on at all in
5661 // this version of $animateCss.
5662 if (options.cleanupStyles) {
5663 options.from = options.to = null;
5667 element.css(options.from);
5668 options.from = null;
5671 /* jshint newcap: false*/
5672 var closed, runner = new $$AnimateRunner();
5680 applyAnimationContents();
5689 function applyAnimationContents() {
5690 if (options.addClass) {
5691 element.addClass(options.addClass);
5692 options.addClass = null;
5694 if (options.removeClass) {
5695 element.removeClass(options.removeClass);
5696 options.removeClass = null;
5699 element.css(options.to);
5707 /* global stripHash: true */
5710 * ! This is a private undocumented service !
5715 * This object has two goals:
5717 * - hide all the global state in the browser caused by the window object
5718 * - abstract away all the browser specific features and inconsistencies
5720 * For tests we provide {@link ngMock.$browser mock implementation} of the `$browser`
5721 * service, which can be used for convenient testing of the application without the interaction with
5722 * the real browser apis.
5725 * @param {object} window The global window object.
5726 * @param {object} document jQuery wrapped document.
5727 * @param {object} $log window.console or an object with the same interface.
5728 * @param {object} $sniffer $sniffer service
5730 function Browser(window, document, $log, $sniffer) {
5732 rawDocument = document[0],
5733 location = window.location,
5734 history = window.history,
5735 setTimeout = window.setTimeout,
5736 clearTimeout = window.clearTimeout,
5737 pendingDeferIds = {};
5739 self.isMock = false;
5741 var outstandingRequestCount = 0;
5742 var outstandingRequestCallbacks = [];
5744 // TODO(vojta): remove this temporary api
5745 self.$$completeOutstandingRequest = completeOutstandingRequest;
5746 self.$$incOutstandingRequestCount = function() { outstandingRequestCount++; };
5749 * Executes the `fn` function(supports currying) and decrements the `outstandingRequestCallbacks`
5750 * counter. If the counter reaches 0, all the `outstandingRequestCallbacks` are executed.
5752 function completeOutstandingRequest(fn) {
5754 fn.apply(null, sliceArgs(arguments, 1));
5756 outstandingRequestCount--;
5757 if (outstandingRequestCount === 0) {
5758 while (outstandingRequestCallbacks.length) {
5760 outstandingRequestCallbacks.pop()();
5769 function getHash(url) {
5770 var index = url.indexOf('#');
5771 return index === -1 ? '' : url.substr(index);
5776 * Note: this method is used only by scenario runner
5777 * TODO(vojta): prefix this method with $$ ?
5778 * @param {function()} callback Function that will be called when no outstanding request
5780 self.notifyWhenNoOutstandingRequests = function(callback) {
5781 if (outstandingRequestCount === 0) {
5784 outstandingRequestCallbacks.push(callback);
5788 //////////////////////////////////////////////////////////////
5790 //////////////////////////////////////////////////////////////
5792 var cachedState, lastHistoryState,
5793 lastBrowserUrl = location.href,
5794 baseElement = document.find('base'),
5795 pendingLocation = null;
5798 lastHistoryState = cachedState;
5801 * @name $browser#url
5805 * Without any argument, this method just returns current value of location.href.
5808 * With at least one argument, this method sets url to new value.
5809 * If html5 history api supported, pushState/replaceState is used, otherwise
5810 * location.href/location.replace is used.
5811 * Returns its own instance to allow chaining
5813 * NOTE: this api is intended for use only by the $location service. Please use the
5814 * {@link ng.$location $location service} to change url.
5816 * @param {string} url New url (when used as setter)
5817 * @param {boolean=} replace Should new url replace current history record?
5818 * @param {object=} state object to use with pushState/replaceState
5820 self.url = function(url, replace, state) {
5821 // In modern browsers `history.state` is `null` by default; treating it separately
5822 // from `undefined` would cause `$browser.url('/foo')` to change `history.state`
5823 // to undefined via `pushState`. Instead, let's change `undefined` to `null` here.
5824 if (isUndefined(state)) {
5828 // Android Browser BFCache causes location, history reference to become stale.
5829 if (location !== window.location) location = window.location;
5830 if (history !== window.history) history = window.history;
5834 var sameState = lastHistoryState === state;
5836 // Don't change anything if previous and current URLs and states match. This also prevents
5837 // IE<10 from getting into redirect loop when in LocationHashbangInHtml5Url mode.
5838 // See https://github.com/angular/angular.js/commit/ffb2701
5839 if (lastBrowserUrl === url && (!$sniffer.history || sameState)) {
5842 var sameBase = lastBrowserUrl && stripHash(lastBrowserUrl) === stripHash(url);
5843 lastBrowserUrl = url;
5844 lastHistoryState = state;
5845 // Don't use history API if only the hash changed
5846 // due to a bug in IE10/IE11 which leads
5847 // to not firing a `hashchange` nor `popstate` event
5848 // in some cases (see #9143).
5849 if ($sniffer.history && (!sameBase || !sameState)) {
5850 history[replace ? 'replaceState' : 'pushState'](state, '', url);
5852 // Do the assignment again so that those two variables are referentially identical.
5853 lastHistoryState = cachedState;
5855 if (!sameBase || pendingLocation) {
5856 pendingLocation = url;
5859 location.replace(url);
5860 } else if (!sameBase) {
5861 location.href = url;
5863 location.hash = getHash(url);
5865 if (location.href !== url) {
5866 pendingLocation = url;
5872 // - pendingLocation is needed as browsers don't allow to read out
5873 // the new location.href if a reload happened or if there is a bug like in iOS 9 (see
5874 // https://openradar.appspot.com/22186109).
5875 // - the replacement is a workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=407172
5876 return pendingLocation || location.href.replace(/%27/g,"'");
5881 * @name $browser#state
5884 * This method is a getter.
5886 * Return history.state or null if history.state is undefined.
5888 * @returns {object} state
5890 self.state = function() {
5894 var urlChangeListeners = [],
5895 urlChangeInit = false;
5897 function cacheStateAndFireUrlChange() {
5898 pendingLocation = null;
5903 function getCurrentState() {
5905 return history.state;
5907 // MSIE can reportedly throw when there is no state (UNCONFIRMED).
5911 // This variable should be used *only* inside the cacheState function.
5912 var lastCachedState = null;
5913 function cacheState() {
5914 // This should be the only place in $browser where `history.state` is read.
5915 cachedState = getCurrentState();
5916 cachedState = isUndefined(cachedState) ? null : cachedState;
5918 // Prevent callbacks fo fire twice if both hashchange & popstate were fired.
5919 if (equals(cachedState, lastCachedState)) {
5920 cachedState = lastCachedState;
5922 lastCachedState = cachedState;
5925 function fireUrlChange() {
5926 if (lastBrowserUrl === self.url() && lastHistoryState === cachedState) {
5930 lastBrowserUrl = self.url();
5931 lastHistoryState = cachedState;
5932 forEach(urlChangeListeners, function(listener) {
5933 listener(self.url(), cachedState);
5938 * @name $browser#onUrlChange
5941 * Register callback function that will be called, when url changes.
5943 * It's only called when the url is changed from outside of angular:
5944 * - user types different url into address bar
5945 * - user clicks on history (forward/back) button
5946 * - user clicks on a link
5948 * It's not called when url is changed by $browser.url() method
5950 * The listener gets called with new url as parameter.
5952 * NOTE: this api is intended for use only by the $location service. Please use the
5953 * {@link ng.$location $location service} to monitor url changes in angular apps.
5955 * @param {function(string)} listener Listener function to be called when url changes.
5956 * @return {function(string)} Returns the registered listener fn - handy if the fn is anonymous.
5958 self.onUrlChange = function(callback) {
5959 // TODO(vojta): refactor to use node's syntax for events
5960 if (!urlChangeInit) {
5961 // We listen on both (hashchange/popstate) when available, as some browsers (e.g. Opera)
5962 // don't fire popstate when user change the address bar and don't fire hashchange when url
5963 // changed by push/replaceState
5965 // html5 history api - popstate event
5966 if ($sniffer.history) jqLite(window).on('popstate', cacheStateAndFireUrlChange);
5968 jqLite(window).on('hashchange', cacheStateAndFireUrlChange);
5970 urlChangeInit = true;
5973 urlChangeListeners.push(callback);
5979 * Remove popstate and hashchange handler from window.
5981 * NOTE: this api is intended for use only by $rootScope.
5983 self.$$applicationDestroyed = function() {
5984 jqLite(window).off('hashchange popstate', cacheStateAndFireUrlChange);
5988 * Checks whether the url has changed outside of Angular.
5989 * Needs to be exported to be able to check for changes that have been done in sync,
5990 * as hashchange/popstate events fire in async.
5992 self.$$checkUrlChange = fireUrlChange;
5994 //////////////////////////////////////////////////////////////
5996 //////////////////////////////////////////////////////////////
5999 * @name $browser#baseHref
6002 * Returns current <base href>
6003 * (always relative - without domain)
6005 * @returns {string} The current base href
6007 self.baseHref = function() {
6008 var href = baseElement.attr('href');
6009 return href ? href.replace(/^(https?\:)?\/\/[^\/]*/, '') : '';
6013 * @name $browser#defer
6014 * @param {function()} fn A function, who's execution should be deferred.
6015 * @param {number=} [delay=0] of milliseconds to defer the function execution.
6016 * @returns {*} DeferId that can be used to cancel the task via `$browser.defer.cancel()`.
6019 * Executes a fn asynchronously via `setTimeout(fn, delay)`.
6021 * Unlike when calling `setTimeout` directly, in test this function is mocked and instead of using
6022 * `setTimeout` in tests, the fns are queued in an array, which can be programmatically flushed
6023 * via `$browser.defer.flush()`.
6026 self.defer = function(fn, delay) {
6028 outstandingRequestCount++;
6029 timeoutId = setTimeout(function() {
6030 delete pendingDeferIds[timeoutId];
6031 completeOutstandingRequest(fn);
6033 pendingDeferIds[timeoutId] = true;
6039 * @name $browser#defer.cancel
6042 * Cancels a deferred task identified with `deferId`.
6044 * @param {*} deferId Token returned by the `$browser.defer` function.
6045 * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully
6048 self.defer.cancel = function(deferId) {
6049 if (pendingDeferIds[deferId]) {
6050 delete pendingDeferIds[deferId];
6051 clearTimeout(deferId);
6052 completeOutstandingRequest(noop);
6060 function $BrowserProvider() {
6061 this.$get = ['$window', '$log', '$sniffer', '$document',
6062 function($window, $log, $sniffer, $document) {
6063 return new Browser($window, $document, $log, $sniffer);
6069 * @name $cacheFactory
6072 * Factory that constructs {@link $cacheFactory.Cache Cache} objects and gives access to
6077 * var cache = $cacheFactory('cacheId');
6078 * expect($cacheFactory.get('cacheId')).toBe(cache);
6079 * expect($cacheFactory.get('noSuchCacheId')).not.toBeDefined();
6081 * cache.put("key", "value");
6082 * cache.put("another key", "another value");
6084 * // We've specified no options on creation
6085 * expect(cache.info()).toEqual({id: 'cacheId', size: 2});
6090 * @param {string} cacheId Name or id of the newly created cache.
6091 * @param {object=} options Options object that specifies the cache behavior. Properties:
6093 * - `{number=}` `capacity` — turns the cache into LRU cache.
6095 * @returns {object} Newly created cache object with the following set of methods:
6097 * - `{object}` `info()` — Returns id, size, and options of cache.
6098 * - `{{*}}` `put({string} key, {*} value)` — Puts a new key-value pair into the cache and returns
6100 * - `{{*}}` `get({string} key)` — Returns cached value for `key` or undefined for cache miss.
6101 * - `{void}` `remove({string} key)` — Removes a key-value pair from the cache.
6102 * - `{void}` `removeAll()` — Removes all cached values.
6103 * - `{void}` `destroy()` — Removes references to this cache from $cacheFactory.
6106 <example module="cacheExampleApp">
6107 <file name="index.html">
6108 <div ng-controller="CacheController">
6109 <input ng-model="newCacheKey" placeholder="Key">
6110 <input ng-model="newCacheValue" placeholder="Value">
6111 <button ng-click="put(newCacheKey, newCacheValue)">Cache</button>
6113 <p ng-if="keys.length">Cached Values</p>
6114 <div ng-repeat="key in keys">
6115 <span ng-bind="key"></span>
6117 <b ng-bind="cache.get(key)"></b>
6121 <div ng-repeat="(key, value) in cache.info()">
6122 <span ng-bind="key"></span>
6124 <b ng-bind="value"></b>
6128 <file name="script.js">
6129 angular.module('cacheExampleApp', []).
6130 controller('CacheController', ['$scope', '$cacheFactory', function($scope, $cacheFactory) {
6132 $scope.cache = $cacheFactory('cacheId');
6133 $scope.put = function(key, value) {
6134 if (angular.isUndefined($scope.cache.get(key))) {
6135 $scope.keys.push(key);
6137 $scope.cache.put(key, angular.isUndefined(value) ? null : value);
6141 <file name="style.css">
6148 function $CacheFactoryProvider() {
6150 this.$get = function() {
6153 function cacheFactory(cacheId, options) {
6154 if (cacheId in caches) {
6155 throw minErr('$cacheFactory')('iid', "CacheId '{0}' is already taken!", cacheId);
6159 stats = extend({}, options, {id: cacheId}),
6161 capacity = (options && options.capacity) || Number.MAX_VALUE,
6162 lruHash = createMap(),
6168 * @name $cacheFactory.Cache
6171 * A cache object used to store and retrieve data, primarily used by
6172 * {@link $http $http} and the {@link ng.directive:script script} directive to cache
6173 * templates and other data.
6176 * angular.module('superCache')
6177 * .factory('superCache', ['$cacheFactory', function($cacheFactory) {
6178 * return $cacheFactory('super-cache');
6185 * it('should behave like a cache', inject(function(superCache) {
6186 * superCache.put('key', 'value');
6187 * superCache.put('another key', 'another value');
6189 * expect(superCache.info()).toEqual({
6190 * id: 'super-cache',
6194 * superCache.remove('another key');
6195 * expect(superCache.get('another key')).toBeUndefined();
6197 * superCache.removeAll();
6198 * expect(superCache.info()).toEqual({
6199 * id: 'super-cache',
6205 return caches[cacheId] = {
6209 * @name $cacheFactory.Cache#put
6213 * Inserts a named entry into the {@link $cacheFactory.Cache Cache} object to be
6214 * retrieved later, and incrementing the size of the cache if the key was not already
6215 * present in the cache. If behaving like an LRU cache, it will also remove stale
6216 * entries from the set.
6218 * It will not insert undefined values into the cache.
6220 * @param {string} key the key under which the cached data is stored.
6221 * @param {*} value the value to store alongside the key. If it is undefined, the key
6222 * will not be stored.
6223 * @returns {*} the value stored.
6225 put: function(key, value) {
6226 if (isUndefined(value)) return;
6227 if (capacity < Number.MAX_VALUE) {
6228 var lruEntry = lruHash[key] || (lruHash[key] = {key: key});
6233 if (!(key in data)) size++;
6236 if (size > capacity) {
6237 this.remove(staleEnd.key);
6245 * @name $cacheFactory.Cache#get
6249 * Retrieves named data stored in the {@link $cacheFactory.Cache Cache} object.
6251 * @param {string} key the key of the data to be retrieved
6252 * @returns {*} the value stored.
6254 get: function(key) {
6255 if (capacity < Number.MAX_VALUE) {
6256 var lruEntry = lruHash[key];
6258 if (!lruEntry) return;
6269 * @name $cacheFactory.Cache#remove
6273 * Removes an entry from the {@link $cacheFactory.Cache Cache} object.
6275 * @param {string} key the key of the entry to be removed
6277 remove: function(key) {
6278 if (capacity < Number.MAX_VALUE) {
6279 var lruEntry = lruHash[key];
6281 if (!lruEntry) return;
6283 if (lruEntry == freshEnd) freshEnd = lruEntry.p;
6284 if (lruEntry == staleEnd) staleEnd = lruEntry.n;
6285 link(lruEntry.n,lruEntry.p);
6287 delete lruHash[key];
6290 if (!(key in data)) return;
6299 * @name $cacheFactory.Cache#removeAll
6303 * Clears the cache object of any entries.
6305 removeAll: function() {
6308 lruHash = createMap();
6309 freshEnd = staleEnd = null;
6315 * @name $cacheFactory.Cache#destroy
6319 * Destroys the {@link $cacheFactory.Cache Cache} object entirely,
6320 * removing it from the {@link $cacheFactory $cacheFactory} set.
6322 destroy: function() {
6326 delete caches[cacheId];
6332 * @name $cacheFactory.Cache#info
6336 * Retrieve information regarding a particular {@link $cacheFactory.Cache Cache}.
6338 * @returns {object} an object with the following properties:
6340 * <li>**id**: the id of the cache instance</li>
6341 * <li>**size**: the number of entries kept in the cache instance</li>
6342 * <li>**...**: any additional properties from the options object when creating the
6347 return extend({}, stats, {size: size});
6353 * makes the `entry` the freshEnd of the LRU linked list
6355 function refresh(entry) {
6356 if (entry != freshEnd) {
6359 } else if (staleEnd == entry) {
6363 link(entry.n, entry.p);
6364 link(entry, freshEnd);
6372 * bidirectionally links two entries of the LRU linked list
6374 function link(nextEntry, prevEntry) {
6375 if (nextEntry != prevEntry) {
6376 if (nextEntry) nextEntry.p = prevEntry; //p stands for previous, 'prev' didn't minify
6377 if (prevEntry) prevEntry.n = nextEntry; //n stands for next, 'next' didn't minify
6385 * @name $cacheFactory#info
6388 * Get information about all the caches that have been created
6390 * @returns {Object} - key-value map of `cacheId` to the result of calling `cache#info`
6392 cacheFactory.info = function() {
6394 forEach(caches, function(cache, cacheId) {
6395 info[cacheId] = cache.info();
6403 * @name $cacheFactory#get
6406 * Get access to a cache object by the `cacheId` used when it was created.
6408 * @param {string} cacheId Name or id of a cache to access.
6409 * @returns {object} Cache object identified by the cacheId or undefined if no such cache.
6411 cacheFactory.get = function(cacheId) {
6412 return caches[cacheId];
6416 return cacheFactory;
6422 * @name $templateCache
6425 * The first time a template is used, it is loaded in the template cache for quick retrieval. You
6426 * can load templates directly into the cache in a `script` tag, or by consuming the
6427 * `$templateCache` service directly.
6429 * Adding via the `script` tag:
6432 * <script type="text/ng-template" id="templateId.html">
6433 * <p>This is the content of the template</p>
6437 * **Note:** the `script` tag containing the template does not need to be included in the `head` of
6438 * the document, but it must be a descendent of the {@link ng.$rootElement $rootElement} (IE,
6439 * element with ng-app attribute), otherwise the template will be ignored.
6441 * Adding via the `$templateCache` service:
6444 * var myApp = angular.module('myApp', []);
6445 * myApp.run(function($templateCache) {
6446 * $templateCache.put('templateId.html', 'This is the content of the template');
6450 * To retrieve the template later, simply use it in your HTML:
6452 * <div ng-include=" 'templateId.html' "></div>
6455 * or get it via Javascript:
6457 * $templateCache.get('templateId.html')
6460 * See {@link ng.$cacheFactory $cacheFactory}.
6463 function $TemplateCacheProvider() {
6464 this.$get = ['$cacheFactory', function($cacheFactory) {
6465 return $cacheFactory('templates');
6469 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
6470 * Any commits to this file should be reviewed with security in mind. *
6471 * Changes to this file can potentially create security vulnerabilities. *
6472 * An approval from 2 Core members with history of modifying *
6473 * this file is required. *
6475 * Does the change somehow allow for arbitrary javascript to be executed? *
6476 * Or allows for someone to change the prototype of built-in objects? *
6477 * Or gives undesired access to variables likes document or window? *
6478 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
6480 /* ! VARIABLE/FUNCTION NAMING CONVENTIONS THAT APPLY TO THIS FILE!
6482 * DOM-related variables:
6484 * - "node" - DOM Node
6485 * - "element" - DOM Element or Node
6486 * - "$node" or "$element" - jqLite-wrapped node or element
6489 * Compiler related stuff:
6491 * - "linkFn" - linking fn of a single directive
6492 * - "nodeLinkFn" - function that aggregates all linking fns for a particular node
6493 * - "childLinkFn" - function that aggregates all linking fns for child nodes of a particular node
6494 * - "compositeLinkFn" - function that aggregates all linking fns for a compilation root (nodeList)
6504 * Compiles an HTML string or DOM into a template and produces a template function, which
6505 * can then be used to link {@link ng.$rootScope.Scope `scope`} and the template together.
6507 * The compilation is a process of walking the DOM tree and matching DOM elements to
6508 * {@link ng.$compileProvider#directive directives}.
6510 * <div class="alert alert-warning">
6511 * **Note:** This document is an in-depth reference of all directive options.
6512 * For a gentle introduction to directives with examples of common use cases,
6513 * see the {@link guide/directive directive guide}.
6516 * ## Comprehensive Directive API
6518 * There are many different options for a directive.
6520 * The difference resides in the return value of the factory function.
6521 * You can either return a "Directive Definition Object" (see below) that defines the directive properties,
6522 * or just the `postLink` function (all other properties will have the default values).
6524 * <div class="alert alert-success">
6525 * **Best Practice:** It's recommended to use the "directive definition object" form.
6528 * Here's an example directive declared with a Directive Definition Object:
6531 * var myModule = angular.module(...);
6533 * myModule.directive('directiveName', function factory(injectables) {
6534 * var directiveDefinitionObject = {
6536 * template: '<div></div>', // or // function(tElement, tAttrs) { ... },
6538 * // templateUrl: 'directive.html', // or // function(tElement, tAttrs) { ... },
6539 * transclude: false,
6541 * templateNamespace: 'html',
6543 * controller: function($scope, $element, $attrs, $transclude, otherInjectables) { ... },
6544 * controllerAs: 'stringIdentifier',
6545 * bindToController: false,
6546 * require: 'siblingDirectiveName', // or // ['^parentDirectiveName', '?optionalDirectiveName', '?^optionalParent'],
6547 * compile: function compile(tElement, tAttrs, transclude) {
6549 * pre: function preLink(scope, iElement, iAttrs, controller) { ... },
6550 * post: function postLink(scope, iElement, iAttrs, controller) { ... }
6553 * // return function postLink( ... ) { ... }
6557 * // pre: function preLink(scope, iElement, iAttrs, controller) { ... },
6558 * // post: function postLink(scope, iElement, iAttrs, controller) { ... }
6561 * // link: function postLink( ... ) { ... }
6563 * return directiveDefinitionObject;
6567 * <div class="alert alert-warning">
6568 * **Note:** Any unspecified options will use the default value. You can see the default values below.
6571 * Therefore the above can be simplified as:
6574 * var myModule = angular.module(...);
6576 * myModule.directive('directiveName', function factory(injectables) {
6577 * var directiveDefinitionObject = {
6578 * link: function postLink(scope, iElement, iAttrs) { ... }
6580 * return directiveDefinitionObject;
6582 * // return function postLink(scope, iElement, iAttrs) { ... }
6588 * ### Directive Definition Object
6590 * The directive definition object provides instructions to the {@link ng.$compile
6591 * compiler}. The attributes are:
6593 * #### `multiElement`
6594 * When this property is set to true, the HTML compiler will collect DOM nodes between
6595 * nodes with the attributes `directive-name-start` and `directive-name-end`, and group them
6596 * together as the directive elements. It is recommended that this feature be used on directives
6597 * which are not strictly behavioural (such as {@link ngClick}), and which
6598 * do not manipulate or replace child nodes (such as {@link ngInclude}).
6601 * When there are multiple directives defined on a single DOM element, sometimes it
6602 * is necessary to specify the order in which the directives are applied. The `priority` is used
6603 * to sort the directives before their `compile` functions get called. Priority is defined as a
6604 * number. Directives with greater numerical `priority` are compiled first. Pre-link functions
6605 * are also run in priority order, but post-link functions are run in reverse order. The order
6606 * of directives with the same priority is undefined. The default priority is `0`.
6609 * If set to true then the current `priority` will be the last set of directives
6610 * which will execute (any directives at the current priority will still execute
6611 * as the order of execution on same `priority` is undefined). Note that expressions
6612 * and other directives used in the directive's template will also be excluded from execution.
6615 * The scope property can be `true`, an object or a falsy value:
6617 * * **falsy:** No scope will be created for the directive. The directive will use its parent's scope.
6619 * * **`true`:** A new child scope that prototypically inherits from its parent will be created for
6620 * the directive's element. If multiple directives on the same element request a new scope,
6621 * only one new scope is created. The new scope rule does not apply for the root of the template
6622 * since the root of the template always gets a new scope.
6624 * * **`{...}` (an object hash):** A new "isolate" scope is created for the directive's element. The
6625 * 'isolate' scope differs from normal scope in that it does not prototypically inherit from its parent
6626 * scope. This is useful when creating reusable components, which should not accidentally read or modify
6627 * data in the parent scope.
6629 * The 'isolate' scope object hash defines a set of local scope properties derived from attributes on the
6630 * directive's element. These local properties are useful for aliasing values for templates. The keys in
6631 * the object hash map to the name of the property on the isolate scope; the values define how the property
6632 * is bound to the parent scope, via matching attributes on the directive's element:
6634 * * `@` or `@attr` - bind a local scope property to the value of DOM attribute. The result is
6635 * always a string since DOM attributes are strings. If no `attr` name is specified then the
6636 * attribute name is assumed to be the same as the local name. Given `<my-component
6637 * my-attr="hello {{name}}">` and the isolate scope definition `scope: { localName:'@myAttr' }`,
6638 * the directive's scope property `localName` will reflect the interpolated value of `hello
6639 * {{name}}`. As the `name` attribute changes so will the `localName` property on the directive's
6640 * scope. The `name` is read from the parent scope (not the directive's scope).
6642 * * `=` or `=attr` - set up a bidirectional binding between a local scope property and an expression
6643 * passed via the attribute `attr`. The expression is evaluated in the context of the parent scope.
6644 * If no `attr` name is specified then the attribute name is assumed to be the same as the local
6645 * name. Given `<my-component my-attr="parentModel">` and the isolate scope definition `scope: {
6646 * localModel: '=myAttr' }`, the property `localModel` on the directive's scope will reflect the
6647 * value of `parentModel` on the parent scope. Changes to `parentModel` will be reflected in
6648 * `localModel` and vice versa. Optional attributes should be marked as such with a question mark:
6649 * `=?` or `=?attr`. If the binding expression is non-assignable, or if the attribute isn't
6650 * optional and doesn't exist, an exception ({@link error/$compile/nonassign `$compile:nonassign`})
6651 * will be thrown upon discovering changes to the local value, since it will be impossible to sync
6652 * them back to the parent scope. By default, the {@link ng.$rootScope.Scope#$watch `$watch`}
6653 * method is used for tracking changes, and the equality check is based on object identity.
6654 * However, if an object literal or an array literal is passed as the binding expression, the
6655 * equality check is done by value (using the {@link angular.equals} function). It's also possible
6656 * to watch the evaluated value shallowly with {@link ng.$rootScope.Scope#$watchCollection
6657 * `$watchCollection`}: use `=*` or `=*attr` (`=*?` or `=*?attr` if the attribute is optional).
6659 * * `&` or `&attr` - provides a way to execute an expression in the context of the parent scope. If
6660 * no `attr` name is specified then the attribute name is assumed to be the same as the local name.
6661 * Given `<my-component my-attr="count = count + value">` and the isolate scope definition `scope: {
6662 * localFn:'&myAttr' }`, the isolate scope property `localFn` will point to a function wrapper for
6663 * the `count = count + value` expression. Often it's desirable to pass data from the isolated scope
6664 * via an expression to the parent scope. This can be done by passing a map of local variable names
6665 * and values into the expression wrapper fn. For example, if the expression is `increment(amount)`
6666 * then we can specify the amount value by calling the `localFn` as `localFn({amount: 22})`.
6668 * In general it's possible to apply more than one directive to one element, but there might be limitations
6669 * depending on the type of scope required by the directives. The following points will help explain these limitations.
6670 * For simplicity only two directives are taken into account, but it is also applicable for several directives:
6672 * * **no scope** + **no scope** => Two directives which don't require their own scope will use their parent's scope
6673 * * **child scope** + **no scope** => Both directives will share one single child scope
6674 * * **child scope** + **child scope** => Both directives will share one single child scope
6675 * * **isolated scope** + **no scope** => The isolated directive will use it's own created isolated scope. The other directive will use
6676 * its parent's scope
6677 * * **isolated scope** + **child scope** => **Won't work!** Only one scope can be related to one element. Therefore these directives cannot
6678 * be applied to the same element.
6679 * * **isolated scope** + **isolated scope** => **Won't work!** Only one scope can be related to one element. Therefore these directives
6680 * cannot be applied to the same element.
6683 * #### `bindToController`
6684 * This property is used to bind scope properties directly to the controller. It can be either
6685 * `true` or an object hash with the same format as the `scope` property. Additionally, a controller
6686 * alias must be set, either by using `controllerAs: 'myAlias'` or by specifying the alias in the controller
6687 * definition: `controller: 'myCtrl as myAlias'`.
6689 * When an isolate scope is used for a directive (see above), `bindToController: true` will
6690 * allow a component to have its properties bound to the controller, rather than to scope. When the controller
6691 * is instantiated, the initial values of the isolate scope bindings are already available.
6693 * It is also possible to set `bindToController` to an object hash with the same format as the `scope` property.
6694 * This will set up the scope bindings to the controller directly. Note that `scope` can still be used
6695 * to define which kind of scope is created. By default, no scope is created. Use `scope: {}` to create an isolate
6696 * scope (useful for component directives).
6698 * If both `bindToController` and `scope` are defined and have object hashes, `bindToController` overrides `scope`.
6702 * Controller constructor function. The controller is instantiated before the
6703 * pre-linking phase and can be accessed by other directives (see
6704 * `require` attribute). This allows the directives to communicate with each other and augment
6705 * each other's behavior. The controller is injectable (and supports bracket notation) with the following locals:
6707 * * `$scope` - Current scope associated with the element
6708 * * `$element` - Current element
6709 * * `$attrs` - Current attributes object for the element
6710 * * `$transclude` - A transclude linking function pre-bound to the correct transclusion scope:
6711 * `function([scope], cloneLinkingFn, futureParentElement)`.
6712 * * `scope`: optional argument to override the scope.
6713 * * `cloneLinkingFn`: optional argument to create clones of the original transcluded content.
6714 * * `futureParentElement`:
6715 * * defines the parent to which the `cloneLinkingFn` will add the cloned elements.
6716 * * default: `$element.parent()` resp. `$element` for `transclude:'element'` resp. `transclude:true`.
6717 * * only needed for transcludes that are allowed to contain non html elements (e.g. SVG elements)
6718 * and when the `cloneLinkinFn` is passed,
6719 * as those elements need to created and cloned in a special way when they are defined outside their
6720 * usual containers (e.g. like `<svg>`).
6721 * * See also the `directive.templateNamespace` property.
6725 * Require another directive and inject its controller as the fourth argument to the linking function. The
6726 * `require` takes a string name (or array of strings) of the directive(s) to pass in. If an array is used, the
6727 * injected argument will be an array in corresponding order. If no such directive can be
6728 * found, or if the directive does not have a controller, then an error is raised (unless no link function
6729 * is specified, in which case error checking is skipped). The name can be prefixed with:
6731 * * (no prefix) - Locate the required controller on the current element. Throw an error if not found.
6732 * * `?` - Attempt to locate the required controller or pass `null` to the `link` fn if not found.
6733 * * `^` - Locate the required controller by searching the element and its parents. Throw an error if not found.
6734 * * `^^` - Locate the required controller by searching the element's parents. Throw an error if not found.
6735 * * `?^` - Attempt to locate the required controller by searching the element and its parents or pass
6736 * `null` to the `link` fn if not found.
6737 * * `?^^` - Attempt to locate the required controller by searching the element's parents, or pass
6738 * `null` to the `link` fn if not found.
6741 * #### `controllerAs`
6742 * Identifier name for a reference to the controller in the directive's scope.
6743 * This allows the controller to be referenced from the directive template. This is especially
6744 * useful when a directive is used as component, i.e. with an `isolate` scope. It's also possible
6745 * to use it in a directive without an `isolate` / `new` scope, but you need to be aware that the
6746 * `controllerAs` reference might overwrite a property that already exists on the parent scope.
6750 * String of subset of `EACM` which restricts the directive to a specific directive
6751 * declaration style. If omitted, the defaults (elements and attributes) are used.
6753 * * `E` - Element name (default): `<my-directive></my-directive>`
6754 * * `A` - Attribute (default): `<div my-directive="exp"></div>`
6755 * * `C` - Class: `<div class="my-directive: exp;"></div>`
6756 * * `M` - Comment: `<!-- directive: my-directive exp -->`
6759 * #### `templateNamespace`
6760 * String representing the document type used by the markup in the template.
6761 * AngularJS needs this information as those elements need to be created and cloned
6762 * in a special way when they are defined outside their usual containers like `<svg>` and `<math>`.
6764 * * `html` - All root nodes in the template are HTML. Root nodes may also be
6765 * top-level elements such as `<svg>` or `<math>`.
6766 * * `svg` - The root nodes in the template are SVG elements (excluding `<math>`).
6767 * * `math` - The root nodes in the template are MathML elements (excluding `<svg>`).
6769 * If no `templateNamespace` is specified, then the namespace is considered to be `html`.
6772 * HTML markup that may:
6773 * * Replace the contents of the directive's element (default).
6774 * * Replace the directive's element itself (if `replace` is true - DEPRECATED).
6775 * * Wrap the contents of the directive's element (if `transclude` is true).
6779 * * A string. For example `<div red-on-hover>{{delete_str}}</div>`.
6780 * * A function which takes two arguments `tElement` and `tAttrs` (described in the `compile`
6781 * function api below) and returns a string value.
6784 * #### `templateUrl`
6785 * This is similar to `template` but the template is loaded from the specified URL, asynchronously.
6787 * Because template loading is asynchronous the compiler will suspend compilation of directives on that element
6788 * for later when the template has been resolved. In the meantime it will continue to compile and link
6789 * sibling and parent elements as though this element had not contained any directives.
6791 * The compiler does not suspend the entire compilation to wait for templates to be loaded because this
6792 * would result in the whole app "stalling" until all templates are loaded asynchronously - even in the
6793 * case when only one deeply nested directive has `templateUrl`.
6795 * Template loading is asynchronous even if the template has been preloaded into the {@link $templateCache}
6797 * You can specify `templateUrl` as a string representing the URL or as a function which takes two
6798 * arguments `tElement` and `tAttrs` (described in the `compile` function api below) and returns
6799 * a string value representing the url. In either case, the template URL is passed through {@link
6800 * $sce#getTrustedResourceUrl $sce.getTrustedResourceUrl}.
6803 * #### `replace` ([*DEPRECATED*!], will be removed in next major release - i.e. v2.0)
6804 * specify what the template should replace. Defaults to `false`.
6806 * * `true` - the template will replace the directive's element.
6807 * * `false` - the template will replace the contents of the directive's element.
6809 * The replacement process migrates all of the attributes / classes from the old element to the new
6810 * one. See the {@link guide/directive#template-expanding-directive
6811 * Directives Guide} for an example.
6813 * There are very few scenarios where element replacement is required for the application function,
6814 * the main one being reusable custom components that are used within SVG contexts
6815 * (because SVG doesn't work with custom elements in the DOM tree).
6818 * Extract the contents of the element where the directive appears and make it available to the directive.
6819 * The contents are compiled and provided to the directive as a **transclusion function**. See the
6820 * {@link $compile#transclusion Transclusion} section below.
6822 * There are two kinds of transclusion depending upon whether you want to transclude just the contents of the
6823 * directive's element or the entire element:
6825 * * `true` - transclude the content (i.e. the child nodes) of the directive's element.
6826 * * `'element'` - transclude the whole of the directive's element including any directives on this
6827 * element that defined at a lower priority than this directive. When used, the `template`
6828 * property is ignored.
6834 * function compile(tElement, tAttrs, transclude) { ... }
6837 * The compile function deals with transforming the template DOM. Since most directives do not do
6838 * template transformation, it is not used often. The compile function takes the following arguments:
6840 * * `tElement` - template element - The element where the directive has been declared. It is
6841 * safe to do template transformation on the element and child elements only.
6843 * * `tAttrs` - template attributes - Normalized list of attributes declared on this element shared
6844 * between all directive compile functions.
6846 * * `transclude` - [*DEPRECATED*!] A transclude linking function: `function(scope, cloneLinkingFn)`
6848 * <div class="alert alert-warning">
6849 * **Note:** The template instance and the link instance may be different objects if the template has
6850 * been cloned. For this reason it is **not** safe to do anything other than DOM transformations that
6851 * apply to all cloned DOM nodes within the compile function. Specifically, DOM listener registration
6852 * should be done in a linking function rather than in a compile function.
6855 * <div class="alert alert-warning">
6856 * **Note:** The compile function cannot handle directives that recursively use themselves in their
6857 * own templates or compile functions. Compiling these directives results in an infinite loop and
6858 * stack overflow errors.
6860 * This can be avoided by manually using $compile in the postLink function to imperatively compile
6861 * a directive's template instead of relying on automatic template compilation via `template` or
6862 * `templateUrl` declaration or manual compilation inside the compile function.
6865 * <div class="alert alert-danger">
6866 * **Note:** The `transclude` function that is passed to the compile function is deprecated, as it
6867 * e.g. does not know about the right outer scope. Please use the transclude function that is passed
6868 * to the link function instead.
6871 * A compile function can have a return value which can be either a function or an object.
6873 * * returning a (post-link) function - is equivalent to registering the linking function via the
6874 * `link` property of the config object when the compile function is empty.
6876 * * returning an object with function(s) registered via `pre` and `post` properties - allows you to
6877 * control when a linking function should be called during the linking phase. See info about
6878 * pre-linking and post-linking functions below.
6882 * This property is used only if the `compile` property is not defined.
6885 * function link(scope, iElement, iAttrs, controller, transcludeFn) { ... }
6888 * The link function is responsible for registering DOM listeners as well as updating the DOM. It is
6889 * executed after the template has been cloned. This is where most of the directive logic will be
6892 * * `scope` - {@link ng.$rootScope.Scope Scope} - The scope to be used by the
6893 * directive for registering {@link ng.$rootScope.Scope#$watch watches}.
6895 * * `iElement` - instance element - The element where the directive is to be used. It is safe to
6896 * manipulate the children of the element only in `postLink` function since the children have
6897 * already been linked.
6899 * * `iAttrs` - instance attributes - Normalized list of attributes declared on this element shared
6900 * between all directive linking functions.
6902 * * `controller` - the directive's required controller instance(s) - Instances are shared
6903 * among all directives, which allows the directives to use the controllers as a communication
6904 * channel. The exact value depends on the directive's `require` property:
6905 * * no controller(s) required: the directive's own controller, or `undefined` if it doesn't have one
6906 * * `string`: the controller instance
6907 * * `array`: array of controller instances
6909 * If a required controller cannot be found, and it is optional, the instance is `null`,
6910 * otherwise the {@link error:$compile:ctreq Missing Required Controller} error is thrown.
6912 * Note that you can also require the directive's own controller - it will be made available like
6913 * any other controller.
6915 * * `transcludeFn` - A transclude linking function pre-bound to the correct transclusion scope.
6916 * This is the same as the `$transclude`
6917 * parameter of directive controllers, see there for details.
6918 * `function([scope], cloneLinkingFn, futureParentElement)`.
6920 * #### Pre-linking function
6922 * Executed before the child elements are linked. Not safe to do DOM transformation since the
6923 * compiler linking function will fail to locate the correct elements for linking.
6925 * #### Post-linking function
6927 * Executed after the child elements are linked.
6929 * Note that child elements that contain `templateUrl` directives will not have been compiled
6930 * and linked since they are waiting for their template to load asynchronously and their own
6931 * compilation and linking has been suspended until that occurs.
6933 * It is safe to do DOM transformation in the post-linking function on elements that are not waiting
6934 * for their async templates to be resolved.
6939 * Transclusion is the process of extracting a collection of DOM elements from one part of the DOM and
6940 * copying them to another part of the DOM, while maintaining their connection to the original AngularJS
6941 * scope from where they were taken.
6943 * Transclusion is used (often with {@link ngTransclude}) to insert the
6944 * original contents of a directive's element into a specified place in the template of the directive.
6945 * The benefit of transclusion, over simply moving the DOM elements manually, is that the transcluded
6946 * content has access to the properties on the scope from which it was taken, even if the directive
6947 * has isolated scope.
6948 * See the {@link guide/directive#creating-a-directive-that-wraps-other-elements Directives Guide}.
6950 * This makes it possible for the widget to have private state for its template, while the transcluded
6951 * content has access to its originating scope.
6953 * <div class="alert alert-warning">
6954 * **Note:** When testing an element transclude directive you must not place the directive at the root of the
6955 * DOM fragment that is being compiled. See {@link guide/unit-testing#testing-transclusion-directives
6956 * Testing Transclusion Directives}.
6959 * #### Transclusion Functions
6961 * When a directive requests transclusion, the compiler extracts its contents and provides a **transclusion
6962 * function** to the directive's `link` function and `controller`. This transclusion function is a special
6963 * **linking function** that will return the compiled contents linked to a new transclusion scope.
6965 * <div class="alert alert-info">
6966 * If you are just using {@link ngTransclude} then you don't need to worry about this function, since
6967 * ngTransclude will deal with it for us.
6970 * If you want to manually control the insertion and removal of the transcluded content in your directive
6971 * then you must use this transclude function. When you call a transclude function it returns a a jqLite/JQuery
6972 * object that contains the compiled DOM, which is linked to the correct transclusion scope.
6974 * When you call a transclusion function you can pass in a **clone attach function**. This function accepts
6975 * two parameters, `function(clone, scope) { ... }`, where the `clone` is a fresh compiled copy of your transcluded
6976 * content and the `scope` is the newly created transclusion scope, to which the clone is bound.
6978 * <div class="alert alert-info">
6979 * **Best Practice**: Always provide a `cloneFn` (clone attach function) when you call a translude function
6980 * since you then get a fresh clone of the original DOM and also have access to the new transclusion scope.
6983 * It is normal practice to attach your transcluded content (`clone`) to the DOM inside your **clone
6984 * attach function**:
6987 * var transcludedContent, transclusionScope;
6989 * $transclude(function(clone, scope) {
6990 * element.append(clone);
6991 * transcludedContent = clone;
6992 * transclusionScope = scope;
6996 * Later, if you want to remove the transcluded content from your DOM then you should also destroy the
6997 * associated transclusion scope:
7000 * transcludedContent.remove();
7001 * transclusionScope.$destroy();
7004 * <div class="alert alert-info">
7005 * **Best Practice**: if you intend to add and remove transcluded content manually in your directive
7006 * (by calling the transclude function to get the DOM and calling `element.remove()` to remove it),
7007 * then you are also responsible for calling `$destroy` on the transclusion scope.
7010 * The built-in DOM manipulation directives, such as {@link ngIf}, {@link ngSwitch} and {@link ngRepeat}
7011 * automatically destroy their transluded clones as necessary so you do not need to worry about this if
7012 * you are simply using {@link ngTransclude} to inject the transclusion into your directive.
7015 * #### Transclusion Scopes
7017 * When you call a transclude function it returns a DOM fragment that is pre-bound to a **transclusion
7018 * scope**. This scope is special, in that it is a child of the directive's scope (and so gets destroyed
7019 * when the directive's scope gets destroyed) but it inherits the properties of the scope from which it
7022 * For example consider a directive that uses transclusion and isolated scope. The DOM hierarchy might look
7028 * <div transclusion>
7034 * The `$parent` scope hierarchy will look like this:
7042 * but the scopes will inherit prototypically from different scopes to their `$parent`.
7053 * The {@link ng.$compile.directive.Attributes Attributes} object - passed as a parameter in the
7054 * `link()` or `compile()` functions. It has a variety of uses.
7056 * * *Accessing normalized attribute names:* Directives like 'ngBind' can be expressed in many ways:
7057 * 'ng:bind', `data-ng-bind`, or 'x-ng-bind'. The attributes object allows for normalized access
7058 * to the attributes.
7060 * * *Directive inter-communication:* All directives share the same instance of the attributes
7061 * object which allows the directives to use the attributes object as inter directive
7064 * * *Supports interpolation:* Interpolation attributes are assigned to the attribute object
7065 * allowing other directives to read the interpolated value.
7067 * * *Observing interpolated attributes:* Use `$observe` to observe the value changes of attributes
7068 * that contain interpolation (e.g. `src="{{bar}}"`). Not only is this very efficient but it's also
7069 * the only way to easily get the actual value because during the linking phase the interpolation
7070 * hasn't been evaluated yet and so the value is at this time set to `undefined`.
7073 * function linkingFn(scope, elm, attrs, ctrl) {
7074 * // get the attribute value
7075 * console.log(attrs.ngModel);
7077 * // change the attribute
7078 * attrs.$set('ngModel', 'new value');
7080 * // observe changes to interpolated attribute
7081 * attrs.$observe('ngModel', function(value) {
7082 * console.log('ngModel has changed value to ' + value);
7089 * <div class="alert alert-warning">
7090 * **Note**: Typically directives are registered with `module.directive`. The example below is
7091 * to illustrate how `$compile` works.
7094 <example module="compileExample">
7095 <file name="index.html">
7097 angular.module('compileExample', [], function($compileProvider) {
7098 // configure new 'compile' directive by passing a directive
7099 // factory function. The factory function injects the '$compile'
7100 $compileProvider.directive('compile', function($compile) {
7101 // directive factory creates a link function
7102 return function(scope, element, attrs) {
7105 // watch the 'compile' expression for changes
7106 return scope.$eval(attrs.compile);
7109 // when the 'compile' expression changes
7110 // assign it into the current DOM
7111 element.html(value);
7113 // compile the new DOM and link it to the current
7115 // NOTE: we only compile .childNodes so that
7116 // we don't get into infinite loop compiling ourselves
7117 $compile(element.contents())(scope);
7123 .controller('GreeterController', ['$scope', function($scope) {
7124 $scope.name = 'Angular';
7125 $scope.html = 'Hello {{name}}';
7128 <div ng-controller="GreeterController">
7129 <input ng-model="name"> <br/>
7130 <textarea ng-model="html"></textarea> <br/>
7131 <div compile="html"></div>
7134 <file name="protractor.js" type="protractor">
7135 it('should auto compile', function() {
7136 var textarea = $('textarea');
7137 var output = $('div[compile]');
7138 // The initial state reads 'Hello Angular'.
7139 expect(output.getText()).toBe('Hello Angular');
7141 textarea.sendKeys('{{name}}!');
7142 expect(output.getText()).toBe('Angular!');
7149 * @param {string|DOMElement} element Element or HTML string to compile into a template function.
7150 * @param {function(angular.Scope, cloneAttachFn=)} transclude function available to directives - DEPRECATED.
7152 * <div class="alert alert-danger">
7153 * **Note:** Passing a `transclude` function to the $compile function is deprecated, as it
7154 * e.g. will not use the right outer scope. Please pass the transclude function as a
7155 * `parentBoundTranscludeFn` to the link function instead.
7158 * @param {number} maxPriority only apply directives lower than given priority (Only effects the
7159 * root element(s), not their children)
7160 * @returns {function(scope, cloneAttachFn=, options=)} a link function which is used to bind template
7161 * (a DOM element/tree) to a scope. Where:
7163 * * `scope` - A {@link ng.$rootScope.Scope Scope} to bind to.
7164 * * `cloneAttachFn` - If `cloneAttachFn` is provided, then the link function will clone the
7165 * `template` and call the `cloneAttachFn` function allowing the caller to attach the
7166 * cloned elements to the DOM document at the appropriate place. The `cloneAttachFn` is
7167 * called as: <br/> `cloneAttachFn(clonedElement, scope)` where:
7169 * * `clonedElement` - is a clone of the original `element` passed into the compiler.
7170 * * `scope` - is the current scope with which the linking function is working with.
7172 * * `options` - An optional object hash with linking options. If `options` is provided, then the following
7173 * keys may be used to control linking behavior:
7175 * * `parentBoundTranscludeFn` - the transclude function made available to
7176 * directives; if given, it will be passed through to the link functions of
7177 * directives found in `element` during compilation.
7178 * * `transcludeControllers` - an object hash with keys that map controller names
7179 * to a hash with the key `instance`, which maps to the controller instance;
7180 * if given, it will make the controllers available to directives on the compileNode:
7184 * instance: parentControllerInstance
7188 * * `futureParentElement` - defines the parent to which the `cloneAttachFn` will add
7189 * the cloned elements; only needed for transcludes that are allowed to contain non html
7190 * elements (e.g. SVG elements). See also the directive.controller property.
7192 * Calling the linking function returns the element of the template. It is either the original
7193 * element passed in, or the clone of the element if the `cloneAttachFn` is provided.
7195 * After linking the view is not updated until after a call to $digest which typically is done by
7196 * Angular automatically.
7198 * If you need access to the bound view, there are two ways to do it:
7200 * - If you are not asking the linking function to clone the template, create the DOM element(s)
7201 * before you send them to the compiler and keep this reference around.
7203 * var element = $compile('<p>{{total}}</p>')(scope);
7206 * - if on the other hand, you need the element to be cloned, the view reference from the original
7207 * example would not point to the clone, but rather to the original template that was cloned. In
7208 * this case, you can access the clone via the cloneAttachFn:
7210 * var templateElement = angular.element('<p>{{total}}</p>'),
7213 * var clonedElement = $compile(templateElement)(scope, function(clonedElement, scope) {
7214 * //attach the clone to DOM document at the right place
7217 * //now we have reference to the cloned DOM via `clonedElement`
7221 * For information on how the compiler works, see the
7222 * {@link guide/compiler Angular HTML Compiler} section of the Developer Guide.
7225 var $compileMinErr = minErr('$compile');
7229 * @name $compileProvider
7233 $CompileProvider.$inject = ['$provide', '$$sanitizeUriProvider'];
7234 function $CompileProvider($provide, $$sanitizeUriProvider) {
7235 var hasDirectives = {},
7236 Suffix = 'Directive',
7237 COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\w\-]+)\s+(.*)$/,
7238 CLASS_DIRECTIVE_REGEXP = /(([\w\-]+)(?:\:([^;]+))?;?)/,
7239 ALL_OR_NOTHING_ATTRS = makeMap('ngSrc,ngSrcset,src,srcset'),
7240 REQUIRE_PREFIX_REGEXP = /^(?:(\^\^?)?(\?)?(\^\^?)?)?/;
7242 // Ref: http://developers.whatwg.org/webappapis.html#event-handler-idl-attributes
7243 // The assumption is that future DOM event attribute names will begin with
7244 // 'on' and be composed of only English letters.
7245 var EVENT_HANDLER_ATTR_REGEXP = /^(on[a-z]+|formaction)$/;
7246 var bindingCache = createMap();
7248 function parseIsolateBindings(scope, directiveName, isController) {
7249 var LOCAL_REGEXP = /^\s*([@&]|=(\*?))(\??)\s*(\w*)\s*$/;
7253 forEach(scope, function(definition, scopeName) {
7254 if (definition in bindingCache) {
7255 bindings[scopeName] = bindingCache[definition];
7258 var match = definition.match(LOCAL_REGEXP);
7261 throw $compileMinErr('iscp',
7262 "Invalid {3} for directive '{0}'." +
7263 " Definition: {... {1}: '{2}' ...}",
7264 directiveName, scopeName, definition,
7265 (isController ? "controller bindings definition" :
7266 "isolate scope definition"));
7269 bindings[scopeName] = {
7271 collection: match[2] === '*',
7272 optional: match[3] === '?',
7273 attrName: match[4] || scopeName
7276 bindingCache[definition] = bindings[scopeName];
7283 function parseDirectiveBindings(directive, directiveName) {
7286 bindToController: null
7288 if (isObject(directive.scope)) {
7289 if (directive.bindToController === true) {
7290 bindings.bindToController = parseIsolateBindings(directive.scope,
7291 directiveName, true);
7292 bindings.isolateScope = {};
7294 bindings.isolateScope = parseIsolateBindings(directive.scope,
7295 directiveName, false);
7298 if (isObject(directive.bindToController)) {
7299 bindings.bindToController =
7300 parseIsolateBindings(directive.bindToController, directiveName, true);
7302 if (isObject(bindings.bindToController)) {
7303 var controller = directive.controller;
7304 var controllerAs = directive.controllerAs;
7306 // There is no controller, there may or may not be a controllerAs property
7307 throw $compileMinErr('noctrl',
7308 "Cannot bind to controller without directive '{0}'s controller.",
7310 } else if (!identifierForController(controller, controllerAs)) {
7311 // There is a controller, but no identifier or controllerAs property
7312 throw $compileMinErr('noident',
7313 "Cannot bind to controller without identifier for directive '{0}'.",
7320 function assertValidDirectiveName(name) {
7321 var letter = name.charAt(0);
7322 if (!letter || letter !== lowercase(letter)) {
7323 throw $compileMinErr('baddir', "Directive name '{0}' is invalid. The first character must be a lowercase letter", name);
7325 if (name !== name.trim()) {
7326 throw $compileMinErr('baddir',
7327 "Directive name '{0}' is invalid. The name should not contain leading or trailing whitespaces",
7334 * @name $compileProvider#directive
7338 * Register a new directive with the compiler.
7340 * @param {string|Object} name Name of the directive in camel-case (i.e. <code>ngBind</code> which
7341 * will match as <code>ng-bind</code>), or an object map of directives where the keys are the
7342 * names and the values are the factories.
7343 * @param {Function|Array} directiveFactory An injectable directive factory function. See
7344 * {@link guide/directive} for more info.
7345 * @returns {ng.$compileProvider} Self for chaining.
7347 this.directive = function registerDirective(name, directiveFactory) {
7348 assertNotHasOwnProperty(name, 'directive');
7349 if (isString(name)) {
7350 assertValidDirectiveName(name);
7351 assertArg(directiveFactory, 'directiveFactory');
7352 if (!hasDirectives.hasOwnProperty(name)) {
7353 hasDirectives[name] = [];
7354 $provide.factory(name + Suffix, ['$injector', '$exceptionHandler',
7355 function($injector, $exceptionHandler) {
7356 var directives = [];
7357 forEach(hasDirectives[name], function(directiveFactory, index) {
7359 var directive = $injector.invoke(directiveFactory);
7360 if (isFunction(directive)) {
7361 directive = { compile: valueFn(directive) };
7362 } else if (!directive.compile && directive.link) {
7363 directive.compile = valueFn(directive.link);
7365 directive.priority = directive.priority || 0;
7366 directive.index = index;
7367 directive.name = directive.name || name;
7368 directive.require = directive.require || (directive.controller && directive.name);
7369 directive.restrict = directive.restrict || 'EA';
7370 directive.$$moduleName = directiveFactory.$$moduleName;
7371 directives.push(directive);
7373 $exceptionHandler(e);
7379 hasDirectives[name].push(directiveFactory);
7381 forEach(name, reverseParams(registerDirective));
7389 * @name $compileProvider#aHrefSanitizationWhitelist
7393 * Retrieves or overrides the default regular expression that is used for whitelisting of safe
7394 * urls during a[href] sanitization.
7396 * The sanitization is a security measure aimed at preventing XSS attacks via html links.
7398 * Any url about to be assigned to a[href] via data-binding is first normalized and turned into
7399 * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist`
7400 * regular expression. If a match is found, the original url is written into the dom. Otherwise,
7401 * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
7403 * @param {RegExp=} regexp New regexp to whitelist urls with.
7404 * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
7405 * chaining otherwise.
7407 this.aHrefSanitizationWhitelist = function(regexp) {
7408 if (isDefined(regexp)) {
7409 $$sanitizeUriProvider.aHrefSanitizationWhitelist(regexp);
7412 return $$sanitizeUriProvider.aHrefSanitizationWhitelist();
7419 * @name $compileProvider#imgSrcSanitizationWhitelist
7423 * Retrieves or overrides the default regular expression that is used for whitelisting of safe
7424 * urls during img[src] sanitization.
7426 * The sanitization is a security measure aimed at prevent XSS attacks via html links.
7428 * Any url about to be assigned to img[src] via data-binding is first normalized and turned into
7429 * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist`
7430 * regular expression. If a match is found, the original url is written into the dom. Otherwise,
7431 * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
7433 * @param {RegExp=} regexp New regexp to whitelist urls with.
7434 * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
7435 * chaining otherwise.
7437 this.imgSrcSanitizationWhitelist = function(regexp) {
7438 if (isDefined(regexp)) {
7439 $$sanitizeUriProvider.imgSrcSanitizationWhitelist(regexp);
7442 return $$sanitizeUriProvider.imgSrcSanitizationWhitelist();
7448 * @name $compileProvider#debugInfoEnabled
7450 * @param {boolean=} enabled update the debugInfoEnabled state if provided, otherwise just return the
7451 * current debugInfoEnabled state
7452 * @returns {*} current value if used as getter or itself (chaining) if used as setter
7457 * Call this method to enable/disable various debug runtime information in the compiler such as adding
7458 * binding information and a reference to the current scope on to DOM elements.
7459 * If enabled, the compiler will add the following to DOM elements that have been bound to the scope
7460 * * `ng-binding` CSS class
7461 * * `$binding` data property containing an array of the binding expressions
7463 * You may want to disable this in production for a significant performance boost. See
7464 * {@link guide/production#disabling-debug-data Disabling Debug Data} for more.
7466 * The default value is true.
7468 var debugInfoEnabled = true;
7469 this.debugInfoEnabled = function(enabled) {
7470 if (isDefined(enabled)) {
7471 debugInfoEnabled = enabled;
7474 return debugInfoEnabled;
7478 '$injector', '$interpolate', '$exceptionHandler', '$templateRequest', '$parse',
7479 '$controller', '$rootScope', '$sce', '$animate', '$$sanitizeUri',
7480 function($injector, $interpolate, $exceptionHandler, $templateRequest, $parse,
7481 $controller, $rootScope, $sce, $animate, $$sanitizeUri) {
7483 var Attributes = function(element, attributesToCopy) {
7484 if (attributesToCopy) {
7485 var keys = Object.keys(attributesToCopy);
7488 for (i = 0, l = keys.length; i < l; i++) {
7490 this[key] = attributesToCopy[key];
7496 this.$$element = element;
7499 Attributes.prototype = {
7502 * @name $compile.directive.Attributes#$normalize
7506 * Converts an attribute name (e.g. dash/colon/underscore-delimited string, optionally prefixed with `x-` or
7507 * `data-`) to its normalized, camelCase form.
7509 * Also there is special case for Moz prefix starting with upper case letter.
7511 * For further information check out the guide on {@link guide/directive#matching-directives Matching Directives}
7513 * @param {string} name Name to normalize
7515 $normalize: directiveNormalize,
7520 * @name $compile.directive.Attributes#$addClass
7524 * Adds the CSS class value specified by the classVal parameter to the element. If animations
7525 * are enabled then an animation will be triggered for the class addition.
7527 * @param {string} classVal The className value that will be added to the element
7529 $addClass: function(classVal) {
7530 if (classVal && classVal.length > 0) {
7531 $animate.addClass(this.$$element, classVal);
7537 * @name $compile.directive.Attributes#$removeClass
7541 * Removes the CSS class value specified by the classVal parameter from the element. If
7542 * animations are enabled then an animation will be triggered for the class removal.
7544 * @param {string} classVal The className value that will be removed from the element
7546 $removeClass: function(classVal) {
7547 if (classVal && classVal.length > 0) {
7548 $animate.removeClass(this.$$element, classVal);
7554 * @name $compile.directive.Attributes#$updateClass
7558 * Adds and removes the appropriate CSS class values to the element based on the difference
7559 * between the new and old CSS class values (specified as newClasses and oldClasses).
7561 * @param {string} newClasses The current CSS className value
7562 * @param {string} oldClasses The former CSS className value
7564 $updateClass: function(newClasses, oldClasses) {
7565 var toAdd = tokenDifference(newClasses, oldClasses);
7566 if (toAdd && toAdd.length) {
7567 $animate.addClass(this.$$element, toAdd);
7570 var toRemove = tokenDifference(oldClasses, newClasses);
7571 if (toRemove && toRemove.length) {
7572 $animate.removeClass(this.$$element, toRemove);
7577 * Set a normalized attribute on the element in a way such that all directives
7578 * can share the attribute. This function properly handles boolean attributes.
7579 * @param {string} key Normalized key. (ie ngAttribute)
7580 * @param {string|boolean} value The value to set. If `null` attribute will be deleted.
7581 * @param {boolean=} writeAttr If false, does not write the value to DOM element attribute.
7583 * @param {string=} attrName Optional none normalized name. Defaults to key.
7585 $set: function(key, value, writeAttr, attrName) {
7586 // TODO: decide whether or not to throw an error if "class"
7587 //is set through this function since it may cause $updateClass to
7590 var node = this.$$element[0],
7591 booleanKey = getBooleanAttrName(node, key),
7592 aliasedKey = getAliasedAttrName(key),
7597 this.$$element.prop(key, value);
7598 attrName = booleanKey;
7599 } else if (aliasedKey) {
7600 this[aliasedKey] = value;
7601 observer = aliasedKey;
7606 // translate normalized key to actual key
7608 this.$attr[key] = attrName;
7610 attrName = this.$attr[key];
7612 this.$attr[key] = attrName = snake_case(key, '-');
7616 nodeName = nodeName_(this.$$element);
7618 if ((nodeName === 'a' && key === 'href') ||
7619 (nodeName === 'img' && key === 'src')) {
7620 // sanitize a[href] and img[src] values
7621 this[key] = value = $$sanitizeUri(value, key === 'src');
7622 } else if (nodeName === 'img' && key === 'srcset') {
7623 // sanitize img[srcset] values
7626 // first check if there are spaces because it's not the same pattern
7627 var trimmedSrcset = trim(value);
7628 // ( 999x ,| 999w ,| ,|, )
7629 var srcPattern = /(\s+\d+x\s*,|\s+\d+w\s*,|\s+,|,\s+)/;
7630 var pattern = /\s/.test(trimmedSrcset) ? srcPattern : /(,)/;
7632 // split srcset into tuple of uri and descriptor except for the last item
7633 var rawUris = trimmedSrcset.split(pattern);
7636 var nbrUrisWith2parts = Math.floor(rawUris.length / 2);
7637 for (var i = 0; i < nbrUrisWith2parts; i++) {
7638 var innerIdx = i * 2;
7640 result += $$sanitizeUri(trim(rawUris[innerIdx]), true);
7641 // add the descriptor
7642 result += (" " + trim(rawUris[innerIdx + 1]));
7645 // split the last item into uri and descriptor
7646 var lastTuple = trim(rawUris[i * 2]).split(/\s/);
7648 // sanitize the last uri
7649 result += $$sanitizeUri(trim(lastTuple[0]), true);
7651 // and add the last descriptor if any
7652 if (lastTuple.length === 2) {
7653 result += (" " + trim(lastTuple[1]));
7655 this[key] = value = result;
7658 if (writeAttr !== false) {
7659 if (value === null || isUndefined(value)) {
7660 this.$$element.removeAttr(attrName);
7662 this.$$element.attr(attrName, value);
7667 var $$observers = this.$$observers;
7668 $$observers && forEach($$observers[observer], function(fn) {
7672 $exceptionHandler(e);
7680 * @name $compile.directive.Attributes#$observe
7684 * Observes an interpolated attribute.
7686 * The observer function will be invoked once during the next `$digest` following
7687 * compilation. The observer is then invoked whenever the interpolated value
7690 * @param {string} key Normalized key. (ie ngAttribute) .
7691 * @param {function(interpolatedValue)} fn Function that will be called whenever
7692 the interpolated value of the attribute changes.
7693 * See the {@link guide/interpolation#how-text-and-attribute-bindings-work Interpolation
7694 * guide} for more info.
7695 * @returns {function()} Returns a deregistration function for this observer.
7697 $observe: function(key, fn) {
7699 $$observers = (attrs.$$observers || (attrs.$$observers = createMap())),
7700 listeners = ($$observers[key] || ($$observers[key] = []));
7703 $rootScope.$evalAsync(function() {
7704 if (!listeners.$$inter && attrs.hasOwnProperty(key) && !isUndefined(attrs[key])) {
7705 // no one registered attribute interpolation function, so lets call it manually
7711 arrayRemove(listeners, fn);
7717 function safeAddClass($element, className) {
7719 $element.addClass(className);
7721 // ignore, since it means that we are trying to set class on
7722 // SVG element, where class name is read-only.
7727 var startSymbol = $interpolate.startSymbol(),
7728 endSymbol = $interpolate.endSymbol(),
7729 denormalizeTemplate = (startSymbol == '{{' && endSymbol == '}}')
7731 : function denormalizeTemplate(template) {
7732 return template.replace(/\{\{/g, startSymbol).replace(/}}/g, endSymbol);
7734 NG_ATTR_BINDING = /^ngAttr[A-Z]/;
7735 var MULTI_ELEMENT_DIR_RE = /^(.+)Start$/;
7737 compile.$$addBindingInfo = debugInfoEnabled ? function $$addBindingInfo($element, binding) {
7738 var bindings = $element.data('$binding') || [];
7740 if (isArray(binding)) {
7741 bindings = bindings.concat(binding);
7743 bindings.push(binding);
7746 $element.data('$binding', bindings);
7749 compile.$$addBindingClass = debugInfoEnabled ? function $$addBindingClass($element) {
7750 safeAddClass($element, 'ng-binding');
7753 compile.$$addScopeInfo = debugInfoEnabled ? function $$addScopeInfo($element, scope, isolated, noTemplate) {
7754 var dataName = isolated ? (noTemplate ? '$isolateScopeNoTemplate' : '$isolateScope') : '$scope';
7755 $element.data(dataName, scope);
7758 compile.$$addScopeClass = debugInfoEnabled ? function $$addScopeClass($element, isolated) {
7759 safeAddClass($element, isolated ? 'ng-isolate-scope' : 'ng-scope');
7764 //================================
7766 function compile($compileNodes, transcludeFn, maxPriority, ignoreDirective,
7767 previousCompileContext) {
7768 if (!($compileNodes instanceof jqLite)) {
7769 // jquery always rewraps, whereas we need to preserve the original selector so that we can
7771 $compileNodes = jqLite($compileNodes);
7774 var NOT_EMPTY = /\S+/;
7776 // We can not compile top level text elements since text nodes can be merged and we will
7777 // not be able to attach scope data to them, so we will wrap them in <span>
7778 for (var i = 0, len = $compileNodes.length; i < len; i++) {
7779 var domNode = $compileNodes[i];
7781 if (domNode.nodeType === NODE_TYPE_TEXT && domNode.nodeValue.match(NOT_EMPTY) /* non-empty */) {
7782 jqLiteWrapNode(domNode, $compileNodes[i] = document.createElement('span'));
7786 var compositeLinkFn =
7787 compileNodes($compileNodes, transcludeFn, $compileNodes,
7788 maxPriority, ignoreDirective, previousCompileContext);
7789 compile.$$addScopeClass($compileNodes);
7790 var namespace = null;
7791 return function publicLinkFn(scope, cloneConnectFn, options) {
7792 assertArg(scope, 'scope');
7794 if (previousCompileContext && previousCompileContext.needsNewScope) {
7795 // A parent directive did a replace and a directive on this element asked
7796 // for transclusion, which caused us to lose a layer of element on which
7797 // we could hold the new transclusion scope, so we will create it manually
7799 scope = scope.$parent.$new();
7802 options = options || {};
7803 var parentBoundTranscludeFn = options.parentBoundTranscludeFn,
7804 transcludeControllers = options.transcludeControllers,
7805 futureParentElement = options.futureParentElement;
7807 // When `parentBoundTranscludeFn` is passed, it is a
7808 // `controllersBoundTransclude` function (it was previously passed
7809 // as `transclude` to directive.link) so we must unwrap it to get
7810 // its `boundTranscludeFn`
7811 if (parentBoundTranscludeFn && parentBoundTranscludeFn.$$boundTransclude) {
7812 parentBoundTranscludeFn = parentBoundTranscludeFn.$$boundTransclude;
7816 namespace = detectNamespaceForChildElements(futureParentElement);
7819 if (namespace !== 'html') {
7820 // When using a directive with replace:true and templateUrl the $compileNodes
7821 // (or a child element inside of them)
7822 // might change, so we need to recreate the namespace adapted compileNodes
7823 // for call to the link function.
7824 // Note: This will already clone the nodes...
7826 wrapTemplate(namespace, jqLite('<div>').append($compileNodes).html())
7828 } else if (cloneConnectFn) {
7829 // important!!: we must call our jqLite.clone() since the jQuery one is trying to be smart
7830 // and sometimes changes the structure of the DOM.
7831 $linkNode = JQLitePrototype.clone.call($compileNodes);
7833 $linkNode = $compileNodes;
7836 if (transcludeControllers) {
7837 for (var controllerName in transcludeControllers) {
7838 $linkNode.data('$' + controllerName + 'Controller', transcludeControllers[controllerName].instance);
7842 compile.$$addScopeInfo($linkNode, scope);
7844 if (cloneConnectFn) cloneConnectFn($linkNode, scope);
7845 if (compositeLinkFn) compositeLinkFn(scope, $linkNode, $linkNode, parentBoundTranscludeFn);
7850 function detectNamespaceForChildElements(parentElement) {
7851 // TODO: Make this detect MathML as well...
7852 var node = parentElement && parentElement[0];
7856 return nodeName_(node) !== 'foreignobject' && node.toString().match(/SVG/) ? 'svg' : 'html';
7861 * Compile function matches each node in nodeList against the directives. Once all directives
7862 * for a particular node are collected their compile functions are executed. The compile
7863 * functions return values - the linking functions - are combined into a composite linking
7864 * function, which is the a linking function for the node.
7866 * @param {NodeList} nodeList an array of nodes or NodeList to compile
7867 * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the
7868 * scope argument is auto-generated to the new child of the transcluded parent scope.
7869 * @param {DOMElement=} $rootElement If the nodeList is the root of the compilation tree then
7870 * the rootElement must be set the jqLite collection of the compile root. This is
7871 * needed so that the jqLite collection items can be replaced with widgets.
7872 * @param {number=} maxPriority Max directive priority.
7873 * @returns {Function} A composite linking function of all of the matched directives or null.
7875 function compileNodes(nodeList, transcludeFn, $rootElement, maxPriority, ignoreDirective,
7876 previousCompileContext) {
7878 attrs, directives, nodeLinkFn, childNodes, childLinkFn, linkFnFound, nodeLinkFnFound;
7880 for (var i = 0; i < nodeList.length; i++) {
7881 attrs = new Attributes();
7883 // we must always refer to nodeList[i] since the nodes can be replaced underneath us.
7884 directives = collectDirectives(nodeList[i], [], attrs, i === 0 ? maxPriority : undefined,
7887 nodeLinkFn = (directives.length)
7888 ? applyDirectivesToNode(directives, nodeList[i], attrs, transcludeFn, $rootElement,
7889 null, [], [], previousCompileContext)
7892 if (nodeLinkFn && nodeLinkFn.scope) {
7893 compile.$$addScopeClass(attrs.$$element);
7896 childLinkFn = (nodeLinkFn && nodeLinkFn.terminal ||
7897 !(childNodes = nodeList[i].childNodes) ||
7900 : compileNodes(childNodes,
7902 (nodeLinkFn.transcludeOnThisElement || !nodeLinkFn.templateOnThisElement)
7903 && nodeLinkFn.transclude) : transcludeFn);
7905 if (nodeLinkFn || childLinkFn) {
7906 linkFns.push(i, nodeLinkFn, childLinkFn);
7908 nodeLinkFnFound = nodeLinkFnFound || nodeLinkFn;
7911 //use the previous context only for the first element in the virtual group
7912 previousCompileContext = null;
7915 // return a linking function if we have found anything, null otherwise
7916 return linkFnFound ? compositeLinkFn : null;
7918 function compositeLinkFn(scope, nodeList, $rootElement, parentBoundTranscludeFn) {
7919 var nodeLinkFn, childLinkFn, node, childScope, i, ii, idx, childBoundTranscludeFn;
7923 if (nodeLinkFnFound) {
7924 // copy nodeList so that if a nodeLinkFn removes or adds an element at this DOM level our
7925 // offsets don't get screwed up
7926 var nodeListLength = nodeList.length;
7927 stableNodeList = new Array(nodeListLength);
7929 // create a sparse array by only copying the elements which have a linkFn
7930 for (i = 0; i < linkFns.length; i+=3) {
7932 stableNodeList[idx] = nodeList[idx];
7935 stableNodeList = nodeList;
7938 for (i = 0, ii = linkFns.length; i < ii;) {
7939 node = stableNodeList[linkFns[i++]];
7940 nodeLinkFn = linkFns[i++];
7941 childLinkFn = linkFns[i++];
7944 if (nodeLinkFn.scope) {
7945 childScope = scope.$new();
7946 compile.$$addScopeInfo(jqLite(node), childScope);
7951 if (nodeLinkFn.transcludeOnThisElement) {
7952 childBoundTranscludeFn = createBoundTranscludeFn(
7953 scope, nodeLinkFn.transclude, parentBoundTranscludeFn);
7955 } else if (!nodeLinkFn.templateOnThisElement && parentBoundTranscludeFn) {
7956 childBoundTranscludeFn = parentBoundTranscludeFn;
7958 } else if (!parentBoundTranscludeFn && transcludeFn) {
7959 childBoundTranscludeFn = createBoundTranscludeFn(scope, transcludeFn);
7962 childBoundTranscludeFn = null;
7965 nodeLinkFn(childLinkFn, childScope, node, $rootElement, childBoundTranscludeFn);
7967 } else if (childLinkFn) {
7968 childLinkFn(scope, node.childNodes, undefined, parentBoundTranscludeFn);
7974 function createBoundTranscludeFn(scope, transcludeFn, previousBoundTranscludeFn) {
7976 var boundTranscludeFn = function(transcludedScope, cloneFn, controllers, futureParentElement, containingScope) {
7978 if (!transcludedScope) {
7979 transcludedScope = scope.$new(false, containingScope);
7980 transcludedScope.$$transcluded = true;
7983 return transcludeFn(transcludedScope, cloneFn, {
7984 parentBoundTranscludeFn: previousBoundTranscludeFn,
7985 transcludeControllers: controllers,
7986 futureParentElement: futureParentElement
7990 return boundTranscludeFn;
7994 * Looks for directives on the given node and adds them to the directive collection which is
7997 * @param node Node to search.
7998 * @param directives An array to which the directives are added to. This array is sorted before
7999 * the function returns.
8000 * @param attrs The shared attrs object which is used to populate the normalized attributes.
8001 * @param {number=} maxPriority Max directive priority.
8003 function collectDirectives(node, directives, attrs, maxPriority, ignoreDirective) {
8004 var nodeType = node.nodeType,
8005 attrsMap = attrs.$attr,
8010 case NODE_TYPE_ELEMENT: /* Element */
8011 // use the node name: <directive>
8012 addDirective(directives,
8013 directiveNormalize(nodeName_(node)), 'E', maxPriority, ignoreDirective);
8015 // iterate over the attributes
8016 for (var attr, name, nName, ngAttrName, value, isNgAttr, nAttrs = node.attributes,
8017 j = 0, jj = nAttrs && nAttrs.length; j < jj; j++) {
8018 var attrStartName = false;
8019 var attrEndName = false;
8023 value = trim(attr.value);
8025 // support ngAttr attribute binding
8026 ngAttrName = directiveNormalize(name);
8027 if (isNgAttr = NG_ATTR_BINDING.test(ngAttrName)) {
8028 name = name.replace(PREFIX_REGEXP, '')
8029 .substr(8).replace(/_(.)/g, function(match, letter) {
8030 return letter.toUpperCase();
8034 var multiElementMatch = ngAttrName.match(MULTI_ELEMENT_DIR_RE);
8035 if (multiElementMatch && directiveIsMultiElement(multiElementMatch[1])) {
8036 attrStartName = name;
8037 attrEndName = name.substr(0, name.length - 5) + 'end';
8038 name = name.substr(0, name.length - 6);
8041 nName = directiveNormalize(name.toLowerCase());
8042 attrsMap[nName] = name;
8043 if (isNgAttr || !attrs.hasOwnProperty(nName)) {
8044 attrs[nName] = value;
8045 if (getBooleanAttrName(node, nName)) {
8046 attrs[nName] = true; // presence means true
8049 addAttrInterpolateDirective(node, directives, value, nName, isNgAttr);
8050 addDirective(directives, nName, 'A', maxPriority, ignoreDirective, attrStartName,
8054 // use class as directive
8055 className = node.className;
8056 if (isObject(className)) {
8057 // Maybe SVGAnimatedString
8058 className = className.animVal;
8060 if (isString(className) && className !== '') {
8061 while (match = CLASS_DIRECTIVE_REGEXP.exec(className)) {
8062 nName = directiveNormalize(match[2]);
8063 if (addDirective(directives, nName, 'C', maxPriority, ignoreDirective)) {
8064 attrs[nName] = trim(match[3]);
8066 className = className.substr(match.index + match[0].length);
8070 case NODE_TYPE_TEXT: /* Text Node */
8072 // Workaround for #11781
8073 while (node.parentNode && node.nextSibling && node.nextSibling.nodeType === NODE_TYPE_TEXT) {
8074 node.nodeValue = node.nodeValue + node.nextSibling.nodeValue;
8075 node.parentNode.removeChild(node.nextSibling);
8078 addTextInterpolateDirective(directives, node.nodeValue);
8080 case NODE_TYPE_COMMENT: /* Comment */
8082 match = COMMENT_DIRECTIVE_REGEXP.exec(node.nodeValue);
8084 nName = directiveNormalize(match[1]);
8085 if (addDirective(directives, nName, 'M', maxPriority, ignoreDirective)) {
8086 attrs[nName] = trim(match[2]);
8090 // turns out that under some circumstances IE9 throws errors when one attempts to read
8091 // comment's node value.
8092 // Just ignore it and continue. (Can't seem to reproduce in test case.)
8097 directives.sort(byPriority);
8102 * Given a node with an directive-start it collects all of the siblings until it finds
8109 function groupScan(node, attrStart, attrEnd) {
8112 if (attrStart && node.hasAttribute && node.hasAttribute(attrStart)) {
8115 throw $compileMinErr('uterdir',
8116 "Unterminated attribute, found '{0}' but no matching '{1}' found.",
8117 attrStart, attrEnd);
8119 if (node.nodeType == NODE_TYPE_ELEMENT) {
8120 if (node.hasAttribute(attrStart)) depth++;
8121 if (node.hasAttribute(attrEnd)) depth--;
8124 node = node.nextSibling;
8125 } while (depth > 0);
8130 return jqLite(nodes);
8134 * Wrapper for linking function which converts normal linking function into a grouped
8139 * @returns {Function}
8141 function groupElementsLinkFnWrapper(linkFn, attrStart, attrEnd) {
8142 return function(scope, element, attrs, controllers, transcludeFn) {
8143 element = groupScan(element[0], attrStart, attrEnd);
8144 return linkFn(scope, element, attrs, controllers, transcludeFn);
8149 * Once the directives have been collected, their compile functions are executed. This method
8150 * is responsible for inlining directive templates as well as terminating the application
8151 * of the directives if the terminal directive has been reached.
8153 * @param {Array} directives Array of collected directives to execute their compile function.
8154 * this needs to be pre-sorted by priority order.
8155 * @param {Node} compileNode The raw DOM node to apply the compile functions to
8156 * @param {Object} templateAttrs The shared attribute function
8157 * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the
8158 * scope argument is auto-generated to the new
8159 * child of the transcluded parent scope.
8160 * @param {JQLite} jqCollection If we are working on the root of the compile tree then this
8161 * argument has the root jqLite array so that we can replace nodes
8163 * @param {Object=} originalReplaceDirective An optional directive that will be ignored when
8164 * compiling the transclusion.
8165 * @param {Array.<Function>} preLinkFns
8166 * @param {Array.<Function>} postLinkFns
8167 * @param {Object} previousCompileContext Context used for previous compilation of the current
8169 * @returns {Function} linkFn
8171 function applyDirectivesToNode(directives, compileNode, templateAttrs, transcludeFn,
8172 jqCollection, originalReplaceDirective, preLinkFns, postLinkFns,
8173 previousCompileContext) {
8174 previousCompileContext = previousCompileContext || {};
8176 var terminalPriority = -Number.MAX_VALUE,
8177 newScopeDirective = previousCompileContext.newScopeDirective,
8178 controllerDirectives = previousCompileContext.controllerDirectives,
8179 newIsolateScopeDirective = previousCompileContext.newIsolateScopeDirective,
8180 templateDirective = previousCompileContext.templateDirective,
8181 nonTlbTranscludeDirective = previousCompileContext.nonTlbTranscludeDirective,
8182 hasTranscludeDirective = false,
8183 hasTemplate = false,
8184 hasElementTranscludeDirective = previousCompileContext.hasElementTranscludeDirective,
8185 $compileNode = templateAttrs.$$element = jqLite(compileNode),
8189 replaceDirective = originalReplaceDirective,
8190 childTranscludeFn = transcludeFn,
8194 // executes all directives on the current element
8195 for (var i = 0, ii = directives.length; i < ii; i++) {
8196 directive = directives[i];
8197 var attrStart = directive.$$start;
8198 var attrEnd = directive.$$end;
8200 // collect multiblock sections
8202 $compileNode = groupScan(compileNode, attrStart, attrEnd);
8204 $template = undefined;
8206 if (terminalPriority > directive.priority) {
8207 break; // prevent further processing of directives
8210 if (directiveValue = directive.scope) {
8212 // skip the check for directives with async templates, we'll check the derived sync
8213 // directive when the template arrives
8214 if (!directive.templateUrl) {
8215 if (isObject(directiveValue)) {
8216 // This directive is trying to add an isolated scope.
8217 // Check that there is no scope of any kind already
8218 assertNoDuplicate('new/isolated scope', newIsolateScopeDirective || newScopeDirective,
8219 directive, $compileNode);
8220 newIsolateScopeDirective = directive;
8222 // This directive is trying to add a child scope.
8223 // Check that there is no isolated scope already
8224 assertNoDuplicate('new/isolated scope', newIsolateScopeDirective, directive,
8229 newScopeDirective = newScopeDirective || directive;
8232 directiveName = directive.name;
8234 if (!directive.templateUrl && directive.controller) {
8235 directiveValue = directive.controller;
8236 controllerDirectives = controllerDirectives || createMap();
8237 assertNoDuplicate("'" + directiveName + "' controller",
8238 controllerDirectives[directiveName], directive, $compileNode);
8239 controllerDirectives[directiveName] = directive;
8242 if (directiveValue = directive.transclude) {
8243 hasTranscludeDirective = true;
8245 // Special case ngIf and ngRepeat so that we don't complain about duplicate transclusion.
8246 // This option should only be used by directives that know how to safely handle element transclusion,
8247 // where the transcluded nodes are added or replaced after linking.
8248 if (!directive.$$tlb) {
8249 assertNoDuplicate('transclusion', nonTlbTranscludeDirective, directive, $compileNode);
8250 nonTlbTranscludeDirective = directive;
8253 if (directiveValue == 'element') {
8254 hasElementTranscludeDirective = true;
8255 terminalPriority = directive.priority;
8256 $template = $compileNode;
8257 $compileNode = templateAttrs.$$element =
8258 jqLite(document.createComment(' ' + directiveName + ': ' +
8259 templateAttrs[directiveName] + ' '));
8260 compileNode = $compileNode[0];
8261 replaceWith(jqCollection, sliceArgs($template), compileNode);
8263 childTranscludeFn = compile($template, transcludeFn, terminalPriority,
8264 replaceDirective && replaceDirective.name, {
8266 // - controllerDirectives - otherwise we'll create duplicates controllers
8267 // - newIsolateScopeDirective or templateDirective - combining templates with
8268 // element transclusion doesn't make sense.
8270 // We need only nonTlbTranscludeDirective so that we prevent putting transclusion
8271 // on the same element more than once.
8272 nonTlbTranscludeDirective: nonTlbTranscludeDirective
8275 $template = jqLite(jqLiteClone(compileNode)).contents();
8276 $compileNode.empty(); // clear contents
8277 childTranscludeFn = compile($template, transcludeFn, undefined,
8278 undefined, { needsNewScope: directive.$$isolateScope || directive.$$newScope});
8282 if (directive.template) {
8284 assertNoDuplicate('template', templateDirective, directive, $compileNode);
8285 templateDirective = directive;
8287 directiveValue = (isFunction(directive.template))
8288 ? directive.template($compileNode, templateAttrs)
8289 : directive.template;
8291 directiveValue = denormalizeTemplate(directiveValue);
8293 if (directive.replace) {
8294 replaceDirective = directive;
8295 if (jqLiteIsTextNode(directiveValue)) {
8298 $template = removeComments(wrapTemplate(directive.templateNamespace, trim(directiveValue)));
8300 compileNode = $template[0];
8302 if ($template.length != 1 || compileNode.nodeType !== NODE_TYPE_ELEMENT) {
8303 throw $compileMinErr('tplrt',
8304 "Template for directive '{0}' must have exactly one root element. {1}",
8308 replaceWith(jqCollection, $compileNode, compileNode);
8310 var newTemplateAttrs = {$attr: {}};
8312 // combine directives from the original node and from the template:
8313 // - take the array of directives for this element
8314 // - split it into two parts, those that already applied (processed) and those that weren't (unprocessed)
8315 // - collect directives from the template and sort them by priority
8316 // - combine directives as: processed + template + unprocessed
8317 var templateDirectives = collectDirectives(compileNode, [], newTemplateAttrs);
8318 var unprocessedDirectives = directives.splice(i + 1, directives.length - (i + 1));
8320 if (newIsolateScopeDirective || newScopeDirective) {
8321 // The original directive caused the current element to be replaced but this element
8322 // also needs to have a new scope, so we need to tell the template directives
8323 // that they would need to get their scope from further up, if they require transclusion
8324 markDirectiveScope(templateDirectives, newIsolateScopeDirective, newScopeDirective);
8326 directives = directives.concat(templateDirectives).concat(unprocessedDirectives);
8327 mergeTemplateAttributes(templateAttrs, newTemplateAttrs);
8329 ii = directives.length;
8331 $compileNode.html(directiveValue);
8335 if (directive.templateUrl) {
8337 assertNoDuplicate('template', templateDirective, directive, $compileNode);
8338 templateDirective = directive;
8340 if (directive.replace) {
8341 replaceDirective = directive;
8344 nodeLinkFn = compileTemplateUrl(directives.splice(i, directives.length - i), $compileNode,
8345 templateAttrs, jqCollection, hasTranscludeDirective && childTranscludeFn, preLinkFns, postLinkFns, {
8346 controllerDirectives: controllerDirectives,
8347 newScopeDirective: (newScopeDirective !== directive) && newScopeDirective,
8348 newIsolateScopeDirective: newIsolateScopeDirective,
8349 templateDirective: templateDirective,
8350 nonTlbTranscludeDirective: nonTlbTranscludeDirective
8352 ii = directives.length;
8353 } else if (directive.compile) {
8355 linkFn = directive.compile($compileNode, templateAttrs, childTranscludeFn);
8356 if (isFunction(linkFn)) {
8357 addLinkFns(null, linkFn, attrStart, attrEnd);
8358 } else if (linkFn) {
8359 addLinkFns(linkFn.pre, linkFn.post, attrStart, attrEnd);
8362 $exceptionHandler(e, startingTag($compileNode));
8366 if (directive.terminal) {
8367 nodeLinkFn.terminal = true;
8368 terminalPriority = Math.max(terminalPriority, directive.priority);
8373 nodeLinkFn.scope = newScopeDirective && newScopeDirective.scope === true;
8374 nodeLinkFn.transcludeOnThisElement = hasTranscludeDirective;
8375 nodeLinkFn.templateOnThisElement = hasTemplate;
8376 nodeLinkFn.transclude = childTranscludeFn;
8378 previousCompileContext.hasElementTranscludeDirective = hasElementTranscludeDirective;
8380 // might be normal or delayed nodeLinkFn depending on if templateUrl is present
8383 ////////////////////
8385 function addLinkFns(pre, post, attrStart, attrEnd) {
8387 if (attrStart) pre = groupElementsLinkFnWrapper(pre, attrStart, attrEnd);
8388 pre.require = directive.require;
8389 pre.directiveName = directiveName;
8390 if (newIsolateScopeDirective === directive || directive.$$isolateScope) {
8391 pre = cloneAndAnnotateFn(pre, {isolateScope: true});
8393 preLinkFns.push(pre);
8396 if (attrStart) post = groupElementsLinkFnWrapper(post, attrStart, attrEnd);
8397 post.require = directive.require;
8398 post.directiveName = directiveName;
8399 if (newIsolateScopeDirective === directive || directive.$$isolateScope) {
8400 post = cloneAndAnnotateFn(post, {isolateScope: true});
8402 postLinkFns.push(post);
8407 function getControllers(directiveName, require, $element, elementControllers) {
8410 if (isString(require)) {
8411 var match = require.match(REQUIRE_PREFIX_REGEXP);
8412 var name = require.substring(match[0].length);
8413 var inheritType = match[1] || match[3];
8414 var optional = match[2] === '?';
8416 //If only parents then start at the parent element
8417 if (inheritType === '^^') {
8418 $element = $element.parent();
8419 //Otherwise attempt getting the controller from elementControllers in case
8420 //the element is transcluded (and has no data) and to avoid .data if possible
8422 value = elementControllers && elementControllers[name];
8423 value = value && value.instance;
8427 var dataName = '$' + name + 'Controller';
8428 value = inheritType ? $element.inheritedData(dataName) : $element.data(dataName);
8431 if (!value && !optional) {
8432 throw $compileMinErr('ctreq',
8433 "Controller '{0}', required by directive '{1}', can't be found!",
8434 name, directiveName);
8436 } else if (isArray(require)) {
8438 for (var i = 0, ii = require.length; i < ii; i++) {
8439 value[i] = getControllers(directiveName, require[i], $element, elementControllers);
8443 return value || null;
8446 function setupControllers($element, attrs, transcludeFn, controllerDirectives, isolateScope, scope) {
8447 var elementControllers = createMap();
8448 for (var controllerKey in controllerDirectives) {
8449 var directive = controllerDirectives[controllerKey];
8451 $scope: directive === newIsolateScopeDirective || directive.$$isolateScope ? isolateScope : scope,
8454 $transclude: transcludeFn
8457 var controller = directive.controller;
8458 if (controller == '@') {
8459 controller = attrs[directive.name];
8462 var controllerInstance = $controller(controller, locals, true, directive.controllerAs);
8464 // For directives with element transclusion the element is a comment.
8465 // In this case .data will not attach any data.
8466 // Instead, we save the controllers for the element in a local hash and attach to .data
8467 // later, once we have the actual element.
8468 elementControllers[directive.name] = controllerInstance;
8469 $element.data('$' + directive.name + 'Controller', controllerInstance.instance);
8471 return elementControllers;
8474 function nodeLinkFn(childLinkFn, scope, linkNode, $rootElement, boundTranscludeFn) {
8475 var linkFn, isolateScope, controllerScope, elementControllers, transcludeFn, $element,
8476 attrs, removeScopeBindingWatches, removeControllerBindingWatches;
8478 if (compileNode === linkNode) {
8479 attrs = templateAttrs;
8480 $element = templateAttrs.$$element;
8482 $element = jqLite(linkNode);
8483 attrs = new Attributes($element, templateAttrs);
8486 controllerScope = scope;
8487 if (newIsolateScopeDirective) {
8488 isolateScope = scope.$new(true);
8489 } else if (newScopeDirective) {
8490 controllerScope = scope.$parent;
8493 if (boundTranscludeFn) {
8494 // track `boundTranscludeFn` so it can be unwrapped if `transcludeFn`
8495 // is later passed as `parentBoundTranscludeFn` to `publicLinkFn`
8496 transcludeFn = controllersBoundTransclude;
8497 transcludeFn.$$boundTransclude = boundTranscludeFn;
8500 if (controllerDirectives) {
8501 elementControllers = setupControllers($element, attrs, transcludeFn, controllerDirectives, isolateScope, scope);
8504 if (newIsolateScopeDirective) {
8505 // Initialize isolate scope bindings for new isolate scope directive.
8506 compile.$$addScopeInfo($element, isolateScope, true, !(templateDirective && (templateDirective === newIsolateScopeDirective ||
8507 templateDirective === newIsolateScopeDirective.$$originalDirective)));
8508 compile.$$addScopeClass($element, true);
8509 isolateScope.$$isolateBindings =
8510 newIsolateScopeDirective.$$isolateBindings;
8511 removeScopeBindingWatches = initializeDirectiveBindings(scope, attrs, isolateScope,
8512 isolateScope.$$isolateBindings,
8513 newIsolateScopeDirective);
8514 if (removeScopeBindingWatches) {
8515 isolateScope.$on('$destroy', removeScopeBindingWatches);
8519 // Initialize bindToController bindings
8520 for (var name in elementControllers) {
8521 var controllerDirective = controllerDirectives[name];
8522 var controller = elementControllers[name];
8523 var bindings = controllerDirective.$$bindings.bindToController;
8525 if (controller.identifier && bindings) {
8526 removeControllerBindingWatches =
8527 initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective);
8530 var controllerResult = controller();
8531 if (controllerResult !== controller.instance) {
8532 // If the controller constructor has a return value, overwrite the instance
8533 // from setupControllers
8534 controller.instance = controllerResult;
8535 $element.data('$' + controllerDirective.name + 'Controller', controllerResult);
8536 removeControllerBindingWatches && removeControllerBindingWatches();
8537 removeControllerBindingWatches =
8538 initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective);
8543 for (i = 0, ii = preLinkFns.length; i < ii; i++) {
8544 linkFn = preLinkFns[i];
8545 invokeLinkFn(linkFn,
8546 linkFn.isolateScope ? isolateScope : scope,
8549 linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers),
8555 // We only pass the isolate scope, if the isolate directive has a template,
8556 // otherwise the child elements do not belong to the isolate directive.
8557 var scopeToChild = scope;
8558 if (newIsolateScopeDirective && (newIsolateScopeDirective.template || newIsolateScopeDirective.templateUrl === null)) {
8559 scopeToChild = isolateScope;
8561 childLinkFn && childLinkFn(scopeToChild, linkNode.childNodes, undefined, boundTranscludeFn);
8564 for (i = postLinkFns.length - 1; i >= 0; i--) {
8565 linkFn = postLinkFns[i];
8566 invokeLinkFn(linkFn,
8567 linkFn.isolateScope ? isolateScope : scope,
8570 linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers),
8575 // This is the function that is injected as `$transclude`.
8576 // Note: all arguments are optional!
8577 function controllersBoundTransclude(scope, cloneAttachFn, futureParentElement) {
8578 var transcludeControllers;
8580 // No scope passed in:
8581 if (!isScope(scope)) {
8582 futureParentElement = cloneAttachFn;
8583 cloneAttachFn = scope;
8587 if (hasElementTranscludeDirective) {
8588 transcludeControllers = elementControllers;
8590 if (!futureParentElement) {
8591 futureParentElement = hasElementTranscludeDirective ? $element.parent() : $element;
8593 return boundTranscludeFn(scope, cloneAttachFn, transcludeControllers, futureParentElement, scopeToChild);
8598 // Depending upon the context in which a directive finds itself it might need to have a new isolated
8599 // or child scope created. For instance:
8600 // * if the directive has been pulled into a template because another directive with a higher priority
8601 // asked for element transclusion
8602 // * if the directive itself asks for transclusion but it is at the root of a template and the original
8603 // element was replaced. See https://github.com/angular/angular.js/issues/12936
8604 function markDirectiveScope(directives, isolateScope, newScope) {
8605 for (var j = 0, jj = directives.length; j < jj; j++) {
8606 directives[j] = inherit(directives[j], {$$isolateScope: isolateScope, $$newScope: newScope});
8611 * looks up the directive and decorates it with exception handling and proper parameters. We
8612 * call this the boundDirective.
8614 * @param {string} name name of the directive to look up.
8615 * @param {string} location The directive must be found in specific format.
8616 * String containing any of theses characters:
8618 * * `E`: element name
8622 * @returns {boolean} true if directive was added.
8624 function addDirective(tDirectives, name, location, maxPriority, ignoreDirective, startAttrName,
8626 if (name === ignoreDirective) return null;
8628 if (hasDirectives.hasOwnProperty(name)) {
8629 for (var directive, directives = $injector.get(name + Suffix),
8630 i = 0, ii = directives.length; i < ii; i++) {
8632 directive = directives[i];
8633 if ((isUndefined(maxPriority) || maxPriority > directive.priority) &&
8634 directive.restrict.indexOf(location) != -1) {
8635 if (startAttrName) {
8636 directive = inherit(directive, {$$start: startAttrName, $$end: endAttrName});
8638 if (!directive.$$bindings) {
8639 var bindings = directive.$$bindings =
8640 parseDirectiveBindings(directive, directive.name);
8641 if (isObject(bindings.isolateScope)) {
8642 directive.$$isolateBindings = bindings.isolateScope;
8645 tDirectives.push(directive);
8648 } catch (e) { $exceptionHandler(e); }
8656 * looks up the directive and returns true if it is a multi-element directive,
8657 * and therefore requires DOM nodes between -start and -end markers to be grouped
8660 * @param {string} name name of the directive to look up.
8661 * @returns true if directive was registered as multi-element.
8663 function directiveIsMultiElement(name) {
8664 if (hasDirectives.hasOwnProperty(name)) {
8665 for (var directive, directives = $injector.get(name + Suffix),
8666 i = 0, ii = directives.length; i < ii; i++) {
8667 directive = directives[i];
8668 if (directive.multiElement) {
8677 * When the element is replaced with HTML template then the new attributes
8678 * on the template need to be merged with the existing attributes in the DOM.
8679 * The desired effect is to have both of the attributes present.
8681 * @param {object} dst destination attributes (original DOM)
8682 * @param {object} src source attributes (from the directive template)
8684 function mergeTemplateAttributes(dst, src) {
8685 var srcAttr = src.$attr,
8686 dstAttr = dst.$attr,
8687 $element = dst.$$element;
8689 // reapply the old attributes to the new element
8690 forEach(dst, function(value, key) {
8691 if (key.charAt(0) != '$') {
8692 if (src[key] && src[key] !== value) {
8693 value += (key === 'style' ? ';' : ' ') + src[key];
8695 dst.$set(key, value, true, srcAttr[key]);
8699 // copy the new attributes on the old attrs object
8700 forEach(src, function(value, key) {
8701 if (key == 'class') {
8702 safeAddClass($element, value);
8703 dst['class'] = (dst['class'] ? dst['class'] + ' ' : '') + value;
8704 } else if (key == 'style') {
8705 $element.attr('style', $element.attr('style') + ';' + value);
8706 dst['style'] = (dst['style'] ? dst['style'] + ';' : '') + value;
8707 // `dst` will never contain hasOwnProperty as DOM parser won't let it.
8708 // You will get an "InvalidCharacterError: DOM Exception 5" error if you
8709 // have an attribute like "has-own-property" or "data-has-own-property", etc.
8710 } else if (key.charAt(0) != '$' && !dst.hasOwnProperty(key)) {
8712 dstAttr[key] = srcAttr[key];
8718 function compileTemplateUrl(directives, $compileNode, tAttrs,
8719 $rootElement, childTranscludeFn, preLinkFns, postLinkFns, previousCompileContext) {
8721 afterTemplateNodeLinkFn,
8722 afterTemplateChildLinkFn,
8723 beforeTemplateCompileNode = $compileNode[0],
8724 origAsyncDirective = directives.shift(),
8725 derivedSyncDirective = inherit(origAsyncDirective, {
8726 templateUrl: null, transclude: null, replace: null, $$originalDirective: origAsyncDirective
8728 templateUrl = (isFunction(origAsyncDirective.templateUrl))
8729 ? origAsyncDirective.templateUrl($compileNode, tAttrs)
8730 : origAsyncDirective.templateUrl,
8731 templateNamespace = origAsyncDirective.templateNamespace;
8733 $compileNode.empty();
8735 $templateRequest(templateUrl)
8736 .then(function(content) {
8737 var compileNode, tempTemplateAttrs, $template, childBoundTranscludeFn;
8739 content = denormalizeTemplate(content);
8741 if (origAsyncDirective.replace) {
8742 if (jqLiteIsTextNode(content)) {
8745 $template = removeComments(wrapTemplate(templateNamespace, trim(content)));
8747 compileNode = $template[0];
8749 if ($template.length != 1 || compileNode.nodeType !== NODE_TYPE_ELEMENT) {
8750 throw $compileMinErr('tplrt',
8751 "Template for directive '{0}' must have exactly one root element. {1}",
8752 origAsyncDirective.name, templateUrl);
8755 tempTemplateAttrs = {$attr: {}};
8756 replaceWith($rootElement, $compileNode, compileNode);
8757 var templateDirectives = collectDirectives(compileNode, [], tempTemplateAttrs);
8759 if (isObject(origAsyncDirective.scope)) {
8760 // the original directive that caused the template to be loaded async required
8762 markDirectiveScope(templateDirectives, true);
8764 directives = templateDirectives.concat(directives);
8765 mergeTemplateAttributes(tAttrs, tempTemplateAttrs);
8767 compileNode = beforeTemplateCompileNode;
8768 $compileNode.html(content);
8771 directives.unshift(derivedSyncDirective);
8773 afterTemplateNodeLinkFn = applyDirectivesToNode(directives, compileNode, tAttrs,
8774 childTranscludeFn, $compileNode, origAsyncDirective, preLinkFns, postLinkFns,
8775 previousCompileContext);
8776 forEach($rootElement, function(node, i) {
8777 if (node == compileNode) {
8778 $rootElement[i] = $compileNode[0];
8781 afterTemplateChildLinkFn = compileNodes($compileNode[0].childNodes, childTranscludeFn);
8783 while (linkQueue.length) {
8784 var scope = linkQueue.shift(),
8785 beforeTemplateLinkNode = linkQueue.shift(),
8786 linkRootElement = linkQueue.shift(),
8787 boundTranscludeFn = linkQueue.shift(),
8788 linkNode = $compileNode[0];
8790 if (scope.$$destroyed) continue;
8792 if (beforeTemplateLinkNode !== beforeTemplateCompileNode) {
8793 var oldClasses = beforeTemplateLinkNode.className;
8795 if (!(previousCompileContext.hasElementTranscludeDirective &&
8796 origAsyncDirective.replace)) {
8797 // it was cloned therefore we have to clone as well.
8798 linkNode = jqLiteClone(compileNode);
8800 replaceWith(linkRootElement, jqLite(beforeTemplateLinkNode), linkNode);
8802 // Copy in CSS classes from original node
8803 safeAddClass(jqLite(linkNode), oldClasses);
8805 if (afterTemplateNodeLinkFn.transcludeOnThisElement) {
8806 childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn);
8808 childBoundTranscludeFn = boundTranscludeFn;
8810 afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, linkNode, $rootElement,
8811 childBoundTranscludeFn);
8816 return function delayedNodeLinkFn(ignoreChildLinkFn, scope, node, rootElement, boundTranscludeFn) {
8817 var childBoundTranscludeFn = boundTranscludeFn;
8818 if (scope.$$destroyed) return;
8820 linkQueue.push(scope,
8823 childBoundTranscludeFn);
8825 if (afterTemplateNodeLinkFn.transcludeOnThisElement) {
8826 childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn);
8828 afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, node, rootElement, childBoundTranscludeFn);
8835 * Sorting function for bound directives.
8837 function byPriority(a, b) {
8838 var diff = b.priority - a.priority;
8839 if (diff !== 0) return diff;
8840 if (a.name !== b.name) return (a.name < b.name) ? -1 : 1;
8841 return a.index - b.index;
8844 function assertNoDuplicate(what, previousDirective, directive, element) {
8846 function wrapModuleNameIfDefined(moduleName) {
8848 (' (module: ' + moduleName + ')') :
8852 if (previousDirective) {
8853 throw $compileMinErr('multidir', 'Multiple directives [{0}{1}, {2}{3}] asking for {4} on: {5}',
8854 previousDirective.name, wrapModuleNameIfDefined(previousDirective.$$moduleName),
8855 directive.name, wrapModuleNameIfDefined(directive.$$moduleName), what, startingTag(element));
8860 function addTextInterpolateDirective(directives, text) {
8861 var interpolateFn = $interpolate(text, true);
8862 if (interpolateFn) {
8865 compile: function textInterpolateCompileFn(templateNode) {
8866 var templateNodeParent = templateNode.parent(),
8867 hasCompileParent = !!templateNodeParent.length;
8869 // When transcluding a template that has bindings in the root
8870 // we don't have a parent and thus need to add the class during linking fn.
8871 if (hasCompileParent) compile.$$addBindingClass(templateNodeParent);
8873 return function textInterpolateLinkFn(scope, node) {
8874 var parent = node.parent();
8875 if (!hasCompileParent) compile.$$addBindingClass(parent);
8876 compile.$$addBindingInfo(parent, interpolateFn.expressions);
8877 scope.$watch(interpolateFn, function interpolateFnWatchAction(value) {
8878 node[0].nodeValue = value;
8887 function wrapTemplate(type, template) {
8888 type = lowercase(type || 'html');
8892 var wrapper = document.createElement('div');
8893 wrapper.innerHTML = '<' + type + '>' + template + '</' + type + '>';
8894 return wrapper.childNodes[0].childNodes;
8901 function getTrustedContext(node, attrNormalizedName) {
8902 if (attrNormalizedName == "srcdoc") {
8905 var tag = nodeName_(node);
8906 // maction[xlink:href] can source SVG. It's not limited to <maction>.
8907 if (attrNormalizedName == "xlinkHref" ||
8908 (tag == "form" && attrNormalizedName == "action") ||
8909 (tag != "img" && (attrNormalizedName == "src" ||
8910 attrNormalizedName == "ngSrc"))) {
8911 return $sce.RESOURCE_URL;
8916 function addAttrInterpolateDirective(node, directives, value, name, allOrNothing) {
8917 var trustedContext = getTrustedContext(node, name);
8918 allOrNothing = ALL_OR_NOTHING_ATTRS[name] || allOrNothing;
8920 var interpolateFn = $interpolate(value, true, trustedContext, allOrNothing);
8922 // no interpolation found -> ignore
8923 if (!interpolateFn) return;
8926 if (name === "multiple" && nodeName_(node) === "select") {
8927 throw $compileMinErr("selmulti",
8928 "Binding to the 'multiple' attribute is not supported. Element: {0}",
8934 compile: function() {
8936 pre: function attrInterpolatePreLinkFn(scope, element, attr) {
8937 var $$observers = (attr.$$observers || (attr.$$observers = createMap()));
8939 if (EVENT_HANDLER_ATTR_REGEXP.test(name)) {
8940 throw $compileMinErr('nodomevents',
8941 "Interpolations for HTML DOM event attributes are disallowed. Please use the " +
8942 "ng- versions (such as ng-click instead of onclick) instead.");
8945 // If the attribute has changed since last $interpolate()ed
8946 var newValue = attr[name];
8947 if (newValue !== value) {
8948 // we need to interpolate again since the attribute value has been updated
8949 // (e.g. by another directive's compile function)
8950 // ensure unset/empty values make interpolateFn falsy
8951 interpolateFn = newValue && $interpolate(newValue, true, trustedContext, allOrNothing);
8955 // if attribute was updated so that there is no interpolation going on we don't want to
8956 // register any observers
8957 if (!interpolateFn) return;
8959 // initialize attr object so that it's ready in case we need the value for isolate
8960 // scope initialization, otherwise the value would not be available from isolate
8961 // directive's linking fn during linking phase
8962 attr[name] = interpolateFn(scope);
8964 ($$observers[name] || ($$observers[name] = [])).$$inter = true;
8965 (attr.$$observers && attr.$$observers[name].$$scope || scope).
8966 $watch(interpolateFn, function interpolateFnWatchAction(newValue, oldValue) {
8967 //special case for class attribute addition + removal
8968 //so that class changes can tap into the animation
8969 //hooks provided by the $animate service. Be sure to
8970 //skip animations when the first digest occurs (when
8971 //both the new and the old values are the same) since
8972 //the CSS classes are the non-interpolated values
8973 if (name === 'class' && newValue != oldValue) {
8974 attr.$updateClass(newValue, oldValue);
8976 attr.$set(name, newValue);
8987 * This is a special jqLite.replaceWith, which can replace items which
8988 * have no parents, provided that the containing jqLite collection is provided.
8990 * @param {JqLite=} $rootElement The root of the compile tree. Used so that we can replace nodes
8991 * in the root of the tree.
8992 * @param {JqLite} elementsToRemove The jqLite element which we are going to replace. We keep
8993 * the shell, but replace its DOM node reference.
8994 * @param {Node} newNode The new DOM node.
8996 function replaceWith($rootElement, elementsToRemove, newNode) {
8997 var firstElementToRemove = elementsToRemove[0],
8998 removeCount = elementsToRemove.length,
8999 parent = firstElementToRemove.parentNode,
9003 for (i = 0, ii = $rootElement.length; i < ii; i++) {
9004 if ($rootElement[i] == firstElementToRemove) {
9005 $rootElement[i++] = newNode;
9006 for (var j = i, j2 = j + removeCount - 1,
9007 jj = $rootElement.length;
9008 j < jj; j++, j2++) {
9010 $rootElement[j] = $rootElement[j2];
9012 delete $rootElement[j];
9015 $rootElement.length -= removeCount - 1;
9017 // If the replaced element is also the jQuery .context then replace it
9018 // .context is a deprecated jQuery api, so we should set it only when jQuery set it
9019 // http://api.jquery.com/context/
9020 if ($rootElement.context === firstElementToRemove) {
9021 $rootElement.context = newNode;
9029 parent.replaceChild(newNode, firstElementToRemove);
9032 // TODO(perf): what's this document fragment for? is it needed? can we at least reuse it?
9033 var fragment = document.createDocumentFragment();
9034 fragment.appendChild(firstElementToRemove);
9036 if (jqLite.hasData(firstElementToRemove)) {
9037 // Copy over user data (that includes Angular's $scope etc.). Don't copy private
9038 // data here because there's no public interface in jQuery to do that and copying over
9039 // event listeners (which is the main use of private data) wouldn't work anyway.
9040 jqLite.data(newNode, jqLite.data(firstElementToRemove));
9042 // Remove data of the replaced element. We cannot just call .remove()
9043 // on the element it since that would deallocate scope that is needed
9044 // for the new node. Instead, remove the data "manually".
9046 delete jqLite.cache[firstElementToRemove[jqLite.expando]];
9048 // jQuery 2.x doesn't expose the data storage. Use jQuery.cleanData to clean up after
9049 // the replaced element. The cleanData version monkey-patched by Angular would cause
9050 // the scope to be trashed and we do need the very same scope to work with the new
9051 // element. However, we cannot just cache the non-patched version and use it here as
9052 // that would break if another library patches the method after Angular does (one
9053 // example is jQuery UI). Instead, set a flag indicating scope destroying should be
9054 // skipped this one time.
9055 skipDestroyOnNextJQueryCleanData = true;
9056 jQuery.cleanData([firstElementToRemove]);
9060 for (var k = 1, kk = elementsToRemove.length; k < kk; k++) {
9061 var element = elementsToRemove[k];
9062 jqLite(element).remove(); // must do this way to clean up expando
9063 fragment.appendChild(element);
9064 delete elementsToRemove[k];
9067 elementsToRemove[0] = newNode;
9068 elementsToRemove.length = 1;
9072 function cloneAndAnnotateFn(fn, annotation) {
9073 return extend(function() { return fn.apply(null, arguments); }, fn, annotation);
9077 function invokeLinkFn(linkFn, scope, $element, attrs, controllers, transcludeFn) {
9079 linkFn(scope, $element, attrs, controllers, transcludeFn);
9081 $exceptionHandler(e, startingTag($element));
9086 // Set up $watches for isolate scope and controller bindings. This process
9087 // only occurs for isolate scopes and new scopes with controllerAs.
9088 function initializeDirectiveBindings(scope, attrs, destination, bindings, directive) {
9089 var removeWatchCollection = [];
9090 forEach(bindings, function(definition, scopeName) {
9091 var attrName = definition.attrName,
9092 optional = definition.optional,
9093 mode = definition.mode, // @, =, or &
9095 parentGet, parentSet, compare;
9100 if (!optional && !hasOwnProperty.call(attrs, attrName)) {
9101 destination[scopeName] = attrs[attrName] = void 0;
9103 attrs.$observe(attrName, function(value) {
9104 if (isString(value)) {
9105 destination[scopeName] = value;
9108 attrs.$$observers[attrName].$$scope = scope;
9109 lastValue = attrs[attrName];
9110 if (isString(lastValue)) {
9111 // If the attribute has been provided then we trigger an interpolation to ensure
9112 // the value is there for use in the link fn
9113 destination[scopeName] = $interpolate(lastValue)(scope);
9114 } else if (isBoolean(lastValue)) {
9115 // If the attributes is one of the BOOLEAN_ATTR then Angular will have converted
9116 // the value to boolean rather than a string, so we special case this situation
9117 destination[scopeName] = lastValue;
9122 if (!hasOwnProperty.call(attrs, attrName)) {
9123 if (optional) break;
9124 attrs[attrName] = void 0;
9126 if (optional && !attrs[attrName]) break;
9128 parentGet = $parse(attrs[attrName]);
9129 if (parentGet.literal) {
9132 compare = function(a, b) { return a === b || (a !== a && b !== b); };
9134 parentSet = parentGet.assign || function() {
9135 // reset the change, or we will throw this exception on every $digest
9136 lastValue = destination[scopeName] = parentGet(scope);
9137 throw $compileMinErr('nonassign',
9138 "Expression '{0}' in attribute '{1}' used with directive '{2}' is non-assignable!",
9139 attrs[attrName], attrName, directive.name);
9141 lastValue = destination[scopeName] = parentGet(scope);
9142 var parentValueWatch = function parentValueWatch(parentValue) {
9143 if (!compare(parentValue, destination[scopeName])) {
9144 // we are out of sync and need to copy
9145 if (!compare(parentValue, lastValue)) {
9146 // parent changed and it has precedence
9147 destination[scopeName] = parentValue;
9149 // if the parent can be assigned then do so
9150 parentSet(scope, parentValue = destination[scopeName]);
9153 return lastValue = parentValue;
9155 parentValueWatch.$stateful = true;
9157 if (definition.collection) {
9158 removeWatch = scope.$watchCollection(attrs[attrName], parentValueWatch);
9160 removeWatch = scope.$watch($parse(attrs[attrName], parentValueWatch), null, parentGet.literal);
9162 removeWatchCollection.push(removeWatch);
9166 // Don't assign Object.prototype method to scope
9167 parentGet = attrs.hasOwnProperty(attrName) ? $parse(attrs[attrName]) : noop;
9169 // Don't assign noop to destination if expression is not valid
9170 if (parentGet === noop && optional) break;
9172 destination[scopeName] = function(locals) {
9173 return parentGet(scope, locals);
9179 return removeWatchCollection.length && function removeWatches() {
9180 for (var i = 0, ii = removeWatchCollection.length; i < ii; ++i) {
9181 removeWatchCollection[i]();
9188 var PREFIX_REGEXP = /^((?:x|data)[\:\-_])/i;
9190 * Converts all accepted directives format into proper directive name.
9191 * @param name Name to normalize
9193 function directiveNormalize(name) {
9194 return camelCase(name.replace(PREFIX_REGEXP, ''));
9199 * @name $compile.directive.Attributes
9202 * A shared object between directive compile / linking functions which contains normalized DOM
9203 * element attributes. The values reflect current binding state `{{ }}`. The normalization is
9204 * needed since all of these are treated as equivalent in Angular:
9207 * <span ng:bind="a" ng-bind="a" data-ng-bind="a" x-ng-bind="a">
9213 * @name $compile.directive.Attributes#$attr
9216 * A map of DOM element attribute names to the normalized name. This is
9217 * needed to do reverse lookup from normalized name back to actual name.
9223 * @name $compile.directive.Attributes#$set
9227 * Set DOM element attribute value.
9230 * @param {string} name Normalized element attribute name of the property to modify. The name is
9231 * reverse-translated using the {@link ng.$compile.directive.Attributes#$attr $attr}
9232 * property to the original name.
9233 * @param {string} value Value to set the attribute to. The value can be an interpolated string.
9239 * Closure compiler type information
9242 function nodesetLinkingFn(
9243 /* angular.Scope */ scope,
9244 /* NodeList */ nodeList,
9245 /* Element */ rootElement,
9246 /* function(Function) */ boundTranscludeFn
9249 function directiveLinkingFn(
9250 /* nodesetLinkingFn */ nodesetLinkingFn,
9251 /* angular.Scope */ scope,
9253 /* Element */ rootElement,
9254 /* function(Function) */ boundTranscludeFn
9257 function tokenDifference(str1, str2) {
9259 tokens1 = str1.split(/\s+/),
9260 tokens2 = str2.split(/\s+/);
9263 for (var i = 0; i < tokens1.length; i++) {
9264 var token = tokens1[i];
9265 for (var j = 0; j < tokens2.length; j++) {
9266 if (token == tokens2[j]) continue outer;
9268 values += (values.length > 0 ? ' ' : '') + token;
9273 function removeComments(jqNodes) {
9274 jqNodes = jqLite(jqNodes);
9275 var i = jqNodes.length;
9282 var node = jqNodes[i];
9283 if (node.nodeType === NODE_TYPE_COMMENT) {
9284 splice.call(jqNodes, i, 1);
9290 var $controllerMinErr = minErr('$controller');
9293 var CNTRL_REG = /^(\S+)(\s+as\s+([\w$]+))?$/;
9294 function identifierForController(controller, ident) {
9295 if (ident && isString(ident)) return ident;
9296 if (isString(controller)) {
9297 var match = CNTRL_REG.exec(controller);
9298 if (match) return match[3];
9305 * @name $controllerProvider
9307 * The {@link ng.$controller $controller service} is used by Angular to create new
9310 * This provider allows controller registration via the
9311 * {@link ng.$controllerProvider#register register} method.
9313 function $ControllerProvider() {
9314 var controllers = {},
9319 * @name $controllerProvider#register
9320 * @param {string|Object} name Controller name, or an object map of controllers where the keys are
9321 * the names and the values are the constructors.
9322 * @param {Function|Array} constructor Controller constructor fn (optionally decorated with DI
9323 * annotations in the array notation).
9325 this.register = function(name, constructor) {
9326 assertNotHasOwnProperty(name, 'controller');
9327 if (isObject(name)) {
9328 extend(controllers, name);
9330 controllers[name] = constructor;
9336 * @name $controllerProvider#allowGlobals
9337 * @description If called, allows `$controller` to find controller constructors on `window`
9339 this.allowGlobals = function() {
9344 this.$get = ['$injector', '$window', function($injector, $window) {
9349 * @requires $injector
9351 * @param {Function|string} constructor If called with a function then it's considered to be the
9352 * controller constructor function. Otherwise it's considered to be a string which is used
9353 * to retrieve the controller constructor using the following steps:
9355 * * check if a controller with given name is registered via `$controllerProvider`
9356 * * check if evaluating the string on the current scope returns a constructor
9357 * * if $controllerProvider#allowGlobals, check `window[constructor]` on the global
9358 * `window` object (not recommended)
9360 * The string can use the `controller as property` syntax, where the controller instance is published
9361 * as the specified property on the `scope`; the `scope` must be injected into `locals` param for this
9362 * to work correctly.
9364 * @param {Object} locals Injection locals for Controller.
9365 * @return {Object} Instance of given controller.
9368 * `$controller` service is responsible for instantiating controllers.
9370 * It's just a simple call to {@link auto.$injector $injector}, but extracted into
9371 * a service, so that one can override this service with [BC version](https://gist.github.com/1649788).
9373 return function(expression, locals, later, ident) {
9375 // param `later` --- indicates that the controller's constructor is invoked at a later time.
9376 // If true, $controller will allocate the object with the correct
9377 // prototype chain, but will not invoke the controller until a returned
9378 // callback is invoked.
9379 // param `ident` --- An optional label which overrides the label parsed from the controller
9380 // expression, if any.
9381 var instance, match, constructor, identifier;
9382 later = later === true;
9383 if (ident && isString(ident)) {
9387 if (isString(expression)) {
9388 match = expression.match(CNTRL_REG);
9390 throw $controllerMinErr('ctrlfmt',
9391 "Badly formed controller string '{0}'. " +
9392 "Must match `__name__ as __id__` or `__name__`.", expression);
9394 constructor = match[1],
9395 identifier = identifier || match[3];
9396 expression = controllers.hasOwnProperty(constructor)
9397 ? controllers[constructor]
9398 : getter(locals.$scope, constructor, true) ||
9399 (globals ? getter($window, constructor, true) : undefined);
9401 assertArgFn(expression, constructor, true);
9405 // Instantiate controller later:
9406 // This machinery is used to create an instance of the object before calling the
9407 // controller's constructor itself.
9409 // This allows properties to be added to the controller before the constructor is
9410 // invoked. Primarily, this is used for isolate scope bindings in $compile.
9412 // This feature is not intended for use by applications, and is thus not documented
9414 // Object creation: http://jsperf.com/create-constructor/2
9415 var controllerPrototype = (isArray(expression) ?
9416 expression[expression.length - 1] : expression).prototype;
9417 instance = Object.create(controllerPrototype || null);
9420 addIdentifier(locals, identifier, instance, constructor || expression.name);
9424 return instantiate = extend(function() {
9425 var result = $injector.invoke(expression, instance, locals, constructor);
9426 if (result !== instance && (isObject(result) || isFunction(result))) {
9429 // If result changed, re-assign controllerAs value to scope.
9430 addIdentifier(locals, identifier, instance, constructor || expression.name);
9436 identifier: identifier
9440 instance = $injector.instantiate(expression, locals, constructor);
9443 addIdentifier(locals, identifier, instance, constructor || expression.name);
9449 function addIdentifier(locals, identifier, instance, name) {
9450 if (!(locals && isObject(locals.$scope))) {
9451 throw minErr('$controller')('noscp',
9452 "Cannot export controller '{0}' as '{1}'! No $scope object provided via `locals`.",
9456 locals.$scope[identifier] = instance;
9467 * A {@link angular.element jQuery or jqLite} wrapper for the browser's `window.document` object.
9470 <example module="documentExample">
9471 <file name="index.html">
9472 <div ng-controller="ExampleController">
9473 <p>$document title: <b ng-bind="title"></b></p>
9474 <p>window.document title: <b ng-bind="windowTitle"></b></p>
9477 <file name="script.js">
9478 angular.module('documentExample', [])
9479 .controller('ExampleController', ['$scope', '$document', function($scope, $document) {
9480 $scope.title = $document[0].title;
9481 $scope.windowTitle = angular.element(window.document)[0].title;
9486 function $DocumentProvider() {
9487 this.$get = ['$window', function(window) {
9488 return jqLite(window.document);
9494 * @name $exceptionHandler
9498 * Any uncaught exception in angular expressions is delegated to this service.
9499 * The default implementation simply delegates to `$log.error` which logs it into
9500 * the browser console.
9502 * In unit tests, if `angular-mocks.js` is loaded, this service is overridden by
9503 * {@link ngMock.$exceptionHandler mock $exceptionHandler} which aids in testing.
9508 * angular.module('exceptionOverride', []).factory('$exceptionHandler', function() {
9509 * return function(exception, cause) {
9510 * exception.message += ' (caused by "' + cause + '")';
9516 * This example will override the normal action of `$exceptionHandler`, to make angular
9517 * exceptions fail hard when they happen, instead of just logging to the console.
9520 * Note, that code executed in event-listeners (even those registered using jqLite's `on`/`bind`
9521 * methods) does not delegate exceptions to the {@link ng.$exceptionHandler $exceptionHandler}
9522 * (unless executed during a digest).
9524 * If you wish, you can manually delegate exceptions, e.g.
9525 * `try { ... } catch(e) { $exceptionHandler(e); }`
9527 * @param {Error} exception Exception associated with the error.
9528 * @param {string=} cause optional information about the context in which
9529 * the error was thrown.
9532 function $ExceptionHandlerProvider() {
9533 this.$get = ['$log', function($log) {
9534 return function(exception, cause) {
9535 $log.error.apply($log, arguments);
9540 var $$ForceReflowProvider = function() {
9541 this.$get = ['$document', function($document) {
9542 return function(domNode) {
9543 //the line below will force the browser to perform a repaint so
9544 //that all the animated elements within the animation frame will
9545 //be properly updated and drawn on screen. This is required to
9546 //ensure that the preparation animation is properly flushed so that
9547 //the active state picks up from there. DO NOT REMOVE THIS LINE.
9548 //DO NOT OPTIMIZE THIS LINE. THE MINIFIER WILL REMOVE IT OTHERWISE WHICH
9549 //WILL RESULT IN AN UNPREDICTABLE BUG THAT IS VERY HARD TO TRACK DOWN AND
9550 //WILL TAKE YEARS AWAY FROM YOUR LIFE.
9552 if (!domNode.nodeType && domNode instanceof jqLite) {
9553 domNode = domNode[0];
9556 domNode = $document[0].body;
9558 return domNode.offsetWidth + 1;
9563 var APPLICATION_JSON = 'application/json';
9564 var CONTENT_TYPE_APPLICATION_JSON = {'Content-Type': APPLICATION_JSON + ';charset=utf-8'};
9565 var JSON_START = /^\[|^\{(?!\{)/;
9570 var JSON_PROTECTION_PREFIX = /^\)\]\}',?\n/;
9571 var $httpMinErr = minErr('$http');
9572 var $httpMinErrLegacyFn = function(method) {
9574 throw $httpMinErr('legacy', 'The method `{0}` on the promise returned from `$http` has been disabled.', method);
9578 function serializeValue(v) {
9580 return isDate(v) ? v.toISOString() : toJson(v);
9586 function $HttpParamSerializerProvider() {
9589 * @name $httpParamSerializer
9592 * Default {@link $http `$http`} params serializer that converts objects to strings
9593 * according to the following rules:
9595 * * `{'foo': 'bar'}` results in `foo=bar`
9596 * * `{'foo': Date.now()}` results in `foo=2015-04-01T09%3A50%3A49.262Z` (`toISOString()` and encoded representation of a Date object)
9597 * * `{'foo': ['bar', 'baz']}` results in `foo=bar&foo=baz` (repeated key for each array element)
9598 * * `{'foo': {'bar':'baz'}}` results in `foo=%7B%22bar%22%3A%22baz%22%7D"` (stringified and encoded representation of an object)
9600 * Note that serializer will sort the request parameters alphabetically.
9603 this.$get = function() {
9604 return function ngParamSerializer(params) {
9605 if (!params) return '';
9607 forEachSorted(params, function(value, key) {
9608 if (value === null || isUndefined(value)) return;
9609 if (isArray(value)) {
9610 forEach(value, function(v, k) {
9611 parts.push(encodeUriQuery(key) + '=' + encodeUriQuery(serializeValue(v)));
9614 parts.push(encodeUriQuery(key) + '=' + encodeUriQuery(serializeValue(value)));
9618 return parts.join('&');
9623 function $HttpParamSerializerJQLikeProvider() {
9626 * @name $httpParamSerializerJQLike
9629 * Alternative {@link $http `$http`} params serializer that follows
9630 * jQuery's [`param()`](http://api.jquery.com/jquery.param/) method logic.
9631 * The serializer will also sort the params alphabetically.
9633 * To use it for serializing `$http` request parameters, set it as the `paramSerializer` property:
9640 * paramSerializer: '$httpParamSerializerJQLike'
9644 * It is also possible to set it as the default `paramSerializer` in the
9645 * {@link $httpProvider#defaults `$httpProvider`}.
9647 * Additionally, you can inject the serializer and use it explicitly, for example to serialize
9648 * form data for submission:
9651 * .controller(function($http, $httpParamSerializerJQLike) {
9657 * data: $httpParamSerializerJQLike(myData),
9659 * 'Content-Type': 'application/x-www-form-urlencoded'
9667 this.$get = function() {
9668 return function jQueryLikeParamSerializer(params) {
9669 if (!params) return '';
9671 serialize(params, '', true);
9672 return parts.join('&');
9674 function serialize(toSerialize, prefix, topLevel) {
9675 if (toSerialize === null || isUndefined(toSerialize)) return;
9676 if (isArray(toSerialize)) {
9677 forEach(toSerialize, function(value, index) {
9678 serialize(value, prefix + '[' + (isObject(value) ? index : '') + ']');
9680 } else if (isObject(toSerialize) && !isDate(toSerialize)) {
9681 forEachSorted(toSerialize, function(value, key) {
9682 serialize(value, prefix +
9683 (topLevel ? '' : '[') +
9685 (topLevel ? '' : ']'));
9688 parts.push(encodeUriQuery(prefix) + '=' + encodeUriQuery(serializeValue(toSerialize)));
9695 function defaultHttpResponseTransform(data, headers) {
9696 if (isString(data)) {
9697 // Strip json vulnerability protection prefix and trim whitespace
9698 var tempData = data.replace(JSON_PROTECTION_PREFIX, '').trim();
9701 var contentType = headers('Content-Type');
9702 if ((contentType && (contentType.indexOf(APPLICATION_JSON) === 0)) || isJsonLike(tempData)) {
9703 data = fromJson(tempData);
9711 function isJsonLike(str) {
9712 var jsonStart = str.match(JSON_START);
9713 return jsonStart && JSON_ENDS[jsonStart[0]].test(str);
9717 * Parse headers into key value object
9719 * @param {string} headers Raw headers as a string
9720 * @returns {Object} Parsed headers as key value object
9722 function parseHeaders(headers) {
9723 var parsed = createMap(), i;
9725 function fillInParsed(key, val) {
9727 parsed[key] = parsed[key] ? parsed[key] + ', ' + val : val;
9731 if (isString(headers)) {
9732 forEach(headers.split('\n'), function(line) {
9733 i = line.indexOf(':');
9734 fillInParsed(lowercase(trim(line.substr(0, i))), trim(line.substr(i + 1)));
9736 } else if (isObject(headers)) {
9737 forEach(headers, function(headerVal, headerKey) {
9738 fillInParsed(lowercase(headerKey), trim(headerVal));
9747 * Returns a function that provides access to parsed headers.
9749 * Headers are lazy parsed when first requested.
9752 * @param {(string|Object)} headers Headers to provide access to.
9753 * @returns {function(string=)} Returns a getter function which if called with:
9755 * - if called with single an argument returns a single header value or null
9756 * - if called with no arguments returns an object containing all headers.
9758 function headersGetter(headers) {
9761 return function(name) {
9762 if (!headersObj) headersObj = parseHeaders(headers);
9765 var value = headersObj[lowercase(name)];
9766 if (value === void 0) {
9778 * Chain all given functions
9780 * This function is used for both request and response transforming
9782 * @param {*} data Data to transform.
9783 * @param {function(string=)} headers HTTP headers getter fn.
9784 * @param {number} status HTTP status code of the response.
9785 * @param {(Function|Array.<Function>)} fns Function or an array of functions.
9786 * @returns {*} Transformed data.
9788 function transformData(data, headers, status, fns) {
9789 if (isFunction(fns)) {
9790 return fns(data, headers, status);
9793 forEach(fns, function(fn) {
9794 data = fn(data, headers, status);
9801 function isSuccess(status) {
9802 return 200 <= status && status < 300;
9808 * @name $httpProvider
9810 * Use `$httpProvider` to change the default behavior of the {@link ng.$http $http} service.
9812 function $HttpProvider() {
9815 * @name $httpProvider#defaults
9818 * Object containing default values for all {@link ng.$http $http} requests.
9820 * - **`defaults.cache`** - {boolean|Object} - A boolean value or object created with
9821 * {@link ng.$cacheFactory `$cacheFactory`} to enable or disable caching of HTTP responses
9822 * by default. See {@link $http#caching $http Caching} for more information.
9824 * - **`defaults.xsrfCookieName`** - {string} - Name of cookie containing the XSRF token.
9825 * Defaults value is `'XSRF-TOKEN'`.
9827 * - **`defaults.xsrfHeaderName`** - {string} - Name of HTTP header to populate with the
9828 * XSRF token. Defaults value is `'X-XSRF-TOKEN'`.
9830 * - **`defaults.headers`** - {Object} - Default headers for all $http requests.
9831 * Refer to {@link ng.$http#setting-http-headers $http} for documentation on
9832 * setting default headers.
9833 * - **`defaults.headers.common`**
9834 * - **`defaults.headers.post`**
9835 * - **`defaults.headers.put`**
9836 * - **`defaults.headers.patch`**
9839 * - **`defaults.paramSerializer`** - `{string|function(Object<string,string>):string}` - A function
9840 * used to the prepare string representation of request parameters (specified as an object).
9841 * If specified as string, it is interpreted as a function registered with the {@link auto.$injector $injector}.
9842 * Defaults to {@link ng.$httpParamSerializer $httpParamSerializer}.
9845 var defaults = this.defaults = {
9846 // transform incoming response data
9847 transformResponse: [defaultHttpResponseTransform],
9849 // transform outgoing request data
9850 transformRequest: [function(d) {
9851 return isObject(d) && !isFile(d) && !isBlob(d) && !isFormData(d) ? toJson(d) : d;
9857 'Accept': 'application/json, text/plain, */*'
9859 post: shallowCopy(CONTENT_TYPE_APPLICATION_JSON),
9860 put: shallowCopy(CONTENT_TYPE_APPLICATION_JSON),
9861 patch: shallowCopy(CONTENT_TYPE_APPLICATION_JSON)
9864 xsrfCookieName: 'XSRF-TOKEN',
9865 xsrfHeaderName: 'X-XSRF-TOKEN',
9867 paramSerializer: '$httpParamSerializer'
9870 var useApplyAsync = false;
9873 * @name $httpProvider#useApplyAsync
9876 * Configure $http service to combine processing of multiple http responses received at around
9877 * the same time via {@link ng.$rootScope.Scope#$applyAsync $rootScope.$applyAsync}. This can result in
9878 * significant performance improvement for bigger applications that make many HTTP requests
9879 * concurrently (common during application bootstrap).
9881 * Defaults to false. If no value is specified, returns the current configured value.
9883 * @param {boolean=} value If true, when requests are loaded, they will schedule a deferred
9884 * "apply" on the next tick, giving time for subsequent requests in a roughly ~10ms window
9885 * to load and share the same digest cycle.
9887 * @returns {boolean|Object} If a value is specified, returns the $httpProvider for chaining.
9888 * otherwise, returns the current configured value.
9890 this.useApplyAsync = function(value) {
9891 if (isDefined(value)) {
9892 useApplyAsync = !!value;
9895 return useApplyAsync;
9898 var useLegacyPromise = true;
9901 * @name $httpProvider#useLegacyPromiseExtensions
9904 * Configure `$http` service to return promises without the shorthand methods `success` and `error`.
9905 * This should be used to make sure that applications work without these methods.
9907 * Defaults to true. If no value is specified, returns the current configured value.
9909 * @param {boolean=} value If true, `$http` will return a promise with the deprecated legacy `success` and `error` methods.
9911 * @returns {boolean|Object} If a value is specified, returns the $httpProvider for chaining.
9912 * otherwise, returns the current configured value.
9914 this.useLegacyPromiseExtensions = function(value) {
9915 if (isDefined(value)) {
9916 useLegacyPromise = !!value;
9919 return useLegacyPromise;
9924 * @name $httpProvider#interceptors
9927 * Array containing service factories for all synchronous or asynchronous {@link ng.$http $http}
9928 * pre-processing of request or postprocessing of responses.
9930 * These service factories are ordered by request, i.e. they are applied in the same order as the
9931 * array, on request, but reverse order, on response.
9933 * {@link ng.$http#interceptors Interceptors detailed info}
9935 var interceptorFactories = this.interceptors = [];
9937 this.$get = ['$httpBackend', '$$cookieReader', '$cacheFactory', '$rootScope', '$q', '$injector',
9938 function($httpBackend, $$cookieReader, $cacheFactory, $rootScope, $q, $injector) {
9940 var defaultCache = $cacheFactory('$http');
9943 * Make sure that default param serializer is exposed as a function
9945 defaults.paramSerializer = isString(defaults.paramSerializer) ?
9946 $injector.get(defaults.paramSerializer) : defaults.paramSerializer;
9949 * Interceptors stored in reverse order. Inner interceptors before outer interceptors.
9950 * The reversal is needed so that we can build up the interception chain around the
9953 var reversedInterceptors = [];
9955 forEach(interceptorFactories, function(interceptorFactory) {
9956 reversedInterceptors.unshift(isString(interceptorFactory)
9957 ? $injector.get(interceptorFactory) : $injector.invoke(interceptorFactory));
9964 * @requires ng.$httpBackend
9965 * @requires $cacheFactory
9966 * @requires $rootScope
9968 * @requires $injector
9971 * The `$http` service is a core Angular service that facilitates communication with the remote
9972 * HTTP servers via the browser's [XMLHttpRequest](https://developer.mozilla.org/en/xmlhttprequest)
9973 * object or via [JSONP](http://en.wikipedia.org/wiki/JSONP).
9975 * For unit testing applications that use `$http` service, see
9976 * {@link ngMock.$httpBackend $httpBackend mock}.
9978 * For a higher level of abstraction, please check out the {@link ngResource.$resource
9979 * $resource} service.
9981 * The $http API is based on the {@link ng.$q deferred/promise APIs} exposed by
9982 * the $q service. While for simple usage patterns this doesn't matter much, for advanced usage
9983 * it is important to familiarize yourself with these APIs and the guarantees they provide.
9987 * The `$http` service is a function which takes a single argument — a {@link $http#usage configuration object} —
9988 * that is used to generate an HTTP request and returns a {@link ng.$q promise}.
9991 * // Simple GET request example:
9995 * }).then(function successCallback(response) {
9996 * // this callback will be called asynchronously
9997 * // when the response is available
9998 * }, function errorCallback(response) {
9999 * // called asynchronously if an error occurs
10000 * // or server returns response with an error status.
10004 * The response object has these properties:
10006 * - **data** – `{string|Object}` – The response body transformed with the transform
10008 * - **status** – `{number}` – HTTP status code of the response.
10009 * - **headers** – `{function([headerName])}` – Header getter function.
10010 * - **config** – `{Object}` – The configuration object that was used to generate the request.
10011 * - **statusText** – `{string}` – HTTP status text of the response.
10013 * A response status code between 200 and 299 is considered a success status and
10014 * will result in the success callback being called. Note that if the response is a redirect,
10015 * XMLHttpRequest will transparently follow it, meaning that the error callback will not be
10016 * called for such responses.
10019 * ## Shortcut methods
10021 * Shortcut methods are also available. All shortcut methods require passing in the URL, and
10022 * request data must be passed in for POST/PUT requests. An optional config can be passed as the
10026 * $http.get('/someUrl', config).then(successCallback, errorCallback);
10027 * $http.post('/someUrl', data, config).then(successCallback, errorCallback);
10030 * Complete list of shortcut methods:
10032 * - {@link ng.$http#get $http.get}
10033 * - {@link ng.$http#head $http.head}
10034 * - {@link ng.$http#post $http.post}
10035 * - {@link ng.$http#put $http.put}
10036 * - {@link ng.$http#delete $http.delete}
10037 * - {@link ng.$http#jsonp $http.jsonp}
10038 * - {@link ng.$http#patch $http.patch}
10041 * ## Writing Unit Tests that use $http
10042 * When unit testing (using {@link ngMock ngMock}), it is necessary to call
10043 * {@link ngMock.$httpBackend#flush $httpBackend.flush()} to flush each pending
10044 * request using trained responses.
10047 * $httpBackend.expectGET(...);
10049 * $httpBackend.flush();
10052 * ## Deprecation Notice
10053 * <div class="alert alert-danger">
10054 * The `$http` legacy promise methods `success` and `error` have been deprecated.
10055 * Use the standard `then` method instead.
10056 * If {@link $httpProvider#useLegacyPromiseExtensions `$httpProvider.useLegacyPromiseExtensions`} is set to
10057 * `false` then these methods will throw {@link $http:legacy `$http/legacy`} error.
10060 * ## Setting HTTP Headers
10062 * The $http service will automatically add certain HTTP headers to all requests. These defaults
10063 * can be fully configured by accessing the `$httpProvider.defaults.headers` configuration
10064 * object, which currently contains this default configuration:
10066 * - `$httpProvider.defaults.headers.common` (headers that are common for all requests):
10067 * - `Accept: application/json, text/plain, * / *`
10068 * - `$httpProvider.defaults.headers.post`: (header defaults for POST requests)
10069 * - `Content-Type: application/json`
10070 * - `$httpProvider.defaults.headers.put` (header defaults for PUT requests)
10071 * - `Content-Type: application/json`
10073 * To add or overwrite these defaults, simply add or remove a property from these configuration
10074 * objects. To add headers for an HTTP method other than POST or PUT, simply add a new object
10075 * with the lowercased HTTP method name as the key, e.g.
10076 * `$httpProvider.defaults.headers.get = { 'My-Header' : 'value' }`.
10078 * The defaults can also be set at runtime via the `$http.defaults` object in the same
10079 * fashion. For example:
10082 * module.run(function($http) {
10083 * $http.defaults.headers.common.Authorization = 'Basic YmVlcDpib29w'
10087 * In addition, you can supply a `headers` property in the config object passed when
10088 * calling `$http(config)`, which overrides the defaults without changing them globally.
10090 * To explicitly remove a header automatically added via $httpProvider.defaults.headers on a per request basis,
10091 * Use the `headers` property, setting the desired header to `undefined`. For example:
10096 * url: 'http://example.com',
10098 * 'Content-Type': undefined
10100 * data: { test: 'test' }
10103 * $http(req).then(function(){...}, function(){...});
10106 * ## Transforming Requests and Responses
10108 * Both requests and responses can be transformed using transformation functions: `transformRequest`
10109 * and `transformResponse`. These properties can be a single function that returns
10110 * the transformed value (`function(data, headersGetter, status)`) or an array of such transformation functions,
10111 * which allows you to `push` or `unshift` a new transformation function into the transformation chain.
10113 * <div class="alert alert-warning">
10114 * **Note:** Angular does not make a copy of the `data` parameter before it is passed into the `transformRequest` pipeline.
10115 * That means changes to the properties of `data` are not local to the transform function (since Javascript passes objects by reference).
10116 * For example, when calling `$http.get(url, $scope.myObject)`, modifications to the object's properties in a transformRequest
10117 * function will be reflected on the scope and in any templates where the object is data-bound.
10118 * To prevent his, transform functions should have no side-effects.
10119 * If you need to modify properties, it is recommended to make a copy of the data, or create new object to return.
10122 * ### Default Transformations
10124 * The `$httpProvider` provider and `$http` service expose `defaults.transformRequest` and
10125 * `defaults.transformResponse` properties. If a request does not provide its own transformations
10126 * then these will be applied.
10128 * You can augment or replace the default transformations by modifying these properties by adding to or
10129 * replacing the array.
10131 * Angular provides the following default transformations:
10133 * Request transformations (`$httpProvider.defaults.transformRequest` and `$http.defaults.transformRequest`):
10135 * - If the `data` property of the request configuration object contains an object, serialize it
10136 * into JSON format.
10138 * Response transformations (`$httpProvider.defaults.transformResponse` and `$http.defaults.transformResponse`):
10140 * - If XSRF prefix is detected, strip it (see Security Considerations section below).
10141 * - If JSON response is detected, deserialize it using a JSON parser.
10144 * ### Overriding the Default Transformations Per Request
10146 * If you wish override the request/response transformations only for a single request then provide
10147 * `transformRequest` and/or `transformResponse` properties on the configuration object passed
10150 * Note that if you provide these properties on the config object the default transformations will be
10151 * overwritten. If you wish to augment the default transformations then you must include them in your
10152 * local transformation array.
10154 * The following code demonstrates adding a new response transformation to be run after the default response
10155 * transformations have been run.
10158 * function appendTransform(defaults, transform) {
10160 * // We can't guarantee that the default transformation is an array
10161 * defaults = angular.isArray(defaults) ? defaults : [defaults];
10163 * // Append the new transformation to the defaults
10164 * return defaults.concat(transform);
10170 * transformResponse: appendTransform($http.defaults.transformResponse, function(value) {
10171 * return doTransform(value);
10179 * {@link ng.$http `$http`} responses are not cached by default. To enable caching, you must
10180 * set the config.cache value or the default cache value to TRUE or to a cache object (created
10181 * with {@link ng.$cacheFactory `$cacheFactory`}). If defined, the value of config.cache takes
10182 * precedence over the default cache value.
10185 * * cache all responses - set the default cache value to TRUE or to a cache object
10186 * * cache a specific response - set config.cache value to TRUE or to a cache object
10188 * If caching is enabled, but neither the default cache nor config.cache are set to a cache object,
10189 * then the default `$cacheFactory($http)` object is used.
10191 * The default cache value can be set by updating the
10192 * {@link ng.$http#defaults `$http.defaults.cache`} property or the
10193 * {@link $httpProvider#defaults `$httpProvider.defaults.cache`} property.
10195 * When caching is enabled, {@link ng.$http `$http`} stores the response from the server using
10196 * the relevant cache object. The next time the same request is made, the response is returned
10197 * from the cache without sending a request to the server.
10201 * * Only GET and JSONP requests are cached.
10202 * * The cache key is the request URL including search parameters; headers are not considered.
10203 * * Cached responses are returned asynchronously, in the same way as responses from the server.
10204 * * If multiple identical requests are made using the same cache, which is not yet populated,
10205 * one request will be made to the server and remaining requests will return the same response.
10206 * * A cache-control header on the response does not affect if or how responses are cached.
10211 * Before you start creating interceptors, be sure to understand the
10212 * {@link ng.$q $q and deferred/promise APIs}.
10214 * For purposes of global error handling, authentication, or any kind of synchronous or
10215 * asynchronous pre-processing of request or postprocessing of responses, it is desirable to be
10216 * able to intercept requests before they are handed to the server and
10217 * responses before they are handed over to the application code that
10218 * initiated these requests. The interceptors leverage the {@link ng.$q
10219 * promise APIs} to fulfill this need for both synchronous and asynchronous pre-processing.
10221 * The interceptors are service factories that are registered with the `$httpProvider` by
10222 * adding them to the `$httpProvider.interceptors` array. The factory is called and
10223 * injected with dependencies (if specified) and returns the interceptor.
10225 * There are two kinds of interceptors (and two kinds of rejection interceptors):
10227 * * `request`: interceptors get called with a http {@link $http#usage config} object. The function is free to
10228 * modify the `config` object or create a new one. The function needs to return the `config`
10229 * object directly, or a promise containing the `config` or a new `config` object.
10230 * * `requestError`: interceptor gets called when a previous interceptor threw an error or
10231 * resolved with a rejection.
10232 * * `response`: interceptors get called with http `response` object. The function is free to
10233 * modify the `response` object or create a new one. The function needs to return the `response`
10234 * object directly, or as a promise containing the `response` or a new `response` object.
10235 * * `responseError`: interceptor gets called when a previous interceptor threw an error or
10236 * resolved with a rejection.
10240 * // register the interceptor as a service
10241 * $provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) {
10243 * // optional method
10244 * 'request': function(config) {
10245 * // do something on success
10249 * // optional method
10250 * 'requestError': function(rejection) {
10251 * // do something on error
10252 * if (canRecover(rejection)) {
10253 * return responseOrNewPromise
10255 * return $q.reject(rejection);
10260 * // optional method
10261 * 'response': function(response) {
10262 * // do something on success
10266 * // optional method
10267 * 'responseError': function(rejection) {
10268 * // do something on error
10269 * if (canRecover(rejection)) {
10270 * return responseOrNewPromise
10272 * return $q.reject(rejection);
10277 * $httpProvider.interceptors.push('myHttpInterceptor');
10280 * // alternatively, register the interceptor via an anonymous factory
10281 * $httpProvider.interceptors.push(function($q, dependency1, dependency2) {
10283 * 'request': function(config) {
10287 * 'response': function(response) {
10294 * ## Security Considerations
10296 * When designing web applications, consider security threats from:
10298 * - [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx)
10299 * - [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery)
10301 * Both server and the client must cooperate in order to eliminate these threats. Angular comes
10302 * pre-configured with strategies that address these issues, but for this to work backend server
10303 * cooperation is required.
10305 * ### JSON Vulnerability Protection
10307 * A [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx)
10308 * allows third party website to turn your JSON resource URL into
10309 * [JSONP](http://en.wikipedia.org/wiki/JSONP) request under some conditions. To
10310 * counter this your server can prefix all JSON requests with following string `")]}',\n"`.
10311 * Angular will automatically strip the prefix before processing it as JSON.
10313 * For example if your server needs to return:
10318 * which is vulnerable to attack, your server can return:
10324 * Angular will strip the prefix, before processing the JSON.
10327 * ### Cross Site Request Forgery (XSRF) Protection
10329 * [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery) is an attack technique by
10330 * which the attacker can trick an authenticated user into unknowingly executing actions on your
10331 * website. Angular provides a mechanism to counter XSRF. When performing XHR requests, the
10332 * $http service reads a token from a cookie (by default, `XSRF-TOKEN`) and sets it as an HTTP
10333 * header (`X-XSRF-TOKEN`). Since only JavaScript that runs on your domain could read the
10334 * cookie, your server can be assured that the XHR came from JavaScript running on your domain.
10335 * The header will not be set for cross-domain requests.
10337 * To take advantage of this, your server needs to set a token in a JavaScript readable session
10338 * cookie called `XSRF-TOKEN` on the first HTTP GET request. On subsequent XHR requests the
10339 * server can verify that the cookie matches `X-XSRF-TOKEN` HTTP header, and therefore be sure
10340 * that only JavaScript running on your domain could have sent the request. The token must be
10341 * unique for each user and must be verifiable by the server (to prevent the JavaScript from
10342 * making up its own tokens). We recommend that the token is a digest of your site's
10343 * authentication cookie with a [salt](https://en.wikipedia.org/wiki/Salt_(cryptography))
10344 * for added security.
10346 * The name of the headers can be specified using the xsrfHeaderName and xsrfCookieName
10347 * properties of either $httpProvider.defaults at config-time, $http.defaults at run-time,
10348 * or the per-request config object.
10350 * In order to prevent collisions in environments where multiple Angular apps share the
10351 * same domain or subdomain, we recommend that each application uses unique cookie name.
10353 * @param {object} config Object describing the request to be made and how it should be
10354 * processed. The object has following properties:
10356 * - **method** – `{string}` – HTTP method (e.g. 'GET', 'POST', etc)
10357 * - **url** – `{string}` – Absolute or relative URL of the resource that is being requested.
10358 * - **params** – `{Object.<string|Object>}` – Map of strings or objects which will be serialized
10359 * with the `paramSerializer` and appended as GET parameters.
10360 * - **data** – `{string|Object}` – Data to be sent as the request message data.
10361 * - **headers** – `{Object}` – Map of strings or functions which return strings representing
10362 * HTTP headers to send to the server. If the return value of a function is null, the
10363 * header will not be sent. Functions accept a config object as an argument.
10364 * - **xsrfHeaderName** – `{string}` – Name of HTTP header to populate with the XSRF token.
10365 * - **xsrfCookieName** – `{string}` – Name of cookie containing the XSRF token.
10366 * - **transformRequest** –
10367 * `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` –
10368 * transform function or an array of such functions. The transform function takes the http
10369 * request body and headers and returns its transformed (typically serialized) version.
10370 * See {@link ng.$http#overriding-the-default-transformations-per-request
10371 * Overriding the Default Transformations}
10372 * - **transformResponse** –
10373 * `{function(data, headersGetter, status)|Array.<function(data, headersGetter, status)>}` –
10374 * transform function or an array of such functions. The transform function takes the http
10375 * response body, headers and status and returns its transformed (typically deserialized) version.
10376 * See {@link ng.$http#overriding-the-default-transformations-per-request
10377 * Overriding the Default Transformations}
10378 * - **paramSerializer** - `{string|function(Object<string,string>):string}` - A function used to
10379 * prepare the string representation of request parameters (specified as an object).
10380 * If specified as string, it is interpreted as function registered with the
10381 * {@link $injector $injector}, which means you can create your own serializer
10382 * by registering it as a {@link auto.$provide#service service}.
10383 * The default serializer is the {@link $httpParamSerializer $httpParamSerializer};
10384 * alternatively, you can use the {@link $httpParamSerializerJQLike $httpParamSerializerJQLike}
10385 * - **cache** – `{boolean|Object}` – A boolean value or object created with
10386 * {@link ng.$cacheFactory `$cacheFactory`} to enable or disable caching of the HTTP response.
10387 * See {@link $http#caching $http Caching} for more information.
10388 * - **timeout** – `{number|Promise}` – timeout in milliseconds, or {@link ng.$q promise}
10389 * that should abort the request when resolved.
10390 * - **withCredentials** - `{boolean}` - whether to set the `withCredentials` flag on the
10391 * XHR object. See [requests with credentials](https://developer.mozilla.org/docs/Web/HTTP/Access_control_CORS#Requests_with_credentials)
10392 * for more information.
10393 * - **responseType** - `{string}` - see
10394 * [XMLHttpRequest.responseType](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest#xmlhttprequest-responsetype).
10396 * @returns {HttpPromise} Returns a {@link ng.$q `Promise}` that will be resolved to a response object
10397 * when the request succeeds or fails.
10400 * @property {Array.<Object>} pendingRequests Array of config objects for currently pending
10401 * requests. This is primarily meant to be used for debugging purposes.
10405 <example module="httpExample">
10406 <file name="index.html">
10407 <div ng-controller="FetchController">
10408 <select ng-model="method" aria-label="Request method">
10409 <option>GET</option>
10410 <option>JSONP</option>
10412 <input type="text" ng-model="url" size="80" aria-label="URL" />
10413 <button id="fetchbtn" ng-click="fetch()">fetch</button><br>
10414 <button id="samplegetbtn" ng-click="updateModel('GET', 'http-hello.html')">Sample GET</button>
10415 <button id="samplejsonpbtn"
10416 ng-click="updateModel('JSONP',
10417 'https://angularjs.org/greet.php?callback=JSON_CALLBACK&name=Super%20Hero')">
10420 <button id="invalidjsonpbtn"
10421 ng-click="updateModel('JSONP', 'https://angularjs.org/doesntexist&callback=JSON_CALLBACK')">
10424 <pre>http status code: {{status}}</pre>
10425 <pre>http response data: {{data}}</pre>
10428 <file name="script.js">
10429 angular.module('httpExample', [])
10430 .controller('FetchController', ['$scope', '$http', '$templateCache',
10431 function($scope, $http, $templateCache) {
10432 $scope.method = 'GET';
10433 $scope.url = 'http-hello.html';
10435 $scope.fetch = function() {
10436 $scope.code = null;
10437 $scope.response = null;
10439 $http({method: $scope.method, url: $scope.url, cache: $templateCache}).
10440 then(function(response) {
10441 $scope.status = response.status;
10442 $scope.data = response.data;
10443 }, function(response) {
10444 $scope.data = response.data || "Request failed";
10445 $scope.status = response.status;
10449 $scope.updateModel = function(method, url) {
10450 $scope.method = method;
10455 <file name="http-hello.html">
10458 <file name="protractor.js" type="protractor">
10459 var status = element(by.binding('status'));
10460 var data = element(by.binding('data'));
10461 var fetchBtn = element(by.id('fetchbtn'));
10462 var sampleGetBtn = element(by.id('samplegetbtn'));
10463 var sampleJsonpBtn = element(by.id('samplejsonpbtn'));
10464 var invalidJsonpBtn = element(by.id('invalidjsonpbtn'));
10466 it('should make an xhr GET request', function() {
10467 sampleGetBtn.click();
10469 expect(status.getText()).toMatch('200');
10470 expect(data.getText()).toMatch(/Hello, \$http!/);
10473 // Commented out due to flakes. See https://github.com/angular/angular.js/issues/9185
10474 // it('should make a JSONP request to angularjs.org', function() {
10475 // sampleJsonpBtn.click();
10476 // fetchBtn.click();
10477 // expect(status.getText()).toMatch('200');
10478 // expect(data.getText()).toMatch(/Super Hero!/);
10481 it('should make JSONP request to invalid URL and invoke the error handler',
10483 invalidJsonpBtn.click();
10485 expect(status.getText()).toMatch('0');
10486 expect(data.getText()).toMatch('Request failed');
10491 function $http(requestConfig) {
10493 if (!angular.isObject(requestConfig)) {
10494 throw minErr('$http')('badreq', 'Http request configuration must be an object. Received: {0}', requestConfig);
10497 if (!isString(requestConfig.url)) {
10498 throw minErr('$http')('badreq', 'Http request configuration url must be a string. Received: {0}', requestConfig.url);
10501 var config = extend({
10503 transformRequest: defaults.transformRequest,
10504 transformResponse: defaults.transformResponse,
10505 paramSerializer: defaults.paramSerializer
10508 config.headers = mergeHeaders(requestConfig);
10509 config.method = uppercase(config.method);
10510 config.paramSerializer = isString(config.paramSerializer) ?
10511 $injector.get(config.paramSerializer) : config.paramSerializer;
10513 var serverRequest = function(config) {
10514 var headers = config.headers;
10515 var reqData = transformData(config.data, headersGetter(headers), undefined, config.transformRequest);
10517 // strip content-type if data is undefined
10518 if (isUndefined(reqData)) {
10519 forEach(headers, function(value, header) {
10520 if (lowercase(header) === 'content-type') {
10521 delete headers[header];
10526 if (isUndefined(config.withCredentials) && !isUndefined(defaults.withCredentials)) {
10527 config.withCredentials = defaults.withCredentials;
10531 return sendReq(config, reqData).then(transformResponse, transformResponse);
10534 var chain = [serverRequest, undefined];
10535 var promise = $q.when(config);
10537 // apply interceptors
10538 forEach(reversedInterceptors, function(interceptor) {
10539 if (interceptor.request || interceptor.requestError) {
10540 chain.unshift(interceptor.request, interceptor.requestError);
10542 if (interceptor.response || interceptor.responseError) {
10543 chain.push(interceptor.response, interceptor.responseError);
10547 while (chain.length) {
10548 var thenFn = chain.shift();
10549 var rejectFn = chain.shift();
10551 promise = promise.then(thenFn, rejectFn);
10554 if (useLegacyPromise) {
10555 promise.success = function(fn) {
10556 assertArgFn(fn, 'fn');
10558 promise.then(function(response) {
10559 fn(response.data, response.status, response.headers, config);
10564 promise.error = function(fn) {
10565 assertArgFn(fn, 'fn');
10567 promise.then(null, function(response) {
10568 fn(response.data, response.status, response.headers, config);
10573 promise.success = $httpMinErrLegacyFn('success');
10574 promise.error = $httpMinErrLegacyFn('error');
10579 function transformResponse(response) {
10580 // make a copy since the response must be cacheable
10581 var resp = extend({}, response);
10582 resp.data = transformData(response.data, response.headers, response.status,
10583 config.transformResponse);
10584 return (isSuccess(response.status))
10589 function executeHeaderFns(headers, config) {
10590 var headerContent, processedHeaders = {};
10592 forEach(headers, function(headerFn, header) {
10593 if (isFunction(headerFn)) {
10594 headerContent = headerFn(config);
10595 if (headerContent != null) {
10596 processedHeaders[header] = headerContent;
10599 processedHeaders[header] = headerFn;
10603 return processedHeaders;
10606 function mergeHeaders(config) {
10607 var defHeaders = defaults.headers,
10608 reqHeaders = extend({}, config.headers),
10609 defHeaderName, lowercaseDefHeaderName, reqHeaderName;
10611 defHeaders = extend({}, defHeaders.common, defHeaders[lowercase(config.method)]);
10613 // using for-in instead of forEach to avoid unecessary iteration after header has been found
10614 defaultHeadersIteration:
10615 for (defHeaderName in defHeaders) {
10616 lowercaseDefHeaderName = lowercase(defHeaderName);
10618 for (reqHeaderName in reqHeaders) {
10619 if (lowercase(reqHeaderName) === lowercaseDefHeaderName) {
10620 continue defaultHeadersIteration;
10624 reqHeaders[defHeaderName] = defHeaders[defHeaderName];
10627 // execute if header value is a function for merged headers
10628 return executeHeaderFns(reqHeaders, shallowCopy(config));
10632 $http.pendingRequests = [];
10639 * Shortcut method to perform `GET` request.
10641 * @param {string} url Relative or absolute URL specifying the destination of the request
10642 * @param {Object=} config Optional configuration object
10643 * @returns {HttpPromise} Future object
10648 * @name $http#delete
10651 * Shortcut method to perform `DELETE` request.
10653 * @param {string} url Relative or absolute URL specifying the destination of the request
10654 * @param {Object=} config Optional configuration object
10655 * @returns {HttpPromise} Future object
10663 * Shortcut method to perform `HEAD` request.
10665 * @param {string} url Relative or absolute URL specifying the destination of the request
10666 * @param {Object=} config Optional configuration object
10667 * @returns {HttpPromise} Future object
10672 * @name $http#jsonp
10675 * Shortcut method to perform `JSONP` request.
10677 * @param {string} url Relative or absolute URL specifying the destination of the request.
10678 * The name of the callback should be the string `JSON_CALLBACK`.
10679 * @param {Object=} config Optional configuration object
10680 * @returns {HttpPromise} Future object
10682 createShortMethods('get', 'delete', 'head', 'jsonp');
10689 * Shortcut method to perform `POST` request.
10691 * @param {string} url Relative or absolute URL specifying the destination of the request
10692 * @param {*} data Request content
10693 * @param {Object=} config Optional configuration object
10694 * @returns {HttpPromise} Future object
10702 * Shortcut method to perform `PUT` request.
10704 * @param {string} url Relative or absolute URL specifying the destination of the request
10705 * @param {*} data Request content
10706 * @param {Object=} config Optional configuration object
10707 * @returns {HttpPromise} Future object
10712 * @name $http#patch
10715 * Shortcut method to perform `PATCH` request.
10717 * @param {string} url Relative or absolute URL specifying the destination of the request
10718 * @param {*} data Request content
10719 * @param {Object=} config Optional configuration object
10720 * @returns {HttpPromise} Future object
10722 createShortMethodsWithData('post', 'put', 'patch');
10726 * @name $http#defaults
10729 * Runtime equivalent of the `$httpProvider.defaults` property. Allows configuration of
10730 * default headers, withCredentials as well as request and response transformations.
10732 * See "Setting HTTP Headers" and "Transforming Requests and Responses" sections above.
10734 $http.defaults = defaults;
10740 function createShortMethods(names) {
10741 forEach(arguments, function(name) {
10742 $http[name] = function(url, config) {
10743 return $http(extend({}, config || {}, {
10752 function createShortMethodsWithData(name) {
10753 forEach(arguments, function(name) {
10754 $http[name] = function(url, data, config) {
10755 return $http(extend({}, config || {}, {
10766 * Makes the request.
10768 * !!! ACCESSES CLOSURE VARS:
10769 * $httpBackend, defaults, $log, $rootScope, defaultCache, $http.pendingRequests
10771 function sendReq(config, reqData) {
10772 var deferred = $q.defer(),
10773 promise = deferred.promise,
10776 reqHeaders = config.headers,
10777 url = buildUrl(config.url, config.paramSerializer(config.params));
10779 $http.pendingRequests.push(config);
10780 promise.then(removePendingReq, removePendingReq);
10783 if ((config.cache || defaults.cache) && config.cache !== false &&
10784 (config.method === 'GET' || config.method === 'JSONP')) {
10785 cache = isObject(config.cache) ? config.cache
10786 : isObject(defaults.cache) ? defaults.cache
10791 cachedResp = cache.get(url);
10792 if (isDefined(cachedResp)) {
10793 if (isPromiseLike(cachedResp)) {
10794 // cached request has already been sent, but there is no response yet
10795 cachedResp.then(resolvePromiseWithResult, resolvePromiseWithResult);
10797 // serving from cache
10798 if (isArray(cachedResp)) {
10799 resolvePromise(cachedResp[1], cachedResp[0], shallowCopy(cachedResp[2]), cachedResp[3]);
10801 resolvePromise(cachedResp, 200, {}, 'OK');
10805 // put the promise for the non-transformed response into cache as a placeholder
10806 cache.put(url, promise);
10811 // if we won't have the response in cache, set the xsrf headers and
10812 // send the request to the backend
10813 if (isUndefined(cachedResp)) {
10814 var xsrfValue = urlIsSameOrigin(config.url)
10815 ? $$cookieReader()[config.xsrfCookieName || defaults.xsrfCookieName]
10818 reqHeaders[(config.xsrfHeaderName || defaults.xsrfHeaderName)] = xsrfValue;
10821 $httpBackend(config.method, url, reqData, done, reqHeaders, config.timeout,
10822 config.withCredentials, config.responseType);
10829 * Callback registered to $httpBackend():
10830 * - caches the response if desired
10831 * - resolves the raw $http promise
10834 function done(status, response, headersString, statusText) {
10836 if (isSuccess(status)) {
10837 cache.put(url, [status, response, parseHeaders(headersString), statusText]);
10839 // remove promise from the cache
10844 function resolveHttpPromise() {
10845 resolvePromise(response, status, headersString, statusText);
10848 if (useApplyAsync) {
10849 $rootScope.$applyAsync(resolveHttpPromise);
10851 resolveHttpPromise();
10852 if (!$rootScope.$$phase) $rootScope.$apply();
10858 * Resolves the raw $http promise.
10860 function resolvePromise(response, status, headers, statusText) {
10861 //status: HTTP response status code, 0, -1 (aborted by timeout / promise)
10862 status = status >= -1 ? status : 0;
10864 (isSuccess(status) ? deferred.resolve : deferred.reject)({
10867 headers: headersGetter(headers),
10869 statusText: statusText
10873 function resolvePromiseWithResult(result) {
10874 resolvePromise(result.data, result.status, shallowCopy(result.headers()), result.statusText);
10877 function removePendingReq() {
10878 var idx = $http.pendingRequests.indexOf(config);
10879 if (idx !== -1) $http.pendingRequests.splice(idx, 1);
10884 function buildUrl(url, serializedParams) {
10885 if (serializedParams.length > 0) {
10886 url += ((url.indexOf('?') == -1) ? '?' : '&') + serializedParams;
10895 * @name $xhrFactory
10898 * Factory function used to create XMLHttpRequest objects.
10900 * Replace or decorate this service to create your own custom XMLHttpRequest objects.
10903 * angular.module('myApp', [])
10904 * .factory('$xhrFactory', function() {
10905 * return function createXhr(method, url) {
10906 * return new window.XMLHttpRequest({mozSystem: true});
10911 * @param {string} method HTTP method of the request (GET, POST, PUT, ..)
10912 * @param {string} url URL of the request.
10914 function $xhrFactoryProvider() {
10915 this.$get = function() {
10916 return function createXhr() {
10917 return new window.XMLHttpRequest();
10924 * @name $httpBackend
10925 * @requires $window
10926 * @requires $document
10927 * @requires $xhrFactory
10930 * HTTP backend used by the {@link ng.$http service} that delegates to
10931 * XMLHttpRequest object or JSONP and deals with browser incompatibilities.
10933 * You should never need to use this service directly, instead use the higher-level abstractions:
10934 * {@link ng.$http $http} or {@link ngResource.$resource $resource}.
10936 * During testing this implementation is swapped with {@link ngMock.$httpBackend mock
10937 * $httpBackend} which can be trained with responses.
10939 function $HttpBackendProvider() {
10940 this.$get = ['$browser', '$window', '$document', '$xhrFactory', function($browser, $window, $document, $xhrFactory) {
10941 return createHttpBackend($browser, $xhrFactory, $browser.defer, $window.angular.callbacks, $document[0]);
10945 function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDocument) {
10946 // TODO(vojta): fix the signature
10947 return function(method, url, post, callback, headers, timeout, withCredentials, responseType) {
10948 $browser.$$incOutstandingRequestCount();
10949 url = url || $browser.url();
10951 if (lowercase(method) == 'jsonp') {
10952 var callbackId = '_' + (callbacks.counter++).toString(36);
10953 callbacks[callbackId] = function(data) {
10954 callbacks[callbackId].data = data;
10955 callbacks[callbackId].called = true;
10958 var jsonpDone = jsonpReq(url.replace('JSON_CALLBACK', 'angular.callbacks.' + callbackId),
10959 callbackId, function(status, text) {
10960 completeRequest(callback, status, callbacks[callbackId].data, "", text);
10961 callbacks[callbackId] = noop;
10965 var xhr = createXhr(method, url);
10967 xhr.open(method, url, true);
10968 forEach(headers, function(value, key) {
10969 if (isDefined(value)) {
10970 xhr.setRequestHeader(key, value);
10974 xhr.onload = function requestLoaded() {
10975 var statusText = xhr.statusText || '';
10977 // responseText is the old-school way of retrieving response (supported by IE9)
10978 // response/responseType properties were introduced in XHR Level2 spec (supported by IE10)
10979 var response = ('response' in xhr) ? xhr.response : xhr.responseText;
10981 // normalize IE9 bug (http://bugs.jquery.com/ticket/1450)
10982 var status = xhr.status === 1223 ? 204 : xhr.status;
10984 // fix status code when it is 0 (0 status is undocumented).
10985 // Occurs when accessing file resources or on Android 4.1 stock browser
10986 // while retrieving files from application cache.
10987 if (status === 0) {
10988 status = response ? 200 : urlResolve(url).protocol == 'file' ? 404 : 0;
10991 completeRequest(callback,
10994 xhr.getAllResponseHeaders(),
10998 var requestError = function() {
10999 // The response is always empty
11000 // See https://xhr.spec.whatwg.org/#request-error-steps and https://fetch.spec.whatwg.org/#concept-network-error
11001 completeRequest(callback, -1, null, null, '');
11004 xhr.onerror = requestError;
11005 xhr.onabort = requestError;
11007 if (withCredentials) {
11008 xhr.withCredentials = true;
11011 if (responseType) {
11013 xhr.responseType = responseType;
11015 // WebKit added support for the json responseType value on 09/03/2013
11016 // https://bugs.webkit.org/show_bug.cgi?id=73648. Versions of Safari prior to 7 are
11017 // known to throw when setting the value "json" as the response type. Other older
11018 // browsers implementing the responseType
11020 // The json response type can be ignored if not supported, because JSON payloads are
11021 // parsed on the client-side regardless.
11022 if (responseType !== 'json') {
11028 xhr.send(isUndefined(post) ? null : post);
11032 var timeoutId = $browserDefer(timeoutRequest, timeout);
11033 } else if (isPromiseLike(timeout)) {
11034 timeout.then(timeoutRequest);
11038 function timeoutRequest() {
11039 jsonpDone && jsonpDone();
11040 xhr && xhr.abort();
11043 function completeRequest(callback, status, response, headersString, statusText) {
11044 // cancel timeout and subsequent timeout promise resolution
11045 if (isDefined(timeoutId)) {
11046 $browserDefer.cancel(timeoutId);
11048 jsonpDone = xhr = null;
11050 callback(status, response, headersString, statusText);
11051 $browser.$$completeOutstandingRequest(noop);
11055 function jsonpReq(url, callbackId, done) {
11056 // we can't use jQuery/jqLite here because jQuery does crazy stuff with script elements, e.g.:
11057 // - fetches local scripts via XHR and evals them
11058 // - adds and immediately removes script elements from the document
11059 var script = rawDocument.createElement('script'), callback = null;
11060 script.type = "text/javascript";
11062 script.async = true;
11064 callback = function(event) {
11065 removeEventListenerFn(script, "load", callback);
11066 removeEventListenerFn(script, "error", callback);
11067 rawDocument.body.removeChild(script);
11070 var text = "unknown";
11073 if (event.type === "load" && !callbacks[callbackId].called) {
11074 event = { type: "error" };
11077 status = event.type === "error" ? 404 : 200;
11081 done(status, text);
11085 addEventListenerFn(script, "load", callback);
11086 addEventListenerFn(script, "error", callback);
11087 rawDocument.body.appendChild(script);
11092 var $interpolateMinErr = angular.$interpolateMinErr = minErr('$interpolate');
11093 $interpolateMinErr.throwNoconcat = function(text) {
11094 throw $interpolateMinErr('noconcat',
11095 "Error while interpolating: {0}\nStrict Contextual Escaping disallows " +
11096 "interpolations that concatenate multiple expressions when a trusted value is " +
11097 "required. See http://docs.angularjs.org/api/ng.$sce", text);
11100 $interpolateMinErr.interr = function(text, err) {
11101 return $interpolateMinErr('interr', "Can't interpolate: {0}\n{1}", text, err.toString());
11106 * @name $interpolateProvider
11110 * Used for configuring the interpolation markup. Defaults to `{{` and `}}`.
11113 <example name="custom-interpolation-markup" module="customInterpolationApp">
11114 <file name="index.html">
11116 var customInterpolationApp = angular.module('customInterpolationApp', []);
11118 customInterpolationApp.config(function($interpolateProvider) {
11119 $interpolateProvider.startSymbol('//');
11120 $interpolateProvider.endSymbol('//');
11124 customInterpolationApp.controller('DemoController', function() {
11125 this.label = "This binding is brought you by // interpolation symbols.";
11128 <div ng-controller="DemoController as demo">
11132 <file name="protractor.js" type="protractor">
11133 it('should interpolate binding with custom symbols', function() {
11134 expect(element(by.binding('demo.label')).getText()).toBe('This binding is brought you by // interpolation symbols.');
11139 function $InterpolateProvider() {
11140 var startSymbol = '{{';
11141 var endSymbol = '}}';
11145 * @name $interpolateProvider#startSymbol
11147 * Symbol to denote start of expression in the interpolated string. Defaults to `{{`.
11149 * @param {string=} value new value to set the starting symbol to.
11150 * @returns {string|self} Returns the symbol when used as getter and self if used as setter.
11152 this.startSymbol = function(value) {
11154 startSymbol = value;
11157 return startSymbol;
11163 * @name $interpolateProvider#endSymbol
11165 * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
11167 * @param {string=} value new value to set the ending symbol to.
11168 * @returns {string|self} Returns the symbol when used as getter and self if used as setter.
11170 this.endSymbol = function(value) {
11180 this.$get = ['$parse', '$exceptionHandler', '$sce', function($parse, $exceptionHandler, $sce) {
11181 var startSymbolLength = startSymbol.length,
11182 endSymbolLength = endSymbol.length,
11183 escapedStartRegexp = new RegExp(startSymbol.replace(/./g, escape), 'g'),
11184 escapedEndRegexp = new RegExp(endSymbol.replace(/./g, escape), 'g');
11186 function escape(ch) {
11187 return '\\\\\\' + ch;
11190 function unescapeText(text) {
11191 return text.replace(escapedStartRegexp, startSymbol).
11192 replace(escapedEndRegexp, endSymbol);
11195 function stringify(value) {
11196 if (value == null) { // null || undefined
11199 switch (typeof value) {
11203 value = '' + value;
11206 value = toJson(value);
11214 * @name $interpolate
11222 * Compiles a string with markup into an interpolation function. This service is used by the
11223 * HTML {@link ng.$compile $compile} service for data binding. See
11224 * {@link ng.$interpolateProvider $interpolateProvider} for configuring the
11225 * interpolation markup.
11229 * var $interpolate = ...; // injected
11230 * var exp = $interpolate('Hello {{name | uppercase}}!');
11231 * expect(exp({name:'Angular'})).toEqual('Hello ANGULAR!');
11234 * `$interpolate` takes an optional fourth argument, `allOrNothing`. If `allOrNothing` is
11235 * `true`, the interpolation function will return `undefined` unless all embedded expressions
11236 * evaluate to a value other than `undefined`.
11239 * var $interpolate = ...; // injected
11240 * var context = {greeting: 'Hello', name: undefined };
11242 * // default "forgiving" mode
11243 * var exp = $interpolate('{{greeting}} {{name}}!');
11244 * expect(exp(context)).toEqual('Hello !');
11246 * // "allOrNothing" mode
11247 * exp = $interpolate('{{greeting}} {{name}}!', false, null, true);
11248 * expect(exp(context)).toBeUndefined();
11249 * context.name = 'Angular';
11250 * expect(exp(context)).toEqual('Hello Angular!');
11253 * `allOrNothing` is useful for interpolating URLs. `ngSrc` and `ngSrcset` use this behavior.
11255 * ####Escaped Interpolation
11256 * $interpolate provides a mechanism for escaping interpolation markers. Start and end markers
11257 * can be escaped by preceding each of their characters with a REVERSE SOLIDUS U+005C (backslash).
11258 * It will be rendered as a regular start/end marker, and will not be interpreted as an expression
11261 * This enables web-servers to prevent script injection attacks and defacing attacks, to some
11262 * degree, while also enabling code examples to work without relying on the
11263 * {@link ng.directive:ngNonBindable ngNonBindable} directive.
11265 * **For security purposes, it is strongly encouraged that web servers escape user-supplied data,
11266 * replacing angle brackets (<, >) with &lt; and &gt; respectively, and replacing all
11267 * interpolation start/end markers with their escaped counterparts.**
11269 * Escaped interpolation markers are only replaced with the actual interpolation markers in rendered
11270 * output when the $interpolate service processes the text. So, for HTML elements interpolated
11271 * by {@link ng.$compile $compile}, or otherwise interpolated with the `mustHaveExpression` parameter
11272 * set to `true`, the interpolated text must contain an unescaped interpolation expression. As such,
11273 * this is typically useful only when user-data is used in rendering a template from the server, or
11274 * when otherwise untrusted data is used by a directive.
11277 * <file name="index.html">
11278 * <div ng-init="username='A user'">
11279 * <p ng-init="apptitle='Escaping demo'">{{apptitle}}: \{\{ username = "defaced value"; \}\}
11281 * <p><strong>{{username}}</strong> attempts to inject code which will deface the
11282 * application, but fails to accomplish their task, because the server has correctly
11283 * escaped the interpolation start/end markers with REVERSE SOLIDUS U+005C (backslash)
11285 * <p>Instead, the result of the attempted script injection is visible, and can be removed
11286 * from the database by an administrator.</p>
11291 * @param {string} text The text with markup to interpolate.
11292 * @param {boolean=} mustHaveExpression if set to true then the interpolation string must have
11293 * embedded expression in order to return an interpolation function. Strings with no
11294 * embedded expression will return null for the interpolation function.
11295 * @param {string=} trustedContext when provided, the returned function passes the interpolated
11296 * result through {@link ng.$sce#getTrusted $sce.getTrusted(interpolatedResult,
11297 * trustedContext)} before returning it. Refer to the {@link ng.$sce $sce} service that
11298 * provides Strict Contextual Escaping for details.
11299 * @param {boolean=} allOrNothing if `true`, then the returned function returns undefined
11300 * unless all embedded expressions evaluate to a value other than `undefined`.
11301 * @returns {function(context)} an interpolation function which is used to compute the
11302 * interpolated string. The function has these parameters:
11304 * - `context`: evaluation context for all expressions embedded in the interpolated text
11306 function $interpolate(text, mustHaveExpression, trustedContext, allOrNothing) {
11307 allOrNothing = !!allOrNothing;
11313 textLength = text.length,
11316 expressionPositions = [];
11318 while (index < textLength) {
11319 if (((startIndex = text.indexOf(startSymbol, index)) != -1) &&
11320 ((endIndex = text.indexOf(endSymbol, startIndex + startSymbolLength)) != -1)) {
11321 if (index !== startIndex) {
11322 concat.push(unescapeText(text.substring(index, startIndex)));
11324 exp = text.substring(startIndex + startSymbolLength, endIndex);
11325 expressions.push(exp);
11326 parseFns.push($parse(exp, parseStringifyInterceptor));
11327 index = endIndex + endSymbolLength;
11328 expressionPositions.push(concat.length);
11331 // we did not find an interpolation, so we have to add the remainder to the separators array
11332 if (index !== textLength) {
11333 concat.push(unescapeText(text.substring(index)));
11339 // Concatenating expressions makes it hard to reason about whether some combination of
11340 // concatenated values are unsafe to use and could easily lead to XSS. By requiring that a
11341 // single expression be used for iframe[src], object[src], etc., we ensure that the value
11342 // that's used is assigned or constructed by some JS code somewhere that is more testable or
11343 // make it obvious that you bound the value to some user controlled value. This helps reduce
11344 // the load when auditing for XSS issues.
11345 if (trustedContext && concat.length > 1) {
11346 $interpolateMinErr.throwNoconcat(text);
11349 if (!mustHaveExpression || expressions.length) {
11350 var compute = function(values) {
11351 for (var i = 0, ii = expressions.length; i < ii; i++) {
11352 if (allOrNothing && isUndefined(values[i])) return;
11353 concat[expressionPositions[i]] = values[i];
11355 return concat.join('');
11358 var getValue = function(value) {
11359 return trustedContext ?
11360 $sce.getTrusted(trustedContext, value) :
11361 $sce.valueOf(value);
11364 return extend(function interpolationFn(context) {
11366 var ii = expressions.length;
11367 var values = new Array(ii);
11370 for (; i < ii; i++) {
11371 values[i] = parseFns[i](context);
11374 return compute(values);
11376 $exceptionHandler($interpolateMinErr.interr(text, err));
11380 // all of these properties are undocumented for now
11381 exp: text, //just for compatibility with regular watchers created via $watch
11382 expressions: expressions,
11383 $$watchDelegate: function(scope, listener) {
11385 return scope.$watchGroup(parseFns, function interpolateFnWatcher(values, oldValues) {
11386 var currValue = compute(values);
11387 if (isFunction(listener)) {
11388 listener.call(this, currValue, values !== oldValues ? lastValue : currValue, scope);
11390 lastValue = currValue;
11396 function parseStringifyInterceptor(value) {
11398 value = getValue(value);
11399 return allOrNothing && !isDefined(value) ? value : stringify(value);
11401 $exceptionHandler($interpolateMinErr.interr(text, err));
11409 * @name $interpolate#startSymbol
11411 * Symbol to denote the start of expression in the interpolated string. Defaults to `{{`.
11413 * Use {@link ng.$interpolateProvider#startSymbol `$interpolateProvider.startSymbol`} to change
11416 * @returns {string} start symbol.
11418 $interpolate.startSymbol = function() {
11419 return startSymbol;
11425 * @name $interpolate#endSymbol
11427 * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
11429 * Use {@link ng.$interpolateProvider#endSymbol `$interpolateProvider.endSymbol`} to change
11432 * @returns {string} end symbol.
11434 $interpolate.endSymbol = function() {
11438 return $interpolate;
11442 function $IntervalProvider() {
11443 this.$get = ['$rootScope', '$window', '$q', '$$q',
11444 function($rootScope, $window, $q, $$q) {
11445 var intervals = {};
11453 * Angular's wrapper for `window.setInterval`. The `fn` function is executed every `delay`
11456 * The return value of registering an interval function is a promise. This promise will be
11457 * notified upon each tick of the interval, and will be resolved after `count` iterations, or
11458 * run indefinitely if `count` is not defined. The value of the notification will be the
11459 * number of iterations that have run.
11460 * To cancel an interval, call `$interval.cancel(promise)`.
11462 * In tests you can use {@link ngMock.$interval#flush `$interval.flush(millis)`} to
11463 * move forward by `millis` milliseconds and trigger any functions scheduled to run in that
11466 * <div class="alert alert-warning">
11467 * **Note**: Intervals created by this service must be explicitly destroyed when you are finished
11468 * with them. In particular they are not automatically destroyed when a controller's scope or a
11469 * directive's element are destroyed.
11470 * You should take this into consideration and make sure to always cancel the interval at the
11471 * appropriate moment. See the example below for more details on how and when to do this.
11474 * @param {function()} fn A function that should be called repeatedly.
11475 * @param {number} delay Number of milliseconds between each function call.
11476 * @param {number=} [count=0] Number of times to repeat. If not set, or 0, will repeat
11478 * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
11479 * will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
11480 * @param {...*=} Pass additional parameters to the executed function.
11481 * @returns {promise} A promise which will be notified on each iteration.
11484 * <example module="intervalExample">
11485 * <file name="index.html">
11487 * angular.module('intervalExample', [])
11488 * .controller('ExampleController', ['$scope', '$interval',
11489 * function($scope, $interval) {
11490 * $scope.format = 'M/d/yy h:mm:ss a';
11491 * $scope.blood_1 = 100;
11492 * $scope.blood_2 = 120;
11495 * $scope.fight = function() {
11496 * // Don't start a new fight if we are already fighting
11497 * if ( angular.isDefined(stop) ) return;
11499 * stop = $interval(function() {
11500 * if ($scope.blood_1 > 0 && $scope.blood_2 > 0) {
11501 * $scope.blood_1 = $scope.blood_1 - 3;
11502 * $scope.blood_2 = $scope.blood_2 - 4;
11504 * $scope.stopFight();
11509 * $scope.stopFight = function() {
11510 * if (angular.isDefined(stop)) {
11511 * $interval.cancel(stop);
11512 * stop = undefined;
11516 * $scope.resetFight = function() {
11517 * $scope.blood_1 = 100;
11518 * $scope.blood_2 = 120;
11521 * $scope.$on('$destroy', function() {
11522 * // Make sure that the interval is destroyed too
11523 * $scope.stopFight();
11526 * // Register the 'myCurrentTime' directive factory method.
11527 * // We inject $interval and dateFilter service since the factory method is DI.
11528 * .directive('myCurrentTime', ['$interval', 'dateFilter',
11529 * function($interval, dateFilter) {
11530 * // return the directive link function. (compile function not needed)
11531 * return function(scope, element, attrs) {
11532 * var format, // date format
11533 * stopTime; // so that we can cancel the time updates
11535 * // used to update the UI
11536 * function updateTime() {
11537 * element.text(dateFilter(new Date(), format));
11540 * // watch the expression, and update the UI on change.
11541 * scope.$watch(attrs.myCurrentTime, function(value) {
11546 * stopTime = $interval(updateTime, 1000);
11548 * // listen on DOM destroy (removal) event, and cancel the next UI update
11549 * // to prevent updating time after the DOM element was removed.
11550 * element.on('$destroy', function() {
11551 * $interval.cancel(stopTime);
11558 * <div ng-controller="ExampleController">
11559 * <label>Date format: <input ng-model="format"></label> <hr/>
11560 * Current time is: <span my-current-time="format"></span>
11562 * Blood 1 : <font color='red'>{{blood_1}}</font>
11563 * Blood 2 : <font color='red'>{{blood_2}}</font>
11564 * <button type="button" data-ng-click="fight()">Fight</button>
11565 * <button type="button" data-ng-click="stopFight()">StopFight</button>
11566 * <button type="button" data-ng-click="resetFight()">resetFight</button>
11573 function interval(fn, delay, count, invokeApply) {
11574 var hasParams = arguments.length > 4,
11575 args = hasParams ? sliceArgs(arguments, 4) : [],
11576 setInterval = $window.setInterval,
11577 clearInterval = $window.clearInterval,
11579 skipApply = (isDefined(invokeApply) && !invokeApply),
11580 deferred = (skipApply ? $$q : $q).defer(),
11581 promise = deferred.promise;
11583 count = isDefined(count) ? count : 0;
11585 promise.then(null, null, (!hasParams) ? fn : function() {
11586 fn.apply(null, args);
11589 promise.$$intervalId = setInterval(function tick() {
11590 deferred.notify(iteration++);
11592 if (count > 0 && iteration >= count) {
11593 deferred.resolve(iteration);
11594 clearInterval(promise.$$intervalId);
11595 delete intervals[promise.$$intervalId];
11598 if (!skipApply) $rootScope.$apply();
11602 intervals[promise.$$intervalId] = deferred;
11610 * @name $interval#cancel
11613 * Cancels a task associated with the `promise`.
11615 * @param {Promise=} promise returned by the `$interval` function.
11616 * @returns {boolean} Returns `true` if the task was successfully canceled.
11618 interval.cancel = function(promise) {
11619 if (promise && promise.$$intervalId in intervals) {
11620 intervals[promise.$$intervalId].reject('canceled');
11621 $window.clearInterval(promise.$$intervalId);
11622 delete intervals[promise.$$intervalId];
11637 * $locale service provides localization rules for various Angular components. As of right now the
11638 * only public api is:
11640 * * `id` – `{string}` – locale id formatted as `languageId-countryId` (e.g. `en-us`)
11643 var PATH_MATCH = /^([^\?#]*)(\?([^#]*))?(#(.*))?$/,
11644 DEFAULT_PORTS = {'http': 80, 'https': 443, 'ftp': 21};
11645 var $locationMinErr = minErr('$location');
11649 * Encode path using encodeUriSegment, ignoring forward slashes
11651 * @param {string} path Path to encode
11652 * @returns {string}
11654 function encodePath(path) {
11655 var segments = path.split('/'),
11656 i = segments.length;
11659 segments[i] = encodeUriSegment(segments[i]);
11662 return segments.join('/');
11665 function parseAbsoluteUrl(absoluteUrl, locationObj) {
11666 var parsedUrl = urlResolve(absoluteUrl);
11668 locationObj.$$protocol = parsedUrl.protocol;
11669 locationObj.$$host = parsedUrl.hostname;
11670 locationObj.$$port = toInt(parsedUrl.port) || DEFAULT_PORTS[parsedUrl.protocol] || null;
11674 function parseAppUrl(relativeUrl, locationObj) {
11675 var prefixed = (relativeUrl.charAt(0) !== '/');
11677 relativeUrl = '/' + relativeUrl;
11679 var match = urlResolve(relativeUrl);
11680 locationObj.$$path = decodeURIComponent(prefixed && match.pathname.charAt(0) === '/' ?
11681 match.pathname.substring(1) : match.pathname);
11682 locationObj.$$search = parseKeyValue(match.search);
11683 locationObj.$$hash = decodeURIComponent(match.hash);
11685 // make sure path starts with '/';
11686 if (locationObj.$$path && locationObj.$$path.charAt(0) != '/') {
11687 locationObj.$$path = '/' + locationObj.$$path;
11694 * @param {string} begin
11695 * @param {string} whole
11696 * @returns {string} returns text from whole after begin or undefined if it does not begin with
11699 function beginsWith(begin, whole) {
11700 if (whole.indexOf(begin) === 0) {
11701 return whole.substr(begin.length);
11706 function stripHash(url) {
11707 var index = url.indexOf('#');
11708 return index == -1 ? url : url.substr(0, index);
11711 function trimEmptyHash(url) {
11712 return url.replace(/(#.+)|#$/, '$1');
11716 function stripFile(url) {
11717 return url.substr(0, stripHash(url).lastIndexOf('/') + 1);
11720 /* return the server only (scheme://host:port) */
11721 function serverBase(url) {
11722 return url.substring(0, url.indexOf('/', url.indexOf('//') + 2));
11727 * LocationHtml5Url represents an url
11728 * This object is exposed as $location service when HTML5 mode is enabled and supported
11731 * @param {string} appBase application base URL
11732 * @param {string} appBaseNoFile application base URL stripped of any filename
11733 * @param {string} basePrefix url path prefix
11735 function LocationHtml5Url(appBase, appBaseNoFile, basePrefix) {
11736 this.$$html5 = true;
11737 basePrefix = basePrefix || '';
11738 parseAbsoluteUrl(appBase, this);
11742 * Parse given html5 (regular) url string into properties
11743 * @param {string} url HTML5 url
11746 this.$$parse = function(url) {
11747 var pathUrl = beginsWith(appBaseNoFile, url);
11748 if (!isString(pathUrl)) {
11749 throw $locationMinErr('ipthprfx', 'Invalid url "{0}", missing path prefix "{1}".', url,
11753 parseAppUrl(pathUrl, this);
11755 if (!this.$$path) {
11763 * Compose url and update `absUrl` property
11766 this.$$compose = function() {
11767 var search = toKeyValue(this.$$search),
11768 hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
11770 this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
11771 this.$$absUrl = appBaseNoFile + this.$$url.substr(1); // first char is always '/'
11774 this.$$parseLinkUrl = function(url, relHref) {
11775 if (relHref && relHref[0] === '#') {
11776 // special case for links to hash fragments:
11777 // keep the old url and only replace the hash fragment
11778 this.hash(relHref.slice(1));
11781 var appUrl, prevAppUrl;
11784 if (isDefined(appUrl = beginsWith(appBase, url))) {
11785 prevAppUrl = appUrl;
11786 if (isDefined(appUrl = beginsWith(basePrefix, appUrl))) {
11787 rewrittenUrl = appBaseNoFile + (beginsWith('/', appUrl) || appUrl);
11789 rewrittenUrl = appBase + prevAppUrl;
11791 } else if (isDefined(appUrl = beginsWith(appBaseNoFile, url))) {
11792 rewrittenUrl = appBaseNoFile + appUrl;
11793 } else if (appBaseNoFile == url + '/') {
11794 rewrittenUrl = appBaseNoFile;
11796 if (rewrittenUrl) {
11797 this.$$parse(rewrittenUrl);
11799 return !!rewrittenUrl;
11805 * LocationHashbangUrl represents url
11806 * This object is exposed as $location service when developer doesn't opt into html5 mode.
11807 * It also serves as the base class for html5 mode fallback on legacy browsers.
11810 * @param {string} appBase application base URL
11811 * @param {string} appBaseNoFile application base URL stripped of any filename
11812 * @param {string} hashPrefix hashbang prefix
11814 function LocationHashbangUrl(appBase, appBaseNoFile, hashPrefix) {
11816 parseAbsoluteUrl(appBase, this);
11820 * Parse given hashbang url into properties
11821 * @param {string} url Hashbang url
11824 this.$$parse = function(url) {
11825 var withoutBaseUrl = beginsWith(appBase, url) || beginsWith(appBaseNoFile, url);
11826 var withoutHashUrl;
11828 if (!isUndefined(withoutBaseUrl) && withoutBaseUrl.charAt(0) === '#') {
11830 // The rest of the url starts with a hash so we have
11831 // got either a hashbang path or a plain hash fragment
11832 withoutHashUrl = beginsWith(hashPrefix, withoutBaseUrl);
11833 if (isUndefined(withoutHashUrl)) {
11834 // There was no hashbang prefix so we just have a hash fragment
11835 withoutHashUrl = withoutBaseUrl;
11839 // There was no hashbang path nor hash fragment:
11840 // If we are in HTML5 mode we use what is left as the path;
11841 // Otherwise we ignore what is left
11842 if (this.$$html5) {
11843 withoutHashUrl = withoutBaseUrl;
11845 withoutHashUrl = '';
11846 if (isUndefined(withoutBaseUrl)) {
11853 parseAppUrl(withoutHashUrl, this);
11855 this.$$path = removeWindowsDriveName(this.$$path, withoutHashUrl, appBase);
11860 * In Windows, on an anchor node on documents loaded from
11861 * the filesystem, the browser will return a pathname
11862 * prefixed with the drive name ('/C:/path') when a
11863 * pathname without a drive is set:
11864 * * a.setAttribute('href', '/foo')
11865 * * a.pathname === '/C:/foo' //true
11867 * Inside of Angular, we're always using pathnames that
11868 * do not include drive names for routing.
11870 function removeWindowsDriveName(path, url, base) {
11872 Matches paths for file protocol on windows,
11873 such as /C:/foo/bar, and captures only /foo/bar.
11875 var windowsFilePathExp = /^\/[A-Z]:(\/.*)/;
11877 var firstPathSegmentMatch;
11879 //Get the relative path from the input URL.
11880 if (url.indexOf(base) === 0) {
11881 url = url.replace(base, '');
11884 // The input URL intentionally contains a first path segment that ends with a colon.
11885 if (windowsFilePathExp.exec(url)) {
11889 firstPathSegmentMatch = windowsFilePathExp.exec(path);
11890 return firstPathSegmentMatch ? firstPathSegmentMatch[1] : path;
11895 * Compose hashbang url and update `absUrl` property
11898 this.$$compose = function() {
11899 var search = toKeyValue(this.$$search),
11900 hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
11902 this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
11903 this.$$absUrl = appBase + (this.$$url ? hashPrefix + this.$$url : '');
11906 this.$$parseLinkUrl = function(url, relHref) {
11907 if (stripHash(appBase) == stripHash(url)) {
11917 * LocationHashbangUrl represents url
11918 * This object is exposed as $location service when html5 history api is enabled but the browser
11919 * does not support it.
11922 * @param {string} appBase application base URL
11923 * @param {string} appBaseNoFile application base URL stripped of any filename
11924 * @param {string} hashPrefix hashbang prefix
11926 function LocationHashbangInHtml5Url(appBase, appBaseNoFile, hashPrefix) {
11927 this.$$html5 = true;
11928 LocationHashbangUrl.apply(this, arguments);
11930 this.$$parseLinkUrl = function(url, relHref) {
11931 if (relHref && relHref[0] === '#') {
11932 // special case for links to hash fragments:
11933 // keep the old url and only replace the hash fragment
11934 this.hash(relHref.slice(1));
11941 if (appBase == stripHash(url)) {
11942 rewrittenUrl = url;
11943 } else if ((appUrl = beginsWith(appBaseNoFile, url))) {
11944 rewrittenUrl = appBase + hashPrefix + appUrl;
11945 } else if (appBaseNoFile === url + '/') {
11946 rewrittenUrl = appBaseNoFile;
11948 if (rewrittenUrl) {
11949 this.$$parse(rewrittenUrl);
11951 return !!rewrittenUrl;
11954 this.$$compose = function() {
11955 var search = toKeyValue(this.$$search),
11956 hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
11958 this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
11959 // include hashPrefix in $$absUrl when $$url is empty so IE9 does not reload page because of removal of '#'
11960 this.$$absUrl = appBase + hashPrefix + this.$$url;
11966 var locationPrototype = {
11969 * Are we in html5 mode?
11975 * Has any change been replacing?
11982 * @name $location#absUrl
11985 * This method is getter only.
11987 * Return full url representation with all segments encoded according to rules specified in
11988 * [RFC 3986](http://www.ietf.org/rfc/rfc3986.txt).
11992 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
11993 * var absUrl = $location.absUrl();
11994 * // => "http://example.com/#/some/path?foo=bar&baz=xoxo"
11997 * @return {string} full url
11999 absUrl: locationGetter('$$absUrl'),
12003 * @name $location#url
12006 * This method is getter / setter.
12008 * Return url (e.g. `/path?a=b#hash`) when called without any parameter.
12010 * Change path, search and hash, when called with parameter and return `$location`.
12014 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
12015 * var url = $location.url();
12016 * // => "/some/path?foo=bar&baz=xoxo"
12019 * @param {string=} url New url without base prefix (e.g. `/path?a=b#hash`)
12020 * @return {string} url
12022 url: function(url) {
12023 if (isUndefined(url)) {
12027 var match = PATH_MATCH.exec(url);
12028 if (match[1] || url === '') this.path(decodeURIComponent(match[1]));
12029 if (match[2] || match[1] || url === '') this.search(match[3] || '');
12030 this.hash(match[5] || '');
12037 * @name $location#protocol
12040 * This method is getter only.
12042 * Return protocol of current url.
12046 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
12047 * var protocol = $location.protocol();
12051 * @return {string} protocol of current url
12053 protocol: locationGetter('$$protocol'),
12057 * @name $location#host
12060 * This method is getter only.
12062 * Return host of current url.
12064 * Note: compared to the non-angular version `location.host` which returns `hostname:port`, this returns the `hostname` portion only.
12068 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
12069 * var host = $location.host();
12070 * // => "example.com"
12072 * // given url http://user:password@example.com:8080/#/some/path?foo=bar&baz=xoxo
12073 * host = $location.host();
12074 * // => "example.com"
12075 * host = location.host;
12076 * // => "example.com:8080"
12079 * @return {string} host of current url.
12081 host: locationGetter('$$host'),
12085 * @name $location#port
12088 * This method is getter only.
12090 * Return port of current url.
12094 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
12095 * var port = $location.port();
12099 * @return {Number} port
12101 port: locationGetter('$$port'),
12105 * @name $location#path
12108 * This method is getter / setter.
12110 * Return path of current url when called without any parameter.
12112 * Change path when called with parameter and return `$location`.
12114 * Note: Path should always begin with forward slash (/), this method will add the forward slash
12115 * if it is missing.
12119 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
12120 * var path = $location.path();
12121 * // => "/some/path"
12124 * @param {(string|number)=} path New path
12125 * @return {string} path
12127 path: locationGetterSetter('$$path', function(path) {
12128 path = path !== null ? path.toString() : '';
12129 return path.charAt(0) == '/' ? path : '/' + path;
12134 * @name $location#search
12137 * This method is getter / setter.
12139 * Return search part (as object) of current url when called without any parameter.
12141 * Change search part when called with parameter and return `$location`.
12145 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
12146 * var searchObject = $location.search();
12147 * // => {foo: 'bar', baz: 'xoxo'}
12149 * // set foo to 'yipee'
12150 * $location.search('foo', 'yipee');
12151 * // $location.search() => {foo: 'yipee', baz: 'xoxo'}
12154 * @param {string|Object.<string>|Object.<Array.<string>>} search New search params - string or
12157 * When called with a single argument the method acts as a setter, setting the `search` component
12158 * of `$location` to the specified value.
12160 * If the argument is a hash object containing an array of values, these values will be encoded
12161 * as duplicate search parameters in the url.
12163 * @param {(string|Number|Array<string>|boolean)=} paramValue If `search` is a string or number, then `paramValue`
12164 * will override only a single search property.
12166 * If `paramValue` is an array, it will override the property of the `search` component of
12167 * `$location` specified via the first argument.
12169 * If `paramValue` is `null`, the property specified via the first argument will be deleted.
12171 * If `paramValue` is `true`, the property specified via the first argument will be added with no
12172 * value nor trailing equal sign.
12174 * @return {Object} If called with no arguments returns the parsed `search` object. If called with
12175 * one or more arguments returns `$location` object itself.
12177 search: function(search, paramValue) {
12178 switch (arguments.length) {
12180 return this.$$search;
12182 if (isString(search) || isNumber(search)) {
12183 search = search.toString();
12184 this.$$search = parseKeyValue(search);
12185 } else if (isObject(search)) {
12186 search = copy(search, {});
12187 // remove object undefined or null properties
12188 forEach(search, function(value, key) {
12189 if (value == null) delete search[key];
12192 this.$$search = search;
12194 throw $locationMinErr('isrcharg',
12195 'The first argument of the `$location#search()` call must be a string or an object.');
12199 if (isUndefined(paramValue) || paramValue === null) {
12200 delete this.$$search[search];
12202 this.$$search[search] = paramValue;
12212 * @name $location#hash
12215 * This method is getter / setter.
12217 * Returns the hash fragment when called without any parameters.
12219 * Changes the hash fragment when called with a parameter and returns `$location`.
12223 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo#hashValue
12224 * var hash = $location.hash();
12225 * // => "hashValue"
12228 * @param {(string|number)=} hash New hash fragment
12229 * @return {string} hash
12231 hash: locationGetterSetter('$$hash', function(hash) {
12232 return hash !== null ? hash.toString() : '';
12237 * @name $location#replace
12240 * If called, all changes to $location during the current `$digest` will replace the current history
12241 * record, instead of adding a new one.
12243 replace: function() {
12244 this.$$replace = true;
12249 forEach([LocationHashbangInHtml5Url, LocationHashbangUrl, LocationHtml5Url], function(Location) {
12250 Location.prototype = Object.create(locationPrototype);
12254 * @name $location#state
12257 * This method is getter / setter.
12259 * Return the history state object when called without any parameter.
12261 * Change the history state object when called with one parameter and return `$location`.
12262 * The state object is later passed to `pushState` or `replaceState`.
12264 * NOTE: This method is supported only in HTML5 mode and only in browsers supporting
12265 * the HTML5 History API (i.e. methods `pushState` and `replaceState`). If you need to support
12266 * older browsers (like IE9 or Android < 4.0), don't use this method.
12268 * @param {object=} state State object for pushState or replaceState
12269 * @return {object} state
12271 Location.prototype.state = function(state) {
12272 if (!arguments.length) {
12273 return this.$$state;
12276 if (Location !== LocationHtml5Url || !this.$$html5) {
12277 throw $locationMinErr('nostate', 'History API state support is available only ' +
12278 'in HTML5 mode and only in browsers supporting HTML5 History API');
12280 // The user might modify `stateObject` after invoking `$location.state(stateObject)`
12281 // but we're changing the $$state reference to $browser.state() during the $digest
12282 // so the modification window is narrow.
12283 this.$$state = isUndefined(state) ? null : state;
12290 function locationGetter(property) {
12291 return function() {
12292 return this[property];
12297 function locationGetterSetter(property, preprocess) {
12298 return function(value) {
12299 if (isUndefined(value)) {
12300 return this[property];
12303 this[property] = preprocess(value);
12315 * @requires $rootElement
12318 * The $location service parses the URL in the browser address bar (based on the
12319 * [window.location](https://developer.mozilla.org/en/window.location)) and makes the URL
12320 * available to your application. Changes to the URL in the address bar are reflected into
12321 * $location service and changes to $location are reflected into the browser address bar.
12323 * **The $location service:**
12325 * - Exposes the current URL in the browser address bar, so you can
12326 * - Watch and observe the URL.
12327 * - Change the URL.
12328 * - Synchronizes the URL with the browser when the user
12329 * - Changes the address bar.
12330 * - Clicks the back or forward button (or clicks a History link).
12331 * - Clicks on a link.
12332 * - Represents the URL object as a set of methods (protocol, host, port, path, search, hash).
12334 * For more information see {@link guide/$location Developer Guide: Using $location}
12339 * @name $locationProvider
12341 * Use the `$locationProvider` to configure how the application deep linking paths are stored.
12343 function $LocationProvider() {
12344 var hashPrefix = '',
12353 * @name $locationProvider#hashPrefix
12355 * @param {string=} prefix Prefix for hash part (containing path and search)
12356 * @returns {*} current value if used as getter or itself (chaining) if used as setter
12358 this.hashPrefix = function(prefix) {
12359 if (isDefined(prefix)) {
12360 hashPrefix = prefix;
12369 * @name $locationProvider#html5Mode
12371 * @param {(boolean|Object)=} mode If boolean, sets `html5Mode.enabled` to value.
12372 * If object, sets `enabled`, `requireBase` and `rewriteLinks` to respective values. Supported
12374 * - **enabled** – `{boolean}` – (default: false) If true, will rely on `history.pushState` to
12375 * change urls where supported. Will fall back to hash-prefixed paths in browsers that do not
12376 * support `pushState`.
12377 * - **requireBase** - `{boolean}` - (default: `true`) When html5Mode is enabled, specifies
12378 * whether or not a <base> tag is required to be present. If `enabled` and `requireBase` are
12379 * true, and a base tag is not present, an error will be thrown when `$location` is injected.
12380 * See the {@link guide/$location $location guide for more information}
12381 * - **rewriteLinks** - `{boolean}` - (default: `true`) When html5Mode is enabled,
12382 * enables/disables url rewriting for relative links.
12384 * @returns {Object} html5Mode object if used as getter or itself (chaining) if used as setter
12386 this.html5Mode = function(mode) {
12387 if (isBoolean(mode)) {
12388 html5Mode.enabled = mode;
12390 } else if (isObject(mode)) {
12392 if (isBoolean(mode.enabled)) {
12393 html5Mode.enabled = mode.enabled;
12396 if (isBoolean(mode.requireBase)) {
12397 html5Mode.requireBase = mode.requireBase;
12400 if (isBoolean(mode.rewriteLinks)) {
12401 html5Mode.rewriteLinks = mode.rewriteLinks;
12412 * @name $location#$locationChangeStart
12413 * @eventType broadcast on root scope
12415 * Broadcasted before a URL will change.
12417 * This change can be prevented by calling
12418 * `preventDefault` method of the event. See {@link ng.$rootScope.Scope#$on} for more
12419 * details about event object. Upon successful change
12420 * {@link ng.$location#$locationChangeSuccess $locationChangeSuccess} is fired.
12422 * The `newState` and `oldState` parameters may be defined only in HTML5 mode and when
12423 * the browser supports the HTML5 History API.
12425 * @param {Object} angularEvent Synthetic event object.
12426 * @param {string} newUrl New URL
12427 * @param {string=} oldUrl URL that was before it was changed.
12428 * @param {string=} newState New history state object
12429 * @param {string=} oldState History state object that was before it was changed.
12434 * @name $location#$locationChangeSuccess
12435 * @eventType broadcast on root scope
12437 * Broadcasted after a URL was changed.
12439 * The `newState` and `oldState` parameters may be defined only in HTML5 mode and when
12440 * the browser supports the HTML5 History API.
12442 * @param {Object} angularEvent Synthetic event object.
12443 * @param {string} newUrl New URL
12444 * @param {string=} oldUrl URL that was before it was changed.
12445 * @param {string=} newState New history state object
12446 * @param {string=} oldState History state object that was before it was changed.
12449 this.$get = ['$rootScope', '$browser', '$sniffer', '$rootElement', '$window',
12450 function($rootScope, $browser, $sniffer, $rootElement, $window) {
12453 baseHref = $browser.baseHref(), // if base[href] is undefined, it defaults to ''
12454 initialUrl = $browser.url(),
12457 if (html5Mode.enabled) {
12458 if (!baseHref && html5Mode.requireBase) {
12459 throw $locationMinErr('nobase',
12460 "$location in HTML5 mode requires a <base> tag to be present!");
12462 appBase = serverBase(initialUrl) + (baseHref || '/');
12463 LocationMode = $sniffer.history ? LocationHtml5Url : LocationHashbangInHtml5Url;
12465 appBase = stripHash(initialUrl);
12466 LocationMode = LocationHashbangUrl;
12468 var appBaseNoFile = stripFile(appBase);
12470 $location = new LocationMode(appBase, appBaseNoFile, '#' + hashPrefix);
12471 $location.$$parseLinkUrl(initialUrl, initialUrl);
12473 $location.$$state = $browser.state();
12475 var IGNORE_URI_REGEXP = /^\s*(javascript|mailto):/i;
12477 function setBrowserUrlWithFallback(url, replace, state) {
12478 var oldUrl = $location.url();
12479 var oldState = $location.$$state;
12481 $browser.url(url, replace, state);
12483 // Make sure $location.state() returns referentially identical (not just deeply equal)
12484 // state object; this makes possible quick checking if the state changed in the digest
12485 // loop. Checking deep equality would be too expensive.
12486 $location.$$state = $browser.state();
12488 // Restore old values if pushState fails
12489 $location.url(oldUrl);
12490 $location.$$state = oldState;
12496 $rootElement.on('click', function(event) {
12497 // TODO(vojta): rewrite link when opening in new tab/window (in legacy browser)
12498 // currently we open nice url link and redirect then
12500 if (!html5Mode.rewriteLinks || event.ctrlKey || event.metaKey || event.shiftKey || event.which == 2 || event.button == 2) return;
12502 var elm = jqLite(event.target);
12504 // traverse the DOM up to find first A tag
12505 while (nodeName_(elm[0]) !== 'a') {
12506 // ignore rewriting if no A tag (reached root element, or no parent - removed from document)
12507 if (elm[0] === $rootElement[0] || !(elm = elm.parent())[0]) return;
12510 var absHref = elm.prop('href');
12511 // get the actual href attribute - see
12512 // http://msdn.microsoft.com/en-us/library/ie/dd347148(v=vs.85).aspx
12513 var relHref = elm.attr('href') || elm.attr('xlink:href');
12515 if (isObject(absHref) && absHref.toString() === '[object SVGAnimatedString]') {
12516 // SVGAnimatedString.animVal should be identical to SVGAnimatedString.baseVal, unless during
12518 absHref = urlResolve(absHref.animVal).href;
12521 // Ignore when url is started with javascript: or mailto:
12522 if (IGNORE_URI_REGEXP.test(absHref)) return;
12524 if (absHref && !elm.attr('target') && !event.isDefaultPrevented()) {
12525 if ($location.$$parseLinkUrl(absHref, relHref)) {
12526 // We do a preventDefault for all urls that are part of the angular application,
12527 // in html5mode and also without, so that we are able to abort navigation without
12528 // getting double entries in the location history.
12529 event.preventDefault();
12530 // update location manually
12531 if ($location.absUrl() != $browser.url()) {
12532 $rootScope.$apply();
12533 // hack to work around FF6 bug 684208 when scenario runner clicks on links
12534 $window.angular['ff-684208-preventDefault'] = true;
12541 // rewrite hashbang url <> html5 url
12542 if (trimEmptyHash($location.absUrl()) != trimEmptyHash(initialUrl)) {
12543 $browser.url($location.absUrl(), true);
12546 var initializing = true;
12548 // update $location when $browser url changes
12549 $browser.onUrlChange(function(newUrl, newState) {
12551 if (isUndefined(beginsWith(appBaseNoFile, newUrl))) {
12552 // If we are navigating outside of the app then force a reload
12553 $window.location.href = newUrl;
12557 $rootScope.$evalAsync(function() {
12558 var oldUrl = $location.absUrl();
12559 var oldState = $location.$$state;
12560 var defaultPrevented;
12561 newUrl = trimEmptyHash(newUrl);
12562 $location.$$parse(newUrl);
12563 $location.$$state = newState;
12565 defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
12566 newState, oldState).defaultPrevented;
12568 // if the location was changed by a `$locationChangeStart` handler then stop
12569 // processing this location change
12570 if ($location.absUrl() !== newUrl) return;
12572 if (defaultPrevented) {
12573 $location.$$parse(oldUrl);
12574 $location.$$state = oldState;
12575 setBrowserUrlWithFallback(oldUrl, false, oldState);
12577 initializing = false;
12578 afterLocationChange(oldUrl, oldState);
12581 if (!$rootScope.$$phase) $rootScope.$digest();
12585 $rootScope.$watch(function $locationWatch() {
12586 var oldUrl = trimEmptyHash($browser.url());
12587 var newUrl = trimEmptyHash($location.absUrl());
12588 var oldState = $browser.state();
12589 var currentReplace = $location.$$replace;
12590 var urlOrStateChanged = oldUrl !== newUrl ||
12591 ($location.$$html5 && $sniffer.history && oldState !== $location.$$state);
12593 if (initializing || urlOrStateChanged) {
12594 initializing = false;
12596 $rootScope.$evalAsync(function() {
12597 var newUrl = $location.absUrl();
12598 var defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
12599 $location.$$state, oldState).defaultPrevented;
12601 // if the location was changed by a `$locationChangeStart` handler then stop
12602 // processing this location change
12603 if ($location.absUrl() !== newUrl) return;
12605 if (defaultPrevented) {
12606 $location.$$parse(oldUrl);
12607 $location.$$state = oldState;
12609 if (urlOrStateChanged) {
12610 setBrowserUrlWithFallback(newUrl, currentReplace,
12611 oldState === $location.$$state ? null : $location.$$state);
12613 afterLocationChange(oldUrl, oldState);
12618 $location.$$replace = false;
12620 // we don't need to return anything because $evalAsync will make the digest loop dirty when
12621 // there is a change
12626 function afterLocationChange(oldUrl, oldState) {
12627 $rootScope.$broadcast('$locationChangeSuccess', $location.absUrl(), oldUrl,
12628 $location.$$state, oldState);
12636 * @requires $window
12639 * Simple service for logging. Default implementation safely writes the message
12640 * into the browser's console (if present).
12642 * The main purpose of this service is to simplify debugging and troubleshooting.
12644 * The default is to log `debug` messages. You can use
12645 * {@link ng.$logProvider ng.$logProvider#debugEnabled} to change this.
12648 <example module="logExample">
12649 <file name="script.js">
12650 angular.module('logExample', [])
12651 .controller('LogController', ['$scope', '$log', function($scope, $log) {
12652 $scope.$log = $log;
12653 $scope.message = 'Hello World!';
12656 <file name="index.html">
12657 <div ng-controller="LogController">
12658 <p>Reload this page with open console, enter text and hit the log button...</p>
12660 <input type="text" ng-model="message" /></label>
12661 <button ng-click="$log.log(message)">log</button>
12662 <button ng-click="$log.warn(message)">warn</button>
12663 <button ng-click="$log.info(message)">info</button>
12664 <button ng-click="$log.error(message)">error</button>
12665 <button ng-click="$log.debug(message)">debug</button>
12673 * @name $logProvider
12675 * Use the `$logProvider` to configure how the application logs messages
12677 function $LogProvider() {
12683 * @name $logProvider#debugEnabled
12685 * @param {boolean=} flag enable or disable debug level messages
12686 * @returns {*} current value if used as getter or itself (chaining) if used as setter
12688 this.debugEnabled = function(flag) {
12689 if (isDefined(flag)) {
12697 this.$get = ['$window', function($window) {
12704 * Write a log message
12706 log: consoleLog('log'),
12713 * Write an information message
12715 info: consoleLog('info'),
12722 * Write a warning message
12724 warn: consoleLog('warn'),
12731 * Write an error message
12733 error: consoleLog('error'),
12740 * Write a debug message
12742 debug: (function() {
12743 var fn = consoleLog('debug');
12745 return function() {
12747 fn.apply(self, arguments);
12753 function formatError(arg) {
12754 if (arg instanceof Error) {
12756 arg = (arg.message && arg.stack.indexOf(arg.message) === -1)
12757 ? 'Error: ' + arg.message + '\n' + arg.stack
12759 } else if (arg.sourceURL) {
12760 arg = arg.message + '\n' + arg.sourceURL + ':' + arg.line;
12766 function consoleLog(type) {
12767 var console = $window.console || {},
12768 logFn = console[type] || console.log || noop,
12771 // Note: reading logFn.apply throws an error in IE11 in IE8 document mode.
12772 // The reason behind this is that console.log has type "object" in IE8...
12774 hasApply = !!logFn.apply;
12778 return function() {
12780 forEach(arguments, function(arg) {
12781 args.push(formatError(arg));
12783 return logFn.apply(console, args);
12787 // we are IE which either doesn't have window.console => this is noop and we do nothing,
12788 // or we are IE where console.log doesn't have apply so we log at least first 2 args
12789 return function(arg1, arg2) {
12790 logFn(arg1, arg2 == null ? '' : arg2);
12796 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
12797 * Any commits to this file should be reviewed with security in mind. *
12798 * Changes to this file can potentially create security vulnerabilities. *
12799 * An approval from 2 Core members with history of modifying *
12800 * this file is required. *
12802 * Does the change somehow allow for arbitrary javascript to be executed? *
12803 * Or allows for someone to change the prototype of built-in objects? *
12804 * Or gives undesired access to variables likes document or window? *
12805 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
12807 var $parseMinErr = minErr('$parse');
12809 // Sandboxing Angular Expressions
12810 // ------------------------------
12811 // Angular expressions are generally considered safe because these expressions only have direct
12812 // access to `$scope` and locals. However, one can obtain the ability to execute arbitrary JS code by
12813 // obtaining a reference to native JS functions such as the Function constructor.
12815 // As an example, consider the following Angular expression:
12817 // {}.toString.constructor('alert("evil JS code")')
12819 // This sandboxing technique is not perfect and doesn't aim to be. The goal is to prevent exploits
12820 // against the expression language, but not to prevent exploits that were enabled by exposing
12821 // sensitive JavaScript or browser APIs on Scope. Exposing such objects on a Scope is never a good
12822 // practice and therefore we are not even trying to protect against interaction with an object
12823 // explicitly exposed in this way.
12825 // In general, it is not possible to access a Window object from an angular expression unless a
12826 // window or some DOM object that has a reference to window is published onto a Scope.
12827 // Similarly we prevent invocations of function known to be dangerous, as well as assignments to
12830 // See https://docs.angularjs.org/guide/security
12833 function ensureSafeMemberName(name, fullExpression) {
12834 if (name === "__defineGetter__" || name === "__defineSetter__"
12835 || name === "__lookupGetter__" || name === "__lookupSetter__"
12836 || name === "__proto__") {
12837 throw $parseMinErr('isecfld',
12838 'Attempting to access a disallowed field in Angular expressions! '
12839 + 'Expression: {0}', fullExpression);
12844 function getStringValue(name, fullExpression) {
12845 // From the JavaScript docs:
12846 // Property names must be strings. This means that non-string objects cannot be used
12847 // as keys in an object. Any non-string object, including a number, is typecasted
12848 // into a string via the toString method.
12850 // So, to ensure that we are checking the same `name` that JavaScript would use,
12851 // we cast it to a string, if possible.
12852 // Doing `name + ''` can cause a repl error if the result to `toString` is not a string,
12853 // this is, this will handle objects that misbehave.
12855 if (!isString(name)) {
12856 throw $parseMinErr('iseccst',
12857 'Cannot convert object to primitive value! '
12858 + 'Expression: {0}', fullExpression);
12863 function ensureSafeObject(obj, fullExpression) {
12864 // nifty check if obj is Function that is fast and works across iframes and other contexts
12866 if (obj.constructor === obj) {
12867 throw $parseMinErr('isecfn',
12868 'Referencing Function in Angular expressions is disallowed! Expression: {0}',
12870 } else if (// isWindow(obj)
12871 obj.window === obj) {
12872 throw $parseMinErr('isecwindow',
12873 'Referencing the Window in Angular expressions is disallowed! Expression: {0}',
12875 } else if (// isElement(obj)
12876 obj.children && (obj.nodeName || (obj.prop && obj.attr && obj.find))) {
12877 throw $parseMinErr('isecdom',
12878 'Referencing DOM nodes in Angular expressions is disallowed! Expression: {0}',
12880 } else if (// block Object so that we can't get hold of dangerous Object.* methods
12882 throw $parseMinErr('isecobj',
12883 'Referencing Object in Angular expressions is disallowed! Expression: {0}',
12890 var CALL = Function.prototype.call;
12891 var APPLY = Function.prototype.apply;
12892 var BIND = Function.prototype.bind;
12894 function ensureSafeFunction(obj, fullExpression) {
12896 if (obj.constructor === obj) {
12897 throw $parseMinErr('isecfn',
12898 'Referencing Function in Angular expressions is disallowed! Expression: {0}',
12900 } else if (obj === CALL || obj === APPLY || obj === BIND) {
12901 throw $parseMinErr('isecff',
12902 'Referencing call, apply or bind in Angular expressions is disallowed! Expression: {0}',
12908 function ensureSafeAssignContext(obj, fullExpression) {
12910 if (obj === (0).constructor || obj === (false).constructor || obj === ''.constructor ||
12911 obj === {}.constructor || obj === [].constructor || obj === Function.constructor) {
12912 throw $parseMinErr('isecaf',
12913 'Assigning to a constructor is disallowed! Expression: {0}', fullExpression);
12918 var OPERATORS = createMap();
12919 forEach('+ - * / % === !== == != < > <= >= && || ! = |'.split(' '), function(operator) { OPERATORS[operator] = true; });
12920 var ESCAPE = {"n":"\n", "f":"\f", "r":"\r", "t":"\t", "v":"\v", "'":"'", '"':'"'};
12923 /////////////////////////////////////////
12929 var Lexer = function(options) {
12930 this.options = options;
12933 Lexer.prototype = {
12934 constructor: Lexer,
12936 lex: function(text) {
12941 while (this.index < this.text.length) {
12942 var ch = this.text.charAt(this.index);
12943 if (ch === '"' || ch === "'") {
12944 this.readString(ch);
12945 } else if (this.isNumber(ch) || ch === '.' && this.isNumber(this.peek())) {
12947 } else if (this.isIdent(ch)) {
12949 } else if (this.is(ch, '(){}[].,;:?')) {
12950 this.tokens.push({index: this.index, text: ch});
12952 } else if (this.isWhitespace(ch)) {
12955 var ch2 = ch + this.peek();
12956 var ch3 = ch2 + this.peek(2);
12957 var op1 = OPERATORS[ch];
12958 var op2 = OPERATORS[ch2];
12959 var op3 = OPERATORS[ch3];
12960 if (op1 || op2 || op3) {
12961 var token = op3 ? ch3 : (op2 ? ch2 : ch);
12962 this.tokens.push({index: this.index, text: token, operator: true});
12963 this.index += token.length;
12965 this.throwError('Unexpected next character ', this.index, this.index + 1);
12969 return this.tokens;
12972 is: function(ch, chars) {
12973 return chars.indexOf(ch) !== -1;
12976 peek: function(i) {
12978 return (this.index + num < this.text.length) ? this.text.charAt(this.index + num) : false;
12981 isNumber: function(ch) {
12982 return ('0' <= ch && ch <= '9') && typeof ch === "string";
12985 isWhitespace: function(ch) {
12986 // IE treats non-breaking space as \u00A0
12987 return (ch === ' ' || ch === '\r' || ch === '\t' ||
12988 ch === '\n' || ch === '\v' || ch === '\u00A0');
12991 isIdent: function(ch) {
12992 return ('a' <= ch && ch <= 'z' ||
12993 'A' <= ch && ch <= 'Z' ||
12994 '_' === ch || ch === '$');
12997 isExpOperator: function(ch) {
12998 return (ch === '-' || ch === '+' || this.isNumber(ch));
13001 throwError: function(error, start, end) {
13002 end = end || this.index;
13003 var colStr = (isDefined(start)
13004 ? 's ' + start + '-' + this.index + ' [' + this.text.substring(start, end) + ']'
13006 throw $parseMinErr('lexerr', 'Lexer Error: {0} at column{1} in expression [{2}].',
13007 error, colStr, this.text);
13010 readNumber: function() {
13012 var start = this.index;
13013 while (this.index < this.text.length) {
13014 var ch = lowercase(this.text.charAt(this.index));
13015 if (ch == '.' || this.isNumber(ch)) {
13018 var peekCh = this.peek();
13019 if (ch == 'e' && this.isExpOperator(peekCh)) {
13021 } else if (this.isExpOperator(ch) &&
13022 peekCh && this.isNumber(peekCh) &&
13023 number.charAt(number.length - 1) == 'e') {
13025 } else if (this.isExpOperator(ch) &&
13026 (!peekCh || !this.isNumber(peekCh)) &&
13027 number.charAt(number.length - 1) == 'e') {
13028 this.throwError('Invalid exponent');
13039 value: Number(number)
13043 readIdent: function() {
13044 var start = this.index;
13045 while (this.index < this.text.length) {
13046 var ch = this.text.charAt(this.index);
13047 if (!(this.isIdent(ch) || this.isNumber(ch))) {
13054 text: this.text.slice(start, this.index),
13059 readString: function(quote) {
13060 var start = this.index;
13063 var rawString = quote;
13064 var escape = false;
13065 while (this.index < this.text.length) {
13066 var ch = this.text.charAt(this.index);
13070 var hex = this.text.substring(this.index + 1, this.index + 5);
13071 if (!hex.match(/[\da-f]{4}/i)) {
13072 this.throwError('Invalid unicode escape [\\u' + hex + ']');
13075 string += String.fromCharCode(parseInt(hex, 16));
13077 var rep = ESCAPE[ch];
13078 string = string + (rep || ch);
13081 } else if (ch === '\\') {
13083 } else if (ch === quote) {
13097 this.throwError('Unterminated quote', start);
13101 var AST = function(lexer, options) {
13102 this.lexer = lexer;
13103 this.options = options;
13106 AST.Program = 'Program';
13107 AST.ExpressionStatement = 'ExpressionStatement';
13108 AST.AssignmentExpression = 'AssignmentExpression';
13109 AST.ConditionalExpression = 'ConditionalExpression';
13110 AST.LogicalExpression = 'LogicalExpression';
13111 AST.BinaryExpression = 'BinaryExpression';
13112 AST.UnaryExpression = 'UnaryExpression';
13113 AST.CallExpression = 'CallExpression';
13114 AST.MemberExpression = 'MemberExpression';
13115 AST.Identifier = 'Identifier';
13116 AST.Literal = 'Literal';
13117 AST.ArrayExpression = 'ArrayExpression';
13118 AST.Property = 'Property';
13119 AST.ObjectExpression = 'ObjectExpression';
13120 AST.ThisExpression = 'ThisExpression';
13122 // Internal use only
13123 AST.NGValueParameter = 'NGValueParameter';
13126 ast: function(text) {
13128 this.tokens = this.lexer.lex(text);
13130 var value = this.program();
13132 if (this.tokens.length !== 0) {
13133 this.throwError('is an unexpected token', this.tokens[0]);
13139 program: function() {
13142 if (this.tokens.length > 0 && !this.peek('}', ')', ';', ']'))
13143 body.push(this.expressionStatement());
13144 if (!this.expect(';')) {
13145 return { type: AST.Program, body: body};
13150 expressionStatement: function() {
13151 return { type: AST.ExpressionStatement, expression: this.filterChain() };
13154 filterChain: function() {
13155 var left = this.expression();
13157 while ((token = this.expect('|'))) {
13158 left = this.filter(left);
13163 expression: function() {
13164 return this.assignment();
13167 assignment: function() {
13168 var result = this.ternary();
13169 if (this.expect('=')) {
13170 result = { type: AST.AssignmentExpression, left: result, right: this.assignment(), operator: '='};
13175 ternary: function() {
13176 var test = this.logicalOR();
13179 if (this.expect('?')) {
13180 alternate = this.expression();
13181 if (this.consume(':')) {
13182 consequent = this.expression();
13183 return { type: AST.ConditionalExpression, test: test, alternate: alternate, consequent: consequent};
13189 logicalOR: function() {
13190 var left = this.logicalAND();
13191 while (this.expect('||')) {
13192 left = { type: AST.LogicalExpression, operator: '||', left: left, right: this.logicalAND() };
13197 logicalAND: function() {
13198 var left = this.equality();
13199 while (this.expect('&&')) {
13200 left = { type: AST.LogicalExpression, operator: '&&', left: left, right: this.equality()};
13205 equality: function() {
13206 var left = this.relational();
13208 while ((token = this.expect('==','!=','===','!=='))) {
13209 left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.relational() };
13214 relational: function() {
13215 var left = this.additive();
13217 while ((token = this.expect('<', '>', '<=', '>='))) {
13218 left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.additive() };
13223 additive: function() {
13224 var left = this.multiplicative();
13226 while ((token = this.expect('+','-'))) {
13227 left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.multiplicative() };
13232 multiplicative: function() {
13233 var left = this.unary();
13235 while ((token = this.expect('*','/','%'))) {
13236 left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.unary() };
13241 unary: function() {
13243 if ((token = this.expect('+', '-', '!'))) {
13244 return { type: AST.UnaryExpression, operator: token.text, prefix: true, argument: this.unary() };
13246 return this.primary();
13250 primary: function() {
13252 if (this.expect('(')) {
13253 primary = this.filterChain();
13255 } else if (this.expect('[')) {
13256 primary = this.arrayDeclaration();
13257 } else if (this.expect('{')) {
13258 primary = this.object();
13259 } else if (this.constants.hasOwnProperty(this.peek().text)) {
13260 primary = copy(this.constants[this.consume().text]);
13261 } else if (this.peek().identifier) {
13262 primary = this.identifier();
13263 } else if (this.peek().constant) {
13264 primary = this.constant();
13266 this.throwError('not a primary expression', this.peek());
13270 while ((next = this.expect('(', '[', '.'))) {
13271 if (next.text === '(') {
13272 primary = {type: AST.CallExpression, callee: primary, arguments: this.parseArguments() };
13274 } else if (next.text === '[') {
13275 primary = { type: AST.MemberExpression, object: primary, property: this.expression(), computed: true };
13277 } else if (next.text === '.') {
13278 primary = { type: AST.MemberExpression, object: primary, property: this.identifier(), computed: false };
13280 this.throwError('IMPOSSIBLE');
13286 filter: function(baseExpression) {
13287 var args = [baseExpression];
13288 var result = {type: AST.CallExpression, callee: this.identifier(), arguments: args, filter: true};
13290 while (this.expect(':')) {
13291 args.push(this.expression());
13297 parseArguments: function() {
13299 if (this.peekToken().text !== ')') {
13301 args.push(this.expression());
13302 } while (this.expect(','));
13307 identifier: function() {
13308 var token = this.consume();
13309 if (!token.identifier) {
13310 this.throwError('is not a valid identifier', token);
13312 return { type: AST.Identifier, name: token.text };
13315 constant: function() {
13316 // TODO check that it is a constant
13317 return { type: AST.Literal, value: this.consume().value };
13320 arrayDeclaration: function() {
13322 if (this.peekToken().text !== ']') {
13324 if (this.peek(']')) {
13325 // Support trailing commas per ES5.1.
13328 elements.push(this.expression());
13329 } while (this.expect(','));
13333 return { type: AST.ArrayExpression, elements: elements };
13336 object: function() {
13337 var properties = [], property;
13338 if (this.peekToken().text !== '}') {
13340 if (this.peek('}')) {
13341 // Support trailing commas per ES5.1.
13344 property = {type: AST.Property, kind: 'init'};
13345 if (this.peek().constant) {
13346 property.key = this.constant();
13347 } else if (this.peek().identifier) {
13348 property.key = this.identifier();
13350 this.throwError("invalid key", this.peek());
13353 property.value = this.expression();
13354 properties.push(property);
13355 } while (this.expect(','));
13359 return {type: AST.ObjectExpression, properties: properties };
13362 throwError: function(msg, token) {
13363 throw $parseMinErr('syntax',
13364 'Syntax Error: Token \'{0}\' {1} at column {2} of the expression [{3}] starting at [{4}].',
13365 token.text, msg, (token.index + 1), this.text, this.text.substring(token.index));
13368 consume: function(e1) {
13369 if (this.tokens.length === 0) {
13370 throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text);
13373 var token = this.expect(e1);
13375 this.throwError('is unexpected, expecting [' + e1 + ']', this.peek());
13380 peekToken: function() {
13381 if (this.tokens.length === 0) {
13382 throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text);
13384 return this.tokens[0];
13387 peek: function(e1, e2, e3, e4) {
13388 return this.peekAhead(0, e1, e2, e3, e4);
13391 peekAhead: function(i, e1, e2, e3, e4) {
13392 if (this.tokens.length > i) {
13393 var token = this.tokens[i];
13394 var t = token.text;
13395 if (t === e1 || t === e2 || t === e3 || t === e4 ||
13396 (!e1 && !e2 && !e3 && !e4)) {
13403 expect: function(e1, e2, e3, e4) {
13404 var token = this.peek(e1, e2, e3, e4);
13406 this.tokens.shift();
13413 /* `undefined` is not a constant, it is an identifier,
13414 * but using it as an identifier is not supported
13417 'true': { type: AST.Literal, value: true },
13418 'false': { type: AST.Literal, value: false },
13419 'null': { type: AST.Literal, value: null },
13420 'undefined': {type: AST.Literal, value: undefined },
13421 'this': {type: AST.ThisExpression }
13425 function ifDefined(v, d) {
13426 return typeof v !== 'undefined' ? v : d;
13429 function plusFn(l, r) {
13430 if (typeof l === 'undefined') return r;
13431 if (typeof r === 'undefined') return l;
13435 function isStateless($filter, filterName) {
13436 var fn = $filter(filterName);
13437 return !fn.$stateful;
13440 function findConstantAndWatchExpressions(ast, $filter) {
13443 switch (ast.type) {
13445 allConstants = true;
13446 forEach(ast.body, function(expr) {
13447 findConstantAndWatchExpressions(expr.expression, $filter);
13448 allConstants = allConstants && expr.expression.constant;
13450 ast.constant = allConstants;
13453 ast.constant = true;
13456 case AST.UnaryExpression:
13457 findConstantAndWatchExpressions(ast.argument, $filter);
13458 ast.constant = ast.argument.constant;
13459 ast.toWatch = ast.argument.toWatch;
13461 case AST.BinaryExpression:
13462 findConstantAndWatchExpressions(ast.left, $filter);
13463 findConstantAndWatchExpressions(ast.right, $filter);
13464 ast.constant = ast.left.constant && ast.right.constant;
13465 ast.toWatch = ast.left.toWatch.concat(ast.right.toWatch);
13467 case AST.LogicalExpression:
13468 findConstantAndWatchExpressions(ast.left, $filter);
13469 findConstantAndWatchExpressions(ast.right, $filter);
13470 ast.constant = ast.left.constant && ast.right.constant;
13471 ast.toWatch = ast.constant ? [] : [ast];
13473 case AST.ConditionalExpression:
13474 findConstantAndWatchExpressions(ast.test, $filter);
13475 findConstantAndWatchExpressions(ast.alternate, $filter);
13476 findConstantAndWatchExpressions(ast.consequent, $filter);
13477 ast.constant = ast.test.constant && ast.alternate.constant && ast.consequent.constant;
13478 ast.toWatch = ast.constant ? [] : [ast];
13480 case AST.Identifier:
13481 ast.constant = false;
13482 ast.toWatch = [ast];
13484 case AST.MemberExpression:
13485 findConstantAndWatchExpressions(ast.object, $filter);
13486 if (ast.computed) {
13487 findConstantAndWatchExpressions(ast.property, $filter);
13489 ast.constant = ast.object.constant && (!ast.computed || ast.property.constant);
13490 ast.toWatch = [ast];
13492 case AST.CallExpression:
13493 allConstants = ast.filter ? isStateless($filter, ast.callee.name) : false;
13495 forEach(ast.arguments, function(expr) {
13496 findConstantAndWatchExpressions(expr, $filter);
13497 allConstants = allConstants && expr.constant;
13498 if (!expr.constant) {
13499 argsToWatch.push.apply(argsToWatch, expr.toWatch);
13502 ast.constant = allConstants;
13503 ast.toWatch = ast.filter && isStateless($filter, ast.callee.name) ? argsToWatch : [ast];
13505 case AST.AssignmentExpression:
13506 findConstantAndWatchExpressions(ast.left, $filter);
13507 findConstantAndWatchExpressions(ast.right, $filter);
13508 ast.constant = ast.left.constant && ast.right.constant;
13509 ast.toWatch = [ast];
13511 case AST.ArrayExpression:
13512 allConstants = true;
13514 forEach(ast.elements, function(expr) {
13515 findConstantAndWatchExpressions(expr, $filter);
13516 allConstants = allConstants && expr.constant;
13517 if (!expr.constant) {
13518 argsToWatch.push.apply(argsToWatch, expr.toWatch);
13521 ast.constant = allConstants;
13522 ast.toWatch = argsToWatch;
13524 case AST.ObjectExpression:
13525 allConstants = true;
13527 forEach(ast.properties, function(property) {
13528 findConstantAndWatchExpressions(property.value, $filter);
13529 allConstants = allConstants && property.value.constant;
13530 if (!property.value.constant) {
13531 argsToWatch.push.apply(argsToWatch, property.value.toWatch);
13534 ast.constant = allConstants;
13535 ast.toWatch = argsToWatch;
13537 case AST.ThisExpression:
13538 ast.constant = false;
13544 function getInputs(body) {
13545 if (body.length != 1) return;
13546 var lastExpression = body[0].expression;
13547 var candidate = lastExpression.toWatch;
13548 if (candidate.length !== 1) return candidate;
13549 return candidate[0] !== lastExpression ? candidate : undefined;
13552 function isAssignable(ast) {
13553 return ast.type === AST.Identifier || ast.type === AST.MemberExpression;
13556 function assignableAST(ast) {
13557 if (ast.body.length === 1 && isAssignable(ast.body[0].expression)) {
13558 return {type: AST.AssignmentExpression, left: ast.body[0].expression, right: {type: AST.NGValueParameter}, operator: '='};
13562 function isLiteral(ast) {
13563 return ast.body.length === 0 ||
13564 ast.body.length === 1 && (
13565 ast.body[0].expression.type === AST.Literal ||
13566 ast.body[0].expression.type === AST.ArrayExpression ||
13567 ast.body[0].expression.type === AST.ObjectExpression);
13570 function isConstant(ast) {
13571 return ast.constant;
13574 function ASTCompiler(astBuilder, $filter) {
13575 this.astBuilder = astBuilder;
13576 this.$filter = $filter;
13579 ASTCompiler.prototype = {
13580 compile: function(expression, expensiveChecks) {
13582 var ast = this.astBuilder.ast(expression);
13586 expensiveChecks: expensiveChecks,
13587 fn: {vars: [], body: [], own: {}},
13588 assign: {vars: [], body: [], own: {}},
13591 findConstantAndWatchExpressions(ast, self.$filter);
13594 this.stage = 'assign';
13595 if ((assignable = assignableAST(ast))) {
13596 this.state.computing = 'assign';
13597 var result = this.nextId();
13598 this.recurse(assignable, result);
13599 this.return_(result);
13600 extra = 'fn.assign=' + this.generateFunction('assign', 's,v,l');
13602 var toWatch = getInputs(ast.body);
13603 self.stage = 'inputs';
13604 forEach(toWatch, function(watch, key) {
13605 var fnKey = 'fn' + key;
13606 self.state[fnKey] = {vars: [], body: [], own: {}};
13607 self.state.computing = fnKey;
13608 var intoId = self.nextId();
13609 self.recurse(watch, intoId);
13610 self.return_(intoId);
13611 self.state.inputs.push(fnKey);
13612 watch.watchId = key;
13614 this.state.computing = 'fn';
13615 this.stage = 'main';
13618 // The build and minification steps remove the string "use strict" from the code, but this is done using a regex.
13619 // This is a workaround for this until we do a better job at only removing the prefix only when we should.
13620 '"' + this.USE + ' ' + this.STRICT + '";\n' +
13621 this.filterPrefix() +
13622 'var fn=' + this.generateFunction('fn', 's,l,a,i') +
13628 var fn = (new Function('$filter',
13629 'ensureSafeMemberName',
13630 'ensureSafeObject',
13631 'ensureSafeFunction',
13633 'ensureSafeAssignContext',
13639 ensureSafeMemberName,
13641 ensureSafeFunction,
13643 ensureSafeAssignContext,
13648 this.state = this.stage = undefined;
13649 fn.literal = isLiteral(ast);
13650 fn.constant = isConstant(ast);
13658 watchFns: function() {
13660 var fns = this.state.inputs;
13662 forEach(fns, function(name) {
13663 result.push('var ' + name + '=' + self.generateFunction(name, 's'));
13666 result.push('fn.inputs=[' + fns.join(',') + '];');
13668 return result.join('');
13671 generateFunction: function(name, params) {
13672 return 'function(' + params + '){' +
13673 this.varsPrefix(name) +
13678 filterPrefix: function() {
13681 forEach(this.state.filters, function(id, filter) {
13682 parts.push(id + '=$filter(' + self.escape(filter) + ')');
13684 if (parts.length) return 'var ' + parts.join(',') + ';';
13688 varsPrefix: function(section) {
13689 return this.state[section].vars.length ? 'var ' + this.state[section].vars.join(',') + ';' : '';
13692 body: function(section) {
13693 return this.state[section].body.join('');
13696 recurse: function(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck) {
13697 var left, right, self = this, args, expression;
13698 recursionFn = recursionFn || noop;
13699 if (!skipWatchIdCheck && isDefined(ast.watchId)) {
13700 intoId = intoId || this.nextId();
13702 this.lazyAssign(intoId, this.computedMember('i', ast.watchId)),
13703 this.lazyRecurse(ast, intoId, nameId, recursionFn, create, true)
13707 switch (ast.type) {
13709 forEach(ast.body, function(expression, pos) {
13710 self.recurse(expression.expression, undefined, undefined, function(expr) { right = expr; });
13711 if (pos !== ast.body.length - 1) {
13712 self.current().body.push(right, ';');
13714 self.return_(right);
13719 expression = this.escape(ast.value);
13720 this.assign(intoId, expression);
13721 recursionFn(expression);
13723 case AST.UnaryExpression:
13724 this.recurse(ast.argument, undefined, undefined, function(expr) { right = expr; });
13725 expression = ast.operator + '(' + this.ifDefined(right, 0) + ')';
13726 this.assign(intoId, expression);
13727 recursionFn(expression);
13729 case AST.BinaryExpression:
13730 this.recurse(ast.left, undefined, undefined, function(expr) { left = expr; });
13731 this.recurse(ast.right, undefined, undefined, function(expr) { right = expr; });
13732 if (ast.operator === '+') {
13733 expression = this.plus(left, right);
13734 } else if (ast.operator === '-') {
13735 expression = this.ifDefined(left, 0) + ast.operator + this.ifDefined(right, 0);
13737 expression = '(' + left + ')' + ast.operator + '(' + right + ')';
13739 this.assign(intoId, expression);
13740 recursionFn(expression);
13742 case AST.LogicalExpression:
13743 intoId = intoId || this.nextId();
13744 self.recurse(ast.left, intoId);
13745 self.if_(ast.operator === '&&' ? intoId : self.not(intoId), self.lazyRecurse(ast.right, intoId));
13746 recursionFn(intoId);
13748 case AST.ConditionalExpression:
13749 intoId = intoId || this.nextId();
13750 self.recurse(ast.test, intoId);
13751 self.if_(intoId, self.lazyRecurse(ast.alternate, intoId), self.lazyRecurse(ast.consequent, intoId));
13752 recursionFn(intoId);
13754 case AST.Identifier:
13755 intoId = intoId || this.nextId();
13757 nameId.context = self.stage === 'inputs' ? 's' : this.assign(this.nextId(), this.getHasOwnProperty('l', ast.name) + '?l:s');
13758 nameId.computed = false;
13759 nameId.name = ast.name;
13761 ensureSafeMemberName(ast.name);
13762 self.if_(self.stage === 'inputs' || self.not(self.getHasOwnProperty('l', ast.name)),
13764 self.if_(self.stage === 'inputs' || 's', function() {
13765 if (create && create !== 1) {
13767 self.not(self.nonComputedMember('s', ast.name)),
13768 self.lazyAssign(self.nonComputedMember('s', ast.name), '{}'));
13770 self.assign(intoId, self.nonComputedMember('s', ast.name));
13772 }, intoId && self.lazyAssign(intoId, self.nonComputedMember('l', ast.name))
13774 if (self.state.expensiveChecks || isPossiblyDangerousMemberName(ast.name)) {
13775 self.addEnsureSafeObject(intoId);
13777 recursionFn(intoId);
13779 case AST.MemberExpression:
13780 left = nameId && (nameId.context = this.nextId()) || this.nextId();
13781 intoId = intoId || this.nextId();
13782 self.recurse(ast.object, left, undefined, function() {
13783 self.if_(self.notNull(left), function() {
13784 if (create && create !== 1) {
13785 self.addEnsureSafeAssignContext(left);
13787 if (ast.computed) {
13788 right = self.nextId();
13789 self.recurse(ast.property, right);
13790 self.getStringValue(right);
13791 self.addEnsureSafeMemberName(right);
13792 if (create && create !== 1) {
13793 self.if_(self.not(self.computedMember(left, right)), self.lazyAssign(self.computedMember(left, right), '{}'));
13795 expression = self.ensureSafeObject(self.computedMember(left, right));
13796 self.assign(intoId, expression);
13798 nameId.computed = true;
13799 nameId.name = right;
13802 ensureSafeMemberName(ast.property.name);
13803 if (create && create !== 1) {
13804 self.if_(self.not(self.nonComputedMember(left, ast.property.name)), self.lazyAssign(self.nonComputedMember(left, ast.property.name), '{}'));
13806 expression = self.nonComputedMember(left, ast.property.name);
13807 if (self.state.expensiveChecks || isPossiblyDangerousMemberName(ast.property.name)) {
13808 expression = self.ensureSafeObject(expression);
13810 self.assign(intoId, expression);
13812 nameId.computed = false;
13813 nameId.name = ast.property.name;
13817 self.assign(intoId, 'undefined');
13819 recursionFn(intoId);
13822 case AST.CallExpression:
13823 intoId = intoId || this.nextId();
13825 right = self.filter(ast.callee.name);
13827 forEach(ast.arguments, function(expr) {
13828 var argument = self.nextId();
13829 self.recurse(expr, argument);
13830 args.push(argument);
13832 expression = right + '(' + args.join(',') + ')';
13833 self.assign(intoId, expression);
13834 recursionFn(intoId);
13836 right = self.nextId();
13839 self.recurse(ast.callee, right, left, function() {
13840 self.if_(self.notNull(right), function() {
13841 self.addEnsureSafeFunction(right);
13842 forEach(ast.arguments, function(expr) {
13843 self.recurse(expr, self.nextId(), undefined, function(argument) {
13844 args.push(self.ensureSafeObject(argument));
13848 if (!self.state.expensiveChecks) {
13849 self.addEnsureSafeObject(left.context);
13851 expression = self.member(left.context, left.name, left.computed) + '(' + args.join(',') + ')';
13853 expression = right + '(' + args.join(',') + ')';
13855 expression = self.ensureSafeObject(expression);
13856 self.assign(intoId, expression);
13858 self.assign(intoId, 'undefined');
13860 recursionFn(intoId);
13864 case AST.AssignmentExpression:
13865 right = this.nextId();
13867 if (!isAssignable(ast.left)) {
13868 throw $parseMinErr('lval', 'Trying to assign a value to a non l-value');
13870 this.recurse(ast.left, undefined, left, function() {
13871 self.if_(self.notNull(left.context), function() {
13872 self.recurse(ast.right, right);
13873 self.addEnsureSafeObject(self.member(left.context, left.name, left.computed));
13874 self.addEnsureSafeAssignContext(left.context);
13875 expression = self.member(left.context, left.name, left.computed) + ast.operator + right;
13876 self.assign(intoId, expression);
13877 recursionFn(intoId || expression);
13881 case AST.ArrayExpression:
13883 forEach(ast.elements, function(expr) {
13884 self.recurse(expr, self.nextId(), undefined, function(argument) {
13885 args.push(argument);
13888 expression = '[' + args.join(',') + ']';
13889 this.assign(intoId, expression);
13890 recursionFn(expression);
13892 case AST.ObjectExpression:
13894 forEach(ast.properties, function(property) {
13895 self.recurse(property.value, self.nextId(), undefined, function(expr) {
13896 args.push(self.escape(
13897 property.key.type === AST.Identifier ? property.key.name :
13898 ('' + property.key.value)) +
13902 expression = '{' + args.join(',') + '}';
13903 this.assign(intoId, expression);
13904 recursionFn(expression);
13906 case AST.ThisExpression:
13907 this.assign(intoId, 's');
13910 case AST.NGValueParameter:
13911 this.assign(intoId, 'v');
13917 getHasOwnProperty: function(element, property) {
13918 var key = element + '.' + property;
13919 var own = this.current().own;
13920 if (!own.hasOwnProperty(key)) {
13921 own[key] = this.nextId(false, element + '&&(' + this.escape(property) + ' in ' + element + ')');
13926 assign: function(id, value) {
13928 this.current().body.push(id, '=', value, ';');
13932 filter: function(filterName) {
13933 if (!this.state.filters.hasOwnProperty(filterName)) {
13934 this.state.filters[filterName] = this.nextId(true);
13936 return this.state.filters[filterName];
13939 ifDefined: function(id, defaultValue) {
13940 return 'ifDefined(' + id + ',' + this.escape(defaultValue) + ')';
13943 plus: function(left, right) {
13944 return 'plus(' + left + ',' + right + ')';
13947 return_: function(id) {
13948 this.current().body.push('return ', id, ';');
13951 if_: function(test, alternate, consequent) {
13952 if (test === true) {
13955 var body = this.current().body;
13956 body.push('if(', test, '){');
13960 body.push('else{');
13967 not: function(expression) {
13968 return '!(' + expression + ')';
13971 notNull: function(expression) {
13972 return expression + '!=null';
13975 nonComputedMember: function(left, right) {
13976 return left + '.' + right;
13979 computedMember: function(left, right) {
13980 return left + '[' + right + ']';
13983 member: function(left, right, computed) {
13984 if (computed) return this.computedMember(left, right);
13985 return this.nonComputedMember(left, right);
13988 addEnsureSafeObject: function(item) {
13989 this.current().body.push(this.ensureSafeObject(item), ';');
13992 addEnsureSafeMemberName: function(item) {
13993 this.current().body.push(this.ensureSafeMemberName(item), ';');
13996 addEnsureSafeFunction: function(item) {
13997 this.current().body.push(this.ensureSafeFunction(item), ';');
14000 addEnsureSafeAssignContext: function(item) {
14001 this.current().body.push(this.ensureSafeAssignContext(item), ';');
14004 ensureSafeObject: function(item) {
14005 return 'ensureSafeObject(' + item + ',text)';
14008 ensureSafeMemberName: function(item) {
14009 return 'ensureSafeMemberName(' + item + ',text)';
14012 ensureSafeFunction: function(item) {
14013 return 'ensureSafeFunction(' + item + ',text)';
14016 getStringValue: function(item) {
14017 this.assign(item, 'getStringValue(' + item + ',text)');
14020 ensureSafeAssignContext: function(item) {
14021 return 'ensureSafeAssignContext(' + item + ',text)';
14024 lazyRecurse: function(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck) {
14026 return function() {
14027 self.recurse(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck);
14031 lazyAssign: function(id, value) {
14033 return function() {
14034 self.assign(id, value);
14038 stringEscapeRegex: /[^ a-zA-Z0-9]/g,
14040 stringEscapeFn: function(c) {
14041 return '\\u' + ('0000' + c.charCodeAt(0).toString(16)).slice(-4);
14044 escape: function(value) {
14045 if (isString(value)) return "'" + value.replace(this.stringEscapeRegex, this.stringEscapeFn) + "'";
14046 if (isNumber(value)) return value.toString();
14047 if (value === true) return 'true';
14048 if (value === false) return 'false';
14049 if (value === null) return 'null';
14050 if (typeof value === 'undefined') return 'undefined';
14052 throw $parseMinErr('esc', 'IMPOSSIBLE');
14055 nextId: function(skip, init) {
14056 var id = 'v' + (this.state.nextId++);
14058 this.current().vars.push(id + (init ? '=' + init : ''));
14063 current: function() {
14064 return this.state[this.state.computing];
14069 function ASTInterpreter(astBuilder, $filter) {
14070 this.astBuilder = astBuilder;
14071 this.$filter = $filter;
14074 ASTInterpreter.prototype = {
14075 compile: function(expression, expensiveChecks) {
14077 var ast = this.astBuilder.ast(expression);
14078 this.expression = expression;
14079 this.expensiveChecks = expensiveChecks;
14080 findConstantAndWatchExpressions(ast, self.$filter);
14083 if ((assignable = assignableAST(ast))) {
14084 assign = this.recurse(assignable);
14086 var toWatch = getInputs(ast.body);
14090 forEach(toWatch, function(watch, key) {
14091 var input = self.recurse(watch);
14092 watch.input = input;
14093 inputs.push(input);
14094 watch.watchId = key;
14097 var expressions = [];
14098 forEach(ast.body, function(expression) {
14099 expressions.push(self.recurse(expression.expression));
14101 var fn = ast.body.length === 0 ? function() {} :
14102 ast.body.length === 1 ? expressions[0] :
14103 function(scope, locals) {
14105 forEach(expressions, function(exp) {
14106 lastValue = exp(scope, locals);
14111 fn.assign = function(scope, value, locals) {
14112 return assign(scope, locals, value);
14116 fn.inputs = inputs;
14118 fn.literal = isLiteral(ast);
14119 fn.constant = isConstant(ast);
14123 recurse: function(ast, context, create) {
14124 var left, right, self = this, args, expression;
14126 return this.inputs(ast.input, ast.watchId);
14128 switch (ast.type) {
14130 return this.value(ast.value, context);
14131 case AST.UnaryExpression:
14132 right = this.recurse(ast.argument);
14133 return this['unary' + ast.operator](right, context);
14134 case AST.BinaryExpression:
14135 left = this.recurse(ast.left);
14136 right = this.recurse(ast.right);
14137 return this['binary' + ast.operator](left, right, context);
14138 case AST.LogicalExpression:
14139 left = this.recurse(ast.left);
14140 right = this.recurse(ast.right);
14141 return this['binary' + ast.operator](left, right, context);
14142 case AST.ConditionalExpression:
14143 return this['ternary?:'](
14144 this.recurse(ast.test),
14145 this.recurse(ast.alternate),
14146 this.recurse(ast.consequent),
14149 case AST.Identifier:
14150 ensureSafeMemberName(ast.name, self.expression);
14151 return self.identifier(ast.name,
14152 self.expensiveChecks || isPossiblyDangerousMemberName(ast.name),
14153 context, create, self.expression);
14154 case AST.MemberExpression:
14155 left = this.recurse(ast.object, false, !!create);
14156 if (!ast.computed) {
14157 ensureSafeMemberName(ast.property.name, self.expression);
14158 right = ast.property.name;
14160 if (ast.computed) right = this.recurse(ast.property);
14161 return ast.computed ?
14162 this.computedMember(left, right, context, create, self.expression) :
14163 this.nonComputedMember(left, right, self.expensiveChecks, context, create, self.expression);
14164 case AST.CallExpression:
14166 forEach(ast.arguments, function(expr) {
14167 args.push(self.recurse(expr));
14169 if (ast.filter) right = this.$filter(ast.callee.name);
14170 if (!ast.filter) right = this.recurse(ast.callee, true);
14171 return ast.filter ?
14172 function(scope, locals, assign, inputs) {
14174 for (var i = 0; i < args.length; ++i) {
14175 values.push(args[i](scope, locals, assign, inputs));
14177 var value = right.apply(undefined, values, inputs);
14178 return context ? {context: undefined, name: undefined, value: value} : value;
14180 function(scope, locals, assign, inputs) {
14181 var rhs = right(scope, locals, assign, inputs);
14183 if (rhs.value != null) {
14184 ensureSafeObject(rhs.context, self.expression);
14185 ensureSafeFunction(rhs.value, self.expression);
14187 for (var i = 0; i < args.length; ++i) {
14188 values.push(ensureSafeObject(args[i](scope, locals, assign, inputs), self.expression));
14190 value = ensureSafeObject(rhs.value.apply(rhs.context, values), self.expression);
14192 return context ? {value: value} : value;
14194 case AST.AssignmentExpression:
14195 left = this.recurse(ast.left, true, 1);
14196 right = this.recurse(ast.right);
14197 return function(scope, locals, assign, inputs) {
14198 var lhs = left(scope, locals, assign, inputs);
14199 var rhs = right(scope, locals, assign, inputs);
14200 ensureSafeObject(lhs.value, self.expression);
14201 ensureSafeAssignContext(lhs.context);
14202 lhs.context[lhs.name] = rhs;
14203 return context ? {value: rhs} : rhs;
14205 case AST.ArrayExpression:
14207 forEach(ast.elements, function(expr) {
14208 args.push(self.recurse(expr));
14210 return function(scope, locals, assign, inputs) {
14212 for (var i = 0; i < args.length; ++i) {
14213 value.push(args[i](scope, locals, assign, inputs));
14215 return context ? {value: value} : value;
14217 case AST.ObjectExpression:
14219 forEach(ast.properties, function(property) {
14220 args.push({key: property.key.type === AST.Identifier ?
14221 property.key.name :
14222 ('' + property.key.value),
14223 value: self.recurse(property.value)
14226 return function(scope, locals, assign, inputs) {
14228 for (var i = 0; i < args.length; ++i) {
14229 value[args[i].key] = args[i].value(scope, locals, assign, inputs);
14231 return context ? {value: value} : value;
14233 case AST.ThisExpression:
14234 return function(scope) {
14235 return context ? {value: scope} : scope;
14237 case AST.NGValueParameter:
14238 return function(scope, locals, assign, inputs) {
14239 return context ? {value: assign} : assign;
14244 'unary+': function(argument, context) {
14245 return function(scope, locals, assign, inputs) {
14246 var arg = argument(scope, locals, assign, inputs);
14247 if (isDefined(arg)) {
14252 return context ? {value: arg} : arg;
14255 'unary-': function(argument, context) {
14256 return function(scope, locals, assign, inputs) {
14257 var arg = argument(scope, locals, assign, inputs);
14258 if (isDefined(arg)) {
14263 return context ? {value: arg} : arg;
14266 'unary!': function(argument, context) {
14267 return function(scope, locals, assign, inputs) {
14268 var arg = !argument(scope, locals, assign, inputs);
14269 return context ? {value: arg} : arg;
14272 'binary+': function(left, right, context) {
14273 return function(scope, locals, assign, inputs) {
14274 var lhs = left(scope, locals, assign, inputs);
14275 var rhs = right(scope, locals, assign, inputs);
14276 var arg = plusFn(lhs, rhs);
14277 return context ? {value: arg} : arg;
14280 'binary-': function(left, right, context) {
14281 return function(scope, locals, assign, inputs) {
14282 var lhs = left(scope, locals, assign, inputs);
14283 var rhs = right(scope, locals, assign, inputs);
14284 var arg = (isDefined(lhs) ? lhs : 0) - (isDefined(rhs) ? rhs : 0);
14285 return context ? {value: arg} : arg;
14288 'binary*': function(left, right, context) {
14289 return function(scope, locals, assign, inputs) {
14290 var arg = left(scope, locals, assign, inputs) * right(scope, locals, assign, inputs);
14291 return context ? {value: arg} : arg;
14294 'binary/': function(left, right, context) {
14295 return function(scope, locals, assign, inputs) {
14296 var arg = left(scope, locals, assign, inputs) / right(scope, locals, assign, inputs);
14297 return context ? {value: arg} : arg;
14300 'binary%': function(left, right, context) {
14301 return function(scope, locals, assign, inputs) {
14302 var arg = left(scope, locals, assign, inputs) % right(scope, locals, assign, inputs);
14303 return context ? {value: arg} : arg;
14306 'binary===': function(left, right, context) {
14307 return function(scope, locals, assign, inputs) {
14308 var arg = left(scope, locals, assign, inputs) === right(scope, locals, assign, inputs);
14309 return context ? {value: arg} : arg;
14312 'binary!==': function(left, right, context) {
14313 return function(scope, locals, assign, inputs) {
14314 var arg = left(scope, locals, assign, inputs) !== right(scope, locals, assign, inputs);
14315 return context ? {value: arg} : arg;
14318 'binary==': function(left, right, context) {
14319 return function(scope, locals, assign, inputs) {
14320 var arg = left(scope, locals, assign, inputs) == right(scope, locals, assign, inputs);
14321 return context ? {value: arg} : arg;
14324 'binary!=': function(left, right, context) {
14325 return function(scope, locals, assign, inputs) {
14326 var arg = left(scope, locals, assign, inputs) != right(scope, locals, assign, inputs);
14327 return context ? {value: arg} : arg;
14330 'binary<': function(left, right, context) {
14331 return function(scope, locals, assign, inputs) {
14332 var arg = left(scope, locals, assign, inputs) < right(scope, locals, assign, inputs);
14333 return context ? {value: arg} : arg;
14336 'binary>': function(left, right, context) {
14337 return function(scope, locals, assign, inputs) {
14338 var arg = left(scope, locals, assign, inputs) > right(scope, locals, assign, inputs);
14339 return context ? {value: arg} : arg;
14342 'binary<=': function(left, right, context) {
14343 return function(scope, locals, assign, inputs) {
14344 var arg = left(scope, locals, assign, inputs) <= right(scope, locals, assign, inputs);
14345 return context ? {value: arg} : arg;
14348 'binary>=': function(left, right, context) {
14349 return function(scope, locals, assign, inputs) {
14350 var arg = left(scope, locals, assign, inputs) >= right(scope, locals, assign, inputs);
14351 return context ? {value: arg} : arg;
14354 'binary&&': function(left, right, context) {
14355 return function(scope, locals, assign, inputs) {
14356 var arg = left(scope, locals, assign, inputs) && right(scope, locals, assign, inputs);
14357 return context ? {value: arg} : arg;
14360 'binary||': function(left, right, context) {
14361 return function(scope, locals, assign, inputs) {
14362 var arg = left(scope, locals, assign, inputs) || right(scope, locals, assign, inputs);
14363 return context ? {value: arg} : arg;
14366 'ternary?:': function(test, alternate, consequent, context) {
14367 return function(scope, locals, assign, inputs) {
14368 var arg = test(scope, locals, assign, inputs) ? alternate(scope, locals, assign, inputs) : consequent(scope, locals, assign, inputs);
14369 return context ? {value: arg} : arg;
14372 value: function(value, context) {
14373 return function() { return context ? {context: undefined, name: undefined, value: value} : value; };
14375 identifier: function(name, expensiveChecks, context, create, expression) {
14376 return function(scope, locals, assign, inputs) {
14377 var base = locals && (name in locals) ? locals : scope;
14378 if (create && create !== 1 && base && !(base[name])) {
14381 var value = base ? base[name] : undefined;
14382 if (expensiveChecks) {
14383 ensureSafeObject(value, expression);
14386 return {context: base, name: name, value: value};
14392 computedMember: function(left, right, context, create, expression) {
14393 return function(scope, locals, assign, inputs) {
14394 var lhs = left(scope, locals, assign, inputs);
14398 rhs = right(scope, locals, assign, inputs);
14399 rhs = getStringValue(rhs);
14400 ensureSafeMemberName(rhs, expression);
14401 if (create && create !== 1) {
14402 ensureSafeAssignContext(lhs);
14403 if (lhs && !(lhs[rhs])) {
14408 ensureSafeObject(value, expression);
14411 return {context: lhs, name: rhs, value: value};
14417 nonComputedMember: function(left, right, expensiveChecks, context, create, expression) {
14418 return function(scope, locals, assign, inputs) {
14419 var lhs = left(scope, locals, assign, inputs);
14420 if (create && create !== 1) {
14421 ensureSafeAssignContext(lhs);
14422 if (lhs && !(lhs[right])) {
14426 var value = lhs != null ? lhs[right] : undefined;
14427 if (expensiveChecks || isPossiblyDangerousMemberName(right)) {
14428 ensureSafeObject(value, expression);
14431 return {context: lhs, name: right, value: value};
14437 inputs: function(input, watchId) {
14438 return function(scope, value, locals, inputs) {
14439 if (inputs) return inputs[watchId];
14440 return input(scope, value, locals);
14448 var Parser = function(lexer, $filter, options) {
14449 this.lexer = lexer;
14450 this.$filter = $filter;
14451 this.options = options;
14452 this.ast = new AST(this.lexer);
14453 this.astCompiler = options.csp ? new ASTInterpreter(this.ast, $filter) :
14454 new ASTCompiler(this.ast, $filter);
14457 Parser.prototype = {
14458 constructor: Parser,
14460 parse: function(text) {
14461 return this.astCompiler.compile(text, this.options.expensiveChecks);
14465 function isPossiblyDangerousMemberName(name) {
14466 return name == 'constructor';
14469 var objectValueOf = Object.prototype.valueOf;
14471 function getValueOf(value) {
14472 return isFunction(value.valueOf) ? value.valueOf() : objectValueOf.call(value);
14475 ///////////////////////////////////
14484 * Converts Angular {@link guide/expression expression} into a function.
14487 * var getter = $parse('user.name');
14488 * var setter = getter.assign;
14489 * var context = {user:{name:'angular'}};
14490 * var locals = {user:{name:'local'}};
14492 * expect(getter(context)).toEqual('angular');
14493 * setter(context, 'newValue');
14494 * expect(context.user.name).toEqual('newValue');
14495 * expect(getter(context, locals)).toEqual('local');
14499 * @param {string} expression String expression to compile.
14500 * @returns {function(context, locals)} a function which represents the compiled expression:
14502 * * `context` – `{object}` – an object against which any expressions embedded in the strings
14503 * are evaluated against (typically a scope object).
14504 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
14507 * The returned function also has the following properties:
14508 * * `literal` – `{boolean}` – whether the expression's top-level node is a JavaScript
14510 * * `constant` – `{boolean}` – whether the expression is made entirely of JavaScript
14511 * constant literals.
14512 * * `assign` – `{?function(context, value)}` – if the expression is assignable, this will be
14513 * set to a function to change its value on the given context.
14520 * @name $parseProvider
14523 * `$parseProvider` can be used for configuring the default behavior of the {@link ng.$parse $parse}
14526 function $ParseProvider() {
14527 var cacheDefault = createMap();
14528 var cacheExpensive = createMap();
14530 this.$get = ['$filter', function($filter) {
14531 var noUnsafeEval = csp().noUnsafeEval;
14532 var $parseOptions = {
14534 expensiveChecks: false
14536 $parseOptionsExpensive = {
14538 expensiveChecks: true
14540 var runningChecksEnabled = false;
14542 $parse.$$runningExpensiveChecks = function() {
14543 return runningChecksEnabled;
14548 function $parse(exp, interceptorFn, expensiveChecks) {
14549 var parsedExpression, oneTime, cacheKey;
14551 expensiveChecks = expensiveChecks || runningChecksEnabled;
14553 switch (typeof exp) {
14558 var cache = (expensiveChecks ? cacheExpensive : cacheDefault);
14559 parsedExpression = cache[cacheKey];
14561 if (!parsedExpression) {
14562 if (exp.charAt(0) === ':' && exp.charAt(1) === ':') {
14564 exp = exp.substring(2);
14566 var parseOptions = expensiveChecks ? $parseOptionsExpensive : $parseOptions;
14567 var lexer = new Lexer(parseOptions);
14568 var parser = new Parser(lexer, $filter, parseOptions);
14569 parsedExpression = parser.parse(exp);
14570 if (parsedExpression.constant) {
14571 parsedExpression.$$watchDelegate = constantWatchDelegate;
14572 } else if (oneTime) {
14573 parsedExpression.$$watchDelegate = parsedExpression.literal ?
14574 oneTimeLiteralWatchDelegate : oneTimeWatchDelegate;
14575 } else if (parsedExpression.inputs) {
14576 parsedExpression.$$watchDelegate = inputsWatchDelegate;
14578 if (expensiveChecks) {
14579 parsedExpression = expensiveChecksInterceptor(parsedExpression);
14581 cache[cacheKey] = parsedExpression;
14583 return addInterceptor(parsedExpression, interceptorFn);
14586 return addInterceptor(exp, interceptorFn);
14589 return addInterceptor(noop, interceptorFn);
14593 function expensiveChecksInterceptor(fn) {
14594 if (!fn) return fn;
14595 expensiveCheckFn.$$watchDelegate = fn.$$watchDelegate;
14596 expensiveCheckFn.assign = expensiveChecksInterceptor(fn.assign);
14597 expensiveCheckFn.constant = fn.constant;
14598 expensiveCheckFn.literal = fn.literal;
14599 for (var i = 0; fn.inputs && i < fn.inputs.length; ++i) {
14600 fn.inputs[i] = expensiveChecksInterceptor(fn.inputs[i]);
14602 expensiveCheckFn.inputs = fn.inputs;
14604 return expensiveCheckFn;
14606 function expensiveCheckFn(scope, locals, assign, inputs) {
14607 var expensiveCheckOldValue = runningChecksEnabled;
14608 runningChecksEnabled = true;
14610 return fn(scope, locals, assign, inputs);
14612 runningChecksEnabled = expensiveCheckOldValue;
14617 function expressionInputDirtyCheck(newValue, oldValueOfValue) {
14619 if (newValue == null || oldValueOfValue == null) { // null/undefined
14620 return newValue === oldValueOfValue;
14623 if (typeof newValue === 'object') {
14625 // attempt to convert the value to a primitive type
14626 // TODO(docs): add a note to docs that by implementing valueOf even objects and arrays can
14627 // be cheaply dirty-checked
14628 newValue = getValueOf(newValue);
14630 if (typeof newValue === 'object') {
14631 // objects/arrays are not supported - deep-watching them would be too expensive
14635 // fall-through to the primitive equality check
14639 return newValue === oldValueOfValue || (newValue !== newValue && oldValueOfValue !== oldValueOfValue);
14642 function inputsWatchDelegate(scope, listener, objectEquality, parsedExpression, prettyPrintExpression) {
14643 var inputExpressions = parsedExpression.inputs;
14646 if (inputExpressions.length === 1) {
14647 var oldInputValueOf = expressionInputDirtyCheck; // init to something unique so that equals check fails
14648 inputExpressions = inputExpressions[0];
14649 return scope.$watch(function expressionInputWatch(scope) {
14650 var newInputValue = inputExpressions(scope);
14651 if (!expressionInputDirtyCheck(newInputValue, oldInputValueOf)) {
14652 lastResult = parsedExpression(scope, undefined, undefined, [newInputValue]);
14653 oldInputValueOf = newInputValue && getValueOf(newInputValue);
14656 }, listener, objectEquality, prettyPrintExpression);
14659 var oldInputValueOfValues = [];
14660 var oldInputValues = [];
14661 for (var i = 0, ii = inputExpressions.length; i < ii; i++) {
14662 oldInputValueOfValues[i] = expressionInputDirtyCheck; // init to something unique so that equals check fails
14663 oldInputValues[i] = null;
14666 return scope.$watch(function expressionInputsWatch(scope) {
14667 var changed = false;
14669 for (var i = 0, ii = inputExpressions.length; i < ii; i++) {
14670 var newInputValue = inputExpressions[i](scope);
14671 if (changed || (changed = !expressionInputDirtyCheck(newInputValue, oldInputValueOfValues[i]))) {
14672 oldInputValues[i] = newInputValue;
14673 oldInputValueOfValues[i] = newInputValue && getValueOf(newInputValue);
14678 lastResult = parsedExpression(scope, undefined, undefined, oldInputValues);
14682 }, listener, objectEquality, prettyPrintExpression);
14685 function oneTimeWatchDelegate(scope, listener, objectEquality, parsedExpression) {
14686 var unwatch, lastValue;
14687 return unwatch = scope.$watch(function oneTimeWatch(scope) {
14688 return parsedExpression(scope);
14689 }, function oneTimeListener(value, old, scope) {
14691 if (isFunction(listener)) {
14692 listener.apply(this, arguments);
14694 if (isDefined(value)) {
14695 scope.$$postDigest(function() {
14696 if (isDefined(lastValue)) {
14701 }, objectEquality);
14704 function oneTimeLiteralWatchDelegate(scope, listener, objectEquality, parsedExpression) {
14705 var unwatch, lastValue;
14706 return unwatch = scope.$watch(function oneTimeWatch(scope) {
14707 return parsedExpression(scope);
14708 }, function oneTimeListener(value, old, scope) {
14710 if (isFunction(listener)) {
14711 listener.call(this, value, old, scope);
14713 if (isAllDefined(value)) {
14714 scope.$$postDigest(function() {
14715 if (isAllDefined(lastValue)) unwatch();
14718 }, objectEquality);
14720 function isAllDefined(value) {
14721 var allDefined = true;
14722 forEach(value, function(val) {
14723 if (!isDefined(val)) allDefined = false;
14729 function constantWatchDelegate(scope, listener, objectEquality, parsedExpression) {
14731 return unwatch = scope.$watch(function constantWatch(scope) {
14732 return parsedExpression(scope);
14733 }, function constantListener(value, old, scope) {
14734 if (isFunction(listener)) {
14735 listener.apply(this, arguments);
14738 }, objectEquality);
14741 function addInterceptor(parsedExpression, interceptorFn) {
14742 if (!interceptorFn) return parsedExpression;
14743 var watchDelegate = parsedExpression.$$watchDelegate;
14744 var useInputs = false;
14747 watchDelegate !== oneTimeLiteralWatchDelegate &&
14748 watchDelegate !== oneTimeWatchDelegate;
14750 var fn = regularWatch ? function regularInterceptedExpression(scope, locals, assign, inputs) {
14751 var value = useInputs && inputs ? inputs[0] : parsedExpression(scope, locals, assign, inputs);
14752 return interceptorFn(value, scope, locals);
14753 } : function oneTimeInterceptedExpression(scope, locals, assign, inputs) {
14754 var value = parsedExpression(scope, locals, assign, inputs);
14755 var result = interceptorFn(value, scope, locals);
14756 // we only return the interceptor's result if the
14757 // initial value is defined (for bind-once)
14758 return isDefined(value) ? result : value;
14761 // Propagate $$watchDelegates other then inputsWatchDelegate
14762 if (parsedExpression.$$watchDelegate &&
14763 parsedExpression.$$watchDelegate !== inputsWatchDelegate) {
14764 fn.$$watchDelegate = parsedExpression.$$watchDelegate;
14765 } else if (!interceptorFn.$stateful) {
14766 // If there is an interceptor, but no watchDelegate then treat the interceptor like
14767 // we treat filters - it is assumed to be a pure function unless flagged with $stateful
14768 fn.$$watchDelegate = inputsWatchDelegate;
14769 useInputs = !parsedExpression.inputs;
14770 fn.inputs = parsedExpression.inputs ? parsedExpression.inputs : [parsedExpression];
14781 * @requires $rootScope
14784 * A service that helps you run functions asynchronously, and use their return values (or exceptions)
14785 * when they are done processing.
14787 * This is an implementation of promises/deferred objects inspired by
14788 * [Kris Kowal's Q](https://github.com/kriskowal/q).
14790 * $q can be used in two fashions --- one which is more similar to Kris Kowal's Q or jQuery's Deferred
14791 * implementations, and the other which resembles ES6 promises to some degree.
14795 * The streamlined ES6 style promise is essentially just using $q as a constructor which takes a `resolver`
14796 * function as the first argument. This is similar to the native Promise implementation from ES6 Harmony,
14797 * see [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise).
14799 * While the constructor-style use is supported, not all of the supporting methods from ES6 Harmony promises are
14802 * It can be used like so:
14805 * // for the purpose of this example let's assume that variables `$q` and `okToGreet`
14806 * // are available in the current lexical scope (they could have been injected or passed in).
14808 * function asyncGreet(name) {
14809 * // perform some asynchronous operation, resolve or reject the promise when appropriate.
14810 * return $q(function(resolve, reject) {
14811 * setTimeout(function() {
14812 * if (okToGreet(name)) {
14813 * resolve('Hello, ' + name + '!');
14815 * reject('Greeting ' + name + ' is not allowed.');
14821 * var promise = asyncGreet('Robin Hood');
14822 * promise.then(function(greeting) {
14823 * alert('Success: ' + greeting);
14824 * }, function(reason) {
14825 * alert('Failed: ' + reason);
14829 * Note: progress/notify callbacks are not currently supported via the ES6-style interface.
14831 * Note: unlike ES6 behaviour, an exception thrown in the constructor function will NOT implicitly reject the promise.
14833 * However, the more traditional CommonJS-style usage is still available, and documented below.
14835 * [The CommonJS Promise proposal](http://wiki.commonjs.org/wiki/Promises) describes a promise as an
14836 * interface for interacting with an object that represents the result of an action that is
14837 * performed asynchronously, and may or may not be finished at any given point in time.
14839 * From the perspective of dealing with error handling, deferred and promise APIs are to
14840 * asynchronous programming what `try`, `catch` and `throw` keywords are to synchronous programming.
14843 * // for the purpose of this example let's assume that variables `$q` and `okToGreet`
14844 * // are available in the current lexical scope (they could have been injected or passed in).
14846 * function asyncGreet(name) {
14847 * var deferred = $q.defer();
14849 * setTimeout(function() {
14850 * deferred.notify('About to greet ' + name + '.');
14852 * if (okToGreet(name)) {
14853 * deferred.resolve('Hello, ' + name + '!');
14855 * deferred.reject('Greeting ' + name + ' is not allowed.');
14859 * return deferred.promise;
14862 * var promise = asyncGreet('Robin Hood');
14863 * promise.then(function(greeting) {
14864 * alert('Success: ' + greeting);
14865 * }, function(reason) {
14866 * alert('Failed: ' + reason);
14867 * }, function(update) {
14868 * alert('Got notification: ' + update);
14872 * At first it might not be obvious why this extra complexity is worth the trouble. The payoff
14873 * comes in the way of guarantees that promise and deferred APIs make, see
14874 * https://github.com/kriskowal/uncommonjs/blob/master/promises/specification.md.
14876 * Additionally the promise api allows for composition that is very hard to do with the
14877 * traditional callback ([CPS](http://en.wikipedia.org/wiki/Continuation-passing_style)) approach.
14878 * For more on this please see the [Q documentation](https://github.com/kriskowal/q) especially the
14879 * section on serial or parallel joining of promises.
14881 * # The Deferred API
14883 * A new instance of deferred is constructed by calling `$q.defer()`.
14885 * The purpose of the deferred object is to expose the associated Promise instance as well as APIs
14886 * that can be used for signaling the successful or unsuccessful completion, as well as the status
14891 * - `resolve(value)` – resolves the derived promise with the `value`. If the value is a rejection
14892 * constructed via `$q.reject`, the promise will be rejected instead.
14893 * - `reject(reason)` – rejects the derived promise with the `reason`. This is equivalent to
14894 * resolving it with a rejection constructed via `$q.reject`.
14895 * - `notify(value)` - provides updates on the status of the promise's execution. This may be called
14896 * multiple times before the promise is either resolved or rejected.
14900 * - promise – `{Promise}` – promise object associated with this deferred.
14903 * # The Promise API
14905 * A new promise instance is created when a deferred instance is created and can be retrieved by
14906 * calling `deferred.promise`.
14908 * The purpose of the promise object is to allow for interested parties to get access to the result
14909 * of the deferred task when it completes.
14913 * - `then(successCallback, errorCallback, notifyCallback)` – regardless of when the promise was or
14914 * will be resolved or rejected, `then` calls one of the success or error callbacks asynchronously
14915 * as soon as the result is available. The callbacks are called with a single argument: the result
14916 * or rejection reason. Additionally, the notify callback may be called zero or more times to
14917 * provide a progress indication, before the promise is resolved or rejected.
14919 * This method *returns a new promise* which is resolved or rejected via the return value of the
14920 * `successCallback`, `errorCallback` (unless that value is a promise, in which case it is resolved
14921 * with the value which is resolved in that promise using
14922 * [promise chaining](http://www.html5rocks.com/en/tutorials/es6/promises/#toc-promises-queues)).
14923 * It also notifies via the return value of the `notifyCallback` method. The promise cannot be
14924 * resolved or rejected from the notifyCallback method.
14926 * - `catch(errorCallback)` – shorthand for `promise.then(null, errorCallback)`
14928 * - `finally(callback, notifyCallback)` – allows you to observe either the fulfillment or rejection of a promise,
14929 * but to do so without modifying the final value. This is useful to release resources or do some
14930 * clean-up that needs to be done whether the promise was rejected or resolved. See the [full
14931 * specification](https://github.com/kriskowal/q/wiki/API-Reference#promisefinallycallback) for
14932 * more information.
14934 * # Chaining promises
14936 * Because calling the `then` method of a promise returns a new derived promise, it is easily
14937 * possible to create a chain of promises:
14940 * promiseB = promiseA.then(function(result) {
14941 * return result + 1;
14944 * // promiseB will be resolved immediately after promiseA is resolved and its value
14945 * // will be the result of promiseA incremented by 1
14948 * It is possible to create chains of any length and since a promise can be resolved with another
14949 * promise (which will defer its resolution further), it is possible to pause/defer resolution of
14950 * the promises at any point in the chain. This makes it possible to implement powerful APIs like
14951 * $http's response interceptors.
14954 * # Differences between Kris Kowal's Q and $q
14956 * There are two main differences:
14958 * - $q is integrated with the {@link ng.$rootScope.Scope} Scope model observation
14959 * mechanism in angular, which means faster propagation of resolution or rejection into your
14960 * models and avoiding unnecessary browser repaints, which would result in flickering UI.
14961 * - Q has many more features than $q, but that comes at a cost of bytes. $q is tiny, but contains
14962 * all the important functionality needed for common async tasks.
14967 * it('should simulate promise', inject(function($q, $rootScope) {
14968 * var deferred = $q.defer();
14969 * var promise = deferred.promise;
14970 * var resolvedValue;
14972 * promise.then(function(value) { resolvedValue = value; });
14973 * expect(resolvedValue).toBeUndefined();
14975 * // Simulate resolving of promise
14976 * deferred.resolve(123);
14977 * // Note that the 'then' function does not get called synchronously.
14978 * // This is because we want the promise API to always be async, whether or not
14979 * // it got called synchronously or asynchronously.
14980 * expect(resolvedValue).toBeUndefined();
14982 * // Propagate promise resolution to 'then' functions using $apply().
14983 * $rootScope.$apply();
14984 * expect(resolvedValue).toEqual(123);
14988 * @param {function(function, function)} resolver Function which is responsible for resolving or
14989 * rejecting the newly created promise. The first parameter is a function which resolves the
14990 * promise, the second parameter is a function which rejects the promise.
14992 * @returns {Promise} The newly created promise.
14994 function $QProvider() {
14996 this.$get = ['$rootScope', '$exceptionHandler', function($rootScope, $exceptionHandler) {
14997 return qFactory(function(callback) {
14998 $rootScope.$evalAsync(callback);
14999 }, $exceptionHandler);
15003 function $$QProvider() {
15004 this.$get = ['$browser', '$exceptionHandler', function($browser, $exceptionHandler) {
15005 return qFactory(function(callback) {
15006 $browser.defer(callback);
15007 }, $exceptionHandler);
15012 * Constructs a promise manager.
15014 * @param {function(function)} nextTick Function for executing functions in the next turn.
15015 * @param {function(...*)} exceptionHandler Function into which unexpected exceptions are passed for
15016 * debugging purposes.
15017 * @returns {object} Promise manager.
15019 function qFactory(nextTick, exceptionHandler) {
15020 var $qMinErr = minErr('$q', TypeError);
15021 function callOnce(self, resolveFn, rejectFn) {
15022 var called = false;
15023 function wrap(fn) {
15024 return function(value) {
15025 if (called) return;
15027 fn.call(self, value);
15031 return [wrap(resolveFn), wrap(rejectFn)];
15036 * @name ng.$q#defer
15040 * Creates a `Deferred` object which represents a task which will finish in the future.
15042 * @returns {Deferred} Returns a new instance of deferred.
15044 var defer = function() {
15045 return new Deferred();
15048 function Promise() {
15049 this.$$state = { status: 0 };
15052 extend(Promise.prototype, {
15053 then: function(onFulfilled, onRejected, progressBack) {
15054 if (isUndefined(onFulfilled) && isUndefined(onRejected) && isUndefined(progressBack)) {
15057 var result = new Deferred();
15059 this.$$state.pending = this.$$state.pending || [];
15060 this.$$state.pending.push([result, onFulfilled, onRejected, progressBack]);
15061 if (this.$$state.status > 0) scheduleProcessQueue(this.$$state);
15063 return result.promise;
15066 "catch": function(callback) {
15067 return this.then(null, callback);
15070 "finally": function(callback, progressBack) {
15071 return this.then(function(value) {
15072 return handleCallback(value, true, callback);
15073 }, function(error) {
15074 return handleCallback(error, false, callback);
15079 //Faster, more basic than angular.bind http://jsperf.com/angular-bind-vs-custom-vs-native
15080 function simpleBind(context, fn) {
15081 return function(value) {
15082 fn.call(context, value);
15086 function processQueue(state) {
15087 var fn, deferred, pending;
15089 pending = state.pending;
15090 state.processScheduled = false;
15091 state.pending = undefined;
15092 for (var i = 0, ii = pending.length; i < ii; ++i) {
15093 deferred = pending[i][0];
15094 fn = pending[i][state.status];
15096 if (isFunction(fn)) {
15097 deferred.resolve(fn(state.value));
15098 } else if (state.status === 1) {
15099 deferred.resolve(state.value);
15101 deferred.reject(state.value);
15104 deferred.reject(e);
15105 exceptionHandler(e);
15110 function scheduleProcessQueue(state) {
15111 if (state.processScheduled || !state.pending) return;
15112 state.processScheduled = true;
15113 nextTick(function() { processQueue(state); });
15116 function Deferred() {
15117 this.promise = new Promise();
15118 //Necessary to support unbound execution :/
15119 this.resolve = simpleBind(this, this.resolve);
15120 this.reject = simpleBind(this, this.reject);
15121 this.notify = simpleBind(this, this.notify);
15124 extend(Deferred.prototype, {
15125 resolve: function(val) {
15126 if (this.promise.$$state.status) return;
15127 if (val === this.promise) {
15128 this.$$reject($qMinErr(
15130 "Expected promise to be resolved with value other than itself '{0}'",
15133 this.$$resolve(val);
15138 $$resolve: function(val) {
15141 fns = callOnce(this, this.$$resolve, this.$$reject);
15143 if ((isObject(val) || isFunction(val))) then = val && val.then;
15144 if (isFunction(then)) {
15145 this.promise.$$state.status = -1;
15146 then.call(val, fns[0], fns[1], this.notify);
15148 this.promise.$$state.value = val;
15149 this.promise.$$state.status = 1;
15150 scheduleProcessQueue(this.promise.$$state);
15154 exceptionHandler(e);
15158 reject: function(reason) {
15159 if (this.promise.$$state.status) return;
15160 this.$$reject(reason);
15163 $$reject: function(reason) {
15164 this.promise.$$state.value = reason;
15165 this.promise.$$state.status = 2;
15166 scheduleProcessQueue(this.promise.$$state);
15169 notify: function(progress) {
15170 var callbacks = this.promise.$$state.pending;
15172 if ((this.promise.$$state.status <= 0) && callbacks && callbacks.length) {
15173 nextTick(function() {
15174 var callback, result;
15175 for (var i = 0, ii = callbacks.length; i < ii; i++) {
15176 result = callbacks[i][0];
15177 callback = callbacks[i][3];
15179 result.notify(isFunction(callback) ? callback(progress) : progress);
15181 exceptionHandler(e);
15195 * Creates a promise that is resolved as rejected with the specified `reason`. This api should be
15196 * used to forward rejection in a chain of promises. If you are dealing with the last promise in
15197 * a promise chain, you don't need to worry about it.
15199 * When comparing deferreds/promises to the familiar behavior of try/catch/throw, think of
15200 * `reject` as the `throw` keyword in JavaScript. This also means that if you "catch" an error via
15201 * a promise error callback and you want to forward the error to the promise derived from the
15202 * current promise, you have to "rethrow" the error by returning a rejection constructed via
15206 * promiseB = promiseA.then(function(result) {
15207 * // success: do something and resolve promiseB
15208 * // with the old or a new result
15210 * }, function(reason) {
15211 * // error: handle the error if possible and
15212 * // resolve promiseB with newPromiseOrValue,
15213 * // otherwise forward the rejection to promiseB
15214 * if (canHandle(reason)) {
15215 * // handle the error and recover
15216 * return newPromiseOrValue;
15218 * return $q.reject(reason);
15222 * @param {*} reason Constant, message, exception or an object representing the rejection reason.
15223 * @returns {Promise} Returns a promise that was already resolved as rejected with the `reason`.
15225 var reject = function(reason) {
15226 var result = new Deferred();
15227 result.reject(reason);
15228 return result.promise;
15231 var makePromise = function makePromise(value, resolved) {
15232 var result = new Deferred();
15234 result.resolve(value);
15236 result.reject(value);
15238 return result.promise;
15241 var handleCallback = function handleCallback(value, isResolved, callback) {
15242 var callbackOutput = null;
15244 if (isFunction(callback)) callbackOutput = callback();
15246 return makePromise(e, false);
15248 if (isPromiseLike(callbackOutput)) {
15249 return callbackOutput.then(function() {
15250 return makePromise(value, isResolved);
15251 }, function(error) {
15252 return makePromise(error, false);
15255 return makePromise(value, isResolved);
15265 * Wraps an object that might be a value or a (3rd party) then-able promise into a $q promise.
15266 * This is useful when you are dealing with an object that might or might not be a promise, or if
15267 * the promise comes from a source that can't be trusted.
15269 * @param {*} value Value or a promise
15270 * @param {Function=} successCallback
15271 * @param {Function=} errorCallback
15272 * @param {Function=} progressCallback
15273 * @returns {Promise} Returns a promise of the passed value or promise
15277 var when = function(value, callback, errback, progressBack) {
15278 var result = new Deferred();
15279 result.resolve(value);
15280 return result.promise.then(callback, errback, progressBack);
15289 * Alias of {@link ng.$q#when when} to maintain naming consistency with ES6.
15291 * @param {*} value Value or a promise
15292 * @param {Function=} successCallback
15293 * @param {Function=} errorCallback
15294 * @param {Function=} progressCallback
15295 * @returns {Promise} Returns a promise of the passed value or promise
15297 var resolve = when;
15305 * Combines multiple promises into a single promise that is resolved when all of the input
15306 * promises are resolved.
15308 * @param {Array.<Promise>|Object.<Promise>} promises An array or hash of promises.
15309 * @returns {Promise} Returns a single promise that will be resolved with an array/hash of values,
15310 * each value corresponding to the promise at the same index/key in the `promises` array/hash.
15311 * If any of the promises is resolved with a rejection, this resulting promise will be rejected
15312 * with the same rejection value.
15315 function all(promises) {
15316 var deferred = new Deferred(),
15318 results = isArray(promises) ? [] : {};
15320 forEach(promises, function(promise, key) {
15322 when(promise).then(function(value) {
15323 if (results.hasOwnProperty(key)) return;
15324 results[key] = value;
15325 if (!(--counter)) deferred.resolve(results);
15326 }, function(reason) {
15327 if (results.hasOwnProperty(key)) return;
15328 deferred.reject(reason);
15332 if (counter === 0) {
15333 deferred.resolve(results);
15336 return deferred.promise;
15339 var $Q = function Q(resolver) {
15340 if (!isFunction(resolver)) {
15341 throw $qMinErr('norslvr', "Expected resolverFn, got '{0}'", resolver);
15344 if (!(this instanceof Q)) {
15345 // More useful when $Q is the Promise itself.
15346 return new Q(resolver);
15349 var deferred = new Deferred();
15351 function resolveFn(value) {
15352 deferred.resolve(value);
15355 function rejectFn(reason) {
15356 deferred.reject(reason);
15359 resolver(resolveFn, rejectFn);
15361 return deferred.promise;
15365 $Q.reject = reject;
15367 $Q.resolve = resolve;
15373 function $$RAFProvider() { //rAF
15374 this.$get = ['$window', '$timeout', function($window, $timeout) {
15375 var requestAnimationFrame = $window.requestAnimationFrame ||
15376 $window.webkitRequestAnimationFrame;
15378 var cancelAnimationFrame = $window.cancelAnimationFrame ||
15379 $window.webkitCancelAnimationFrame ||
15380 $window.webkitCancelRequestAnimationFrame;
15382 var rafSupported = !!requestAnimationFrame;
15383 var raf = rafSupported
15385 var id = requestAnimationFrame(fn);
15386 return function() {
15387 cancelAnimationFrame(id);
15391 var timer = $timeout(fn, 16.66, false); // 1000 / 60 = 16.666
15392 return function() {
15393 $timeout.cancel(timer);
15397 raf.supported = rafSupported;
15406 * The design decisions behind the scope are heavily favored for speed and memory consumption.
15408 * The typical use of scope is to watch the expressions, which most of the time return the same
15409 * value as last time so we optimize the operation.
15411 * Closures construction is expensive in terms of speed as well as memory:
15412 * - No closures, instead use prototypical inheritance for API
15413 * - Internal state needs to be stored on scope directly, which means that private state is
15414 * exposed as $$____ properties
15416 * Loop operations are optimized by using while(count--) { ... }
15417 * - This means that in order to keep the same order of execution as addition we have to add
15418 * items to the array at the beginning (unshift) instead of at the end (push)
15420 * Child scopes are created and removed often
15421 * - Using an array would be slow since inserts in the middle are expensive; so we use linked lists
15423 * There are fewer watches than observers. This is why you don't want the observer to be implemented
15424 * in the same way as watch. Watch requires return of the initialization function which is expensive
15431 * @name $rootScopeProvider
15434 * Provider for the $rootScope service.
15439 * @name $rootScopeProvider#digestTtl
15442 * Sets the number of `$digest` iterations the scope should attempt to execute before giving up and
15443 * assuming that the model is unstable.
15445 * The current default is 10 iterations.
15447 * In complex applications it's possible that the dependencies between `$watch`s will result in
15448 * several digest iterations. However if an application needs more than the default 10 digest
15449 * iterations for its model to stabilize then you should investigate what is causing the model to
15450 * continuously change during the digest.
15452 * Increasing the TTL could have performance implications, so you should not change it without
15453 * proper justification.
15455 * @param {number} limit The number of digest iterations.
15464 * Every application has a single root {@link ng.$rootScope.Scope scope}.
15465 * All other scopes are descendant scopes of the root scope. Scopes provide separation
15466 * between the model and the view, via a mechanism for watching the model for changes.
15467 * They also provide event emission/broadcast and subscription facility. See the
15468 * {@link guide/scope developer guide on scopes}.
15470 function $RootScopeProvider() {
15472 var $rootScopeMinErr = minErr('$rootScope');
15473 var lastDirtyWatch = null;
15474 var applyAsyncId = null;
15476 this.digestTtl = function(value) {
15477 if (arguments.length) {
15483 function createChildScopeClass(parent) {
15484 function ChildScope() {
15485 this.$$watchers = this.$$nextSibling =
15486 this.$$childHead = this.$$childTail = null;
15487 this.$$listeners = {};
15488 this.$$listenerCount = {};
15489 this.$$watchersCount = 0;
15490 this.$id = nextUid();
15491 this.$$ChildScope = null;
15493 ChildScope.prototype = parent;
15497 this.$get = ['$injector', '$exceptionHandler', '$parse', '$browser',
15498 function($injector, $exceptionHandler, $parse, $browser) {
15500 function destroyChildScope($event) {
15501 $event.currentScope.$$destroyed = true;
15504 function cleanUpScope($scope) {
15507 // There is a memory leak in IE9 if all child scopes are not disconnected
15508 // completely when a scope is destroyed. So this code will recurse up through
15509 // all this scopes children
15511 // See issue https://github.com/angular/angular.js/issues/10706
15512 $scope.$$childHead && cleanUpScope($scope.$$childHead);
15513 $scope.$$nextSibling && cleanUpScope($scope.$$nextSibling);
15516 // The code below works around IE9 and V8's memory leaks
15519 // - https://code.google.com/p/v8/issues/detail?id=2073#c26
15520 // - https://github.com/angular/angular.js/issues/6794#issuecomment-38648909
15521 // - https://github.com/angular/angular.js/issues/1313#issuecomment-10378451
15523 $scope.$parent = $scope.$$nextSibling = $scope.$$prevSibling = $scope.$$childHead =
15524 $scope.$$childTail = $scope.$root = $scope.$$watchers = null;
15529 * @name $rootScope.Scope
15532 * A root scope can be retrieved using the {@link ng.$rootScope $rootScope} key from the
15533 * {@link auto.$injector $injector}. Child scopes are created using the
15534 * {@link ng.$rootScope.Scope#$new $new()} method. (Most scopes are created automatically when
15535 * compiled HTML template is executed.) See also the {@link guide/scope Scopes guide} for
15536 * an in-depth introduction and usage examples.
15540 * A scope can inherit from a parent scope, as in this example:
15542 var parent = $rootScope;
15543 var child = parent.$new();
15545 parent.salutation = "Hello";
15546 expect(child.salutation).toEqual('Hello');
15548 child.salutation = "Welcome";
15549 expect(child.salutation).toEqual('Welcome');
15550 expect(parent.salutation).toEqual('Hello');
15553 * When interacting with `Scope` in tests, additional helper methods are available on the
15554 * instances of `Scope` type. See {@link ngMock.$rootScope.Scope ngMock Scope} for additional
15558 * @param {Object.<string, function()>=} providers Map of service factory which need to be
15559 * provided for the current scope. Defaults to {@link ng}.
15560 * @param {Object.<string, *>=} instanceCache Provides pre-instantiated services which should
15561 * append/override services provided by `providers`. This is handy
15562 * when unit-testing and having the need to override a default
15564 * @returns {Object} Newly created scope.
15568 this.$id = nextUid();
15569 this.$$phase = this.$parent = this.$$watchers =
15570 this.$$nextSibling = this.$$prevSibling =
15571 this.$$childHead = this.$$childTail = null;
15573 this.$$destroyed = false;
15574 this.$$listeners = {};
15575 this.$$listenerCount = {};
15576 this.$$watchersCount = 0;
15577 this.$$isolateBindings = null;
15582 * @name $rootScope.Scope#$id
15585 * Unique scope ID (monotonically increasing) useful for debugging.
15590 * @name $rootScope.Scope#$parent
15593 * Reference to the parent scope.
15598 * @name $rootScope.Scope#$root
15601 * Reference to the root scope.
15604 Scope.prototype = {
15605 constructor: Scope,
15608 * @name $rootScope.Scope#$new
15612 * Creates a new child {@link ng.$rootScope.Scope scope}.
15614 * The parent scope will propagate the {@link ng.$rootScope.Scope#$digest $digest()} event.
15615 * The scope can be removed from the scope hierarchy using {@link ng.$rootScope.Scope#$destroy $destroy()}.
15617 * {@link ng.$rootScope.Scope#$destroy $destroy()} must be called on a scope when it is
15618 * desired for the scope and its child scopes to be permanently detached from the parent and
15619 * thus stop participating in model change detection and listener notification by invoking.
15621 * @param {boolean} isolate If true, then the scope does not prototypically inherit from the
15622 * parent scope. The scope is isolated, as it can not see parent scope properties.
15623 * When creating widgets, it is useful for the widget to not accidentally read parent
15626 * @param {Scope} [parent=this] The {@link ng.$rootScope.Scope `Scope`} that will be the `$parent`
15627 * of the newly created scope. Defaults to `this` scope if not provided.
15628 * This is used when creating a transclude scope to correctly place it
15629 * in the scope hierarchy while maintaining the correct prototypical
15632 * @returns {Object} The newly created child scope.
15635 $new: function(isolate, parent) {
15638 parent = parent || this;
15641 child = new Scope();
15642 child.$root = this.$root;
15644 // Only create a child scope class if somebody asks for one,
15645 // but cache it to allow the VM to optimize lookups.
15646 if (!this.$$ChildScope) {
15647 this.$$ChildScope = createChildScopeClass(this);
15649 child = new this.$$ChildScope();
15651 child.$parent = parent;
15652 child.$$prevSibling = parent.$$childTail;
15653 if (parent.$$childHead) {
15654 parent.$$childTail.$$nextSibling = child;
15655 parent.$$childTail = child;
15657 parent.$$childHead = parent.$$childTail = child;
15660 // When the new scope is not isolated or we inherit from `this`, and
15661 // the parent scope is destroyed, the property `$$destroyed` is inherited
15662 // prototypically. In all other cases, this property needs to be set
15663 // when the parent scope is destroyed.
15664 // The listener needs to be added after the parent is set
15665 if (isolate || parent != this) child.$on('$destroy', destroyChildScope);
15672 * @name $rootScope.Scope#$watch
15676 * Registers a `listener` callback to be executed whenever the `watchExpression` changes.
15678 * - The `watchExpression` is called on every call to {@link ng.$rootScope.Scope#$digest
15679 * $digest()} and should return the value that will be watched. (`watchExpression` should not change
15680 * its value when executed multiple times with the same input because it may be executed multiple
15681 * times by {@link ng.$rootScope.Scope#$digest $digest()}. That is, `watchExpression` should be
15682 * [idempotent](http://en.wikipedia.org/wiki/Idempotence).
15683 * - The `listener` is called only when the value from the current `watchExpression` and the
15684 * previous call to `watchExpression` are not equal (with the exception of the initial run,
15685 * see below). Inequality is determined according to reference inequality,
15686 * [strict comparison](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators)
15687 * via the `!==` Javascript operator, unless `objectEquality == true`
15689 * - When `objectEquality == true`, inequality of the `watchExpression` is determined
15690 * according to the {@link angular.equals} function. To save the value of the object for
15691 * later comparison, the {@link angular.copy} function is used. This therefore means that
15692 * watching complex objects will have adverse memory and performance implications.
15693 * - The watch `listener` may change the model, which may trigger other `listener`s to fire.
15694 * This is achieved by rerunning the watchers until no changes are detected. The rerun
15695 * iteration limit is 10 to prevent an infinite loop deadlock.
15698 * If you want to be notified whenever {@link ng.$rootScope.Scope#$digest $digest} is called,
15699 * you can register a `watchExpression` function with no `listener`. (Be prepared for
15700 * multiple calls to your `watchExpression` because it will execute multiple times in a
15701 * single {@link ng.$rootScope.Scope#$digest $digest} cycle if a change is detected.)
15703 * After a watcher is registered with the scope, the `listener` fn is called asynchronously
15704 * (via {@link ng.$rootScope.Scope#$evalAsync $evalAsync}) to initialize the
15705 * watcher. In rare cases, this is undesirable because the listener is called when the result
15706 * of `watchExpression` didn't change. To detect this scenario within the `listener` fn, you
15707 * can compare the `newVal` and `oldVal`. If these two values are identical (`===`) then the
15708 * listener was called due to initialization.
15714 // let's assume that scope was dependency injected as the $rootScope
15715 var scope = $rootScope;
15716 scope.name = 'misko';
15719 expect(scope.counter).toEqual(0);
15720 scope.$watch('name', function(newValue, oldValue) {
15721 scope.counter = scope.counter + 1;
15723 expect(scope.counter).toEqual(0);
15726 // the listener is always called during the first $digest loop after it was registered
15727 expect(scope.counter).toEqual(1);
15730 // but now it will not be called unless the value changes
15731 expect(scope.counter).toEqual(1);
15733 scope.name = 'adam';
15735 expect(scope.counter).toEqual(2);
15739 // Using a function as a watchExpression
15741 scope.foodCounter = 0;
15742 expect(scope.foodCounter).toEqual(0);
15744 // This function returns the value being watched. It is called for each turn of the $digest loop
15745 function() { return food; },
15746 // This is the change listener, called when the value returned from the above function changes
15747 function(newValue, oldValue) {
15748 if ( newValue !== oldValue ) {
15749 // Only increment the counter if the value changed
15750 scope.foodCounter = scope.foodCounter + 1;
15754 // No digest has been run so the counter will be zero
15755 expect(scope.foodCounter).toEqual(0);
15757 // Run the digest but since food has not changed count will still be zero
15759 expect(scope.foodCounter).toEqual(0);
15761 // Update food and run digest. Now the counter will increment
15762 food = 'cheeseburger';
15764 expect(scope.foodCounter).toEqual(1);
15770 * @param {(function()|string)} watchExpression Expression that is evaluated on each
15771 * {@link ng.$rootScope.Scope#$digest $digest} cycle. A change in the return value triggers
15772 * a call to the `listener`.
15774 * - `string`: Evaluated as {@link guide/expression expression}
15775 * - `function(scope)`: called with current `scope` as a parameter.
15776 * @param {function(newVal, oldVal, scope)} listener Callback called whenever the value
15777 * of `watchExpression` changes.
15779 * - `newVal` contains the current value of the `watchExpression`
15780 * - `oldVal` contains the previous value of the `watchExpression`
15781 * - `scope` refers to the current scope
15782 * @param {boolean=} objectEquality Compare for object equality using {@link angular.equals} instead of
15783 * comparing for reference equality.
15784 * @returns {function()} Returns a deregistration function for this listener.
15786 $watch: function(watchExp, listener, objectEquality, prettyPrintExpression) {
15787 var get = $parse(watchExp);
15789 if (get.$$watchDelegate) {
15790 return get.$$watchDelegate(this, listener, objectEquality, get, watchExp);
15793 array = scope.$$watchers,
15796 last: initWatchVal,
15798 exp: prettyPrintExpression || watchExp,
15799 eq: !!objectEquality
15802 lastDirtyWatch = null;
15804 if (!isFunction(listener)) {
15809 array = scope.$$watchers = [];
15811 // we use unshift since we use a while loop in $digest for speed.
15812 // the while loop reads in reverse order.
15813 array.unshift(watcher);
15814 incrementWatchersCount(this, 1);
15816 return function deregisterWatch() {
15817 if (arrayRemove(array, watcher) >= 0) {
15818 incrementWatchersCount(scope, -1);
15820 lastDirtyWatch = null;
15826 * @name $rootScope.Scope#$watchGroup
15830 * A variant of {@link ng.$rootScope.Scope#$watch $watch()} where it watches an array of `watchExpressions`.
15831 * If any one expression in the collection changes the `listener` is executed.
15833 * - The items in the `watchExpressions` array are observed via standard $watch operation and are examined on every
15834 * call to $digest() to see if any items changes.
15835 * - The `listener` is called whenever any expression in the `watchExpressions` array changes.
15837 * @param {Array.<string|Function(scope)>} watchExpressions Array of expressions that will be individually
15838 * watched using {@link ng.$rootScope.Scope#$watch $watch()}
15840 * @param {function(newValues, oldValues, scope)} listener Callback called whenever the return value of any
15841 * expression in `watchExpressions` changes
15842 * The `newValues` array contains the current values of the `watchExpressions`, with the indexes matching
15843 * those of `watchExpression`
15844 * and the `oldValues` array contains the previous values of the `watchExpressions`, with the indexes matching
15845 * those of `watchExpression`
15846 * The `scope` refers to the current scope.
15847 * @returns {function()} Returns a de-registration function for all listeners.
15849 $watchGroup: function(watchExpressions, listener) {
15850 var oldValues = new Array(watchExpressions.length);
15851 var newValues = new Array(watchExpressions.length);
15852 var deregisterFns = [];
15854 var changeReactionScheduled = false;
15855 var firstRun = true;
15857 if (!watchExpressions.length) {
15858 // No expressions means we call the listener ASAP
15859 var shouldCall = true;
15860 self.$evalAsync(function() {
15861 if (shouldCall) listener(newValues, newValues, self);
15863 return function deregisterWatchGroup() {
15864 shouldCall = false;
15868 if (watchExpressions.length === 1) {
15869 // Special case size of one
15870 return this.$watch(watchExpressions[0], function watchGroupAction(value, oldValue, scope) {
15871 newValues[0] = value;
15872 oldValues[0] = oldValue;
15873 listener(newValues, (value === oldValue) ? newValues : oldValues, scope);
15877 forEach(watchExpressions, function(expr, i) {
15878 var unwatchFn = self.$watch(expr, function watchGroupSubAction(value, oldValue) {
15879 newValues[i] = value;
15880 oldValues[i] = oldValue;
15881 if (!changeReactionScheduled) {
15882 changeReactionScheduled = true;
15883 self.$evalAsync(watchGroupAction);
15886 deregisterFns.push(unwatchFn);
15889 function watchGroupAction() {
15890 changeReactionScheduled = false;
15894 listener(newValues, newValues, self);
15896 listener(newValues, oldValues, self);
15900 return function deregisterWatchGroup() {
15901 while (deregisterFns.length) {
15902 deregisterFns.shift()();
15910 * @name $rootScope.Scope#$watchCollection
15914 * Shallow watches the properties of an object and fires whenever any of the properties change
15915 * (for arrays, this implies watching the array items; for object maps, this implies watching
15916 * the properties). If a change is detected, the `listener` callback is fired.
15918 * - The `obj` collection is observed via standard $watch operation and is examined on every
15919 * call to $digest() to see if any items have been added, removed, or moved.
15920 * - The `listener` is called whenever anything within the `obj` has changed. Examples include
15921 * adding, removing, and moving items belonging to an object or array.
15926 $scope.names = ['igor', 'matias', 'misko', 'james'];
15927 $scope.dataCount = 4;
15929 $scope.$watchCollection('names', function(newNames, oldNames) {
15930 $scope.dataCount = newNames.length;
15933 expect($scope.dataCount).toEqual(4);
15936 //still at 4 ... no changes
15937 expect($scope.dataCount).toEqual(4);
15939 $scope.names.pop();
15942 //now there's been a change
15943 expect($scope.dataCount).toEqual(3);
15947 * @param {string|function(scope)} obj Evaluated as {@link guide/expression expression}. The
15948 * expression value should evaluate to an object or an array which is observed on each
15949 * {@link ng.$rootScope.Scope#$digest $digest} cycle. Any shallow change within the
15950 * collection will trigger a call to the `listener`.
15952 * @param {function(newCollection, oldCollection, scope)} listener a callback function called
15953 * when a change is detected.
15954 * - The `newCollection` object is the newly modified data obtained from the `obj` expression
15955 * - The `oldCollection` object is a copy of the former collection data.
15956 * Due to performance considerations, the`oldCollection` value is computed only if the
15957 * `listener` function declares two or more arguments.
15958 * - The `scope` argument refers to the current scope.
15960 * @returns {function()} Returns a de-registration function for this listener. When the
15961 * de-registration function is executed, the internal watch operation is terminated.
15963 $watchCollection: function(obj, listener) {
15964 $watchCollectionInterceptor.$stateful = true;
15967 // the current value, updated on each dirty-check run
15969 // a shallow copy of the newValue from the last dirty-check run,
15970 // updated to match newValue during dirty-check run
15972 // a shallow copy of the newValue from when the last change happened
15974 // only track veryOldValue if the listener is asking for it
15975 var trackVeryOldValue = (listener.length > 1);
15976 var changeDetected = 0;
15977 var changeDetector = $parse(obj, $watchCollectionInterceptor);
15978 var internalArray = [];
15979 var internalObject = {};
15980 var initRun = true;
15983 function $watchCollectionInterceptor(_value) {
15985 var newLength, key, bothNaN, newItem, oldItem;
15987 // If the new value is undefined, then return undefined as the watch may be a one-time watch
15988 if (isUndefined(newValue)) return;
15990 if (!isObject(newValue)) { // if primitive
15991 if (oldValue !== newValue) {
15992 oldValue = newValue;
15995 } else if (isArrayLike(newValue)) {
15996 if (oldValue !== internalArray) {
15997 // we are transitioning from something which was not an array into array.
15998 oldValue = internalArray;
15999 oldLength = oldValue.length = 0;
16003 newLength = newValue.length;
16005 if (oldLength !== newLength) {
16006 // if lengths do not match we need to trigger change notification
16008 oldValue.length = oldLength = newLength;
16010 // copy the items to oldValue and look for changes.
16011 for (var i = 0; i < newLength; i++) {
16012 oldItem = oldValue[i];
16013 newItem = newValue[i];
16015 bothNaN = (oldItem !== oldItem) && (newItem !== newItem);
16016 if (!bothNaN && (oldItem !== newItem)) {
16018 oldValue[i] = newItem;
16022 if (oldValue !== internalObject) {
16023 // we are transitioning from something which was not an object into object.
16024 oldValue = internalObject = {};
16028 // copy the items to oldValue and look for changes.
16030 for (key in newValue) {
16031 if (hasOwnProperty.call(newValue, key)) {
16033 newItem = newValue[key];
16034 oldItem = oldValue[key];
16036 if (key in oldValue) {
16037 bothNaN = (oldItem !== oldItem) && (newItem !== newItem);
16038 if (!bothNaN && (oldItem !== newItem)) {
16040 oldValue[key] = newItem;
16044 oldValue[key] = newItem;
16049 if (oldLength > newLength) {
16050 // we used to have more keys, need to find them and destroy them.
16052 for (key in oldValue) {
16053 if (!hasOwnProperty.call(newValue, key)) {
16055 delete oldValue[key];
16060 return changeDetected;
16063 function $watchCollectionAction() {
16066 listener(newValue, newValue, self);
16068 listener(newValue, veryOldValue, self);
16071 // make a copy for the next time a collection is changed
16072 if (trackVeryOldValue) {
16073 if (!isObject(newValue)) {
16075 veryOldValue = newValue;
16076 } else if (isArrayLike(newValue)) {
16077 veryOldValue = new Array(newValue.length);
16078 for (var i = 0; i < newValue.length; i++) {
16079 veryOldValue[i] = newValue[i];
16081 } else { // if object
16083 for (var key in newValue) {
16084 if (hasOwnProperty.call(newValue, key)) {
16085 veryOldValue[key] = newValue[key];
16092 return this.$watch(changeDetector, $watchCollectionAction);
16097 * @name $rootScope.Scope#$digest
16101 * Processes all of the {@link ng.$rootScope.Scope#$watch watchers} of the current scope and
16102 * its children. Because a {@link ng.$rootScope.Scope#$watch watcher}'s listener can change
16103 * the model, the `$digest()` keeps calling the {@link ng.$rootScope.Scope#$watch watchers}
16104 * until no more listeners are firing. This means that it is possible to get into an infinite
16105 * loop. This function will throw `'Maximum iteration limit exceeded.'` if the number of
16106 * iterations exceeds 10.
16108 * Usually, you don't call `$digest()` directly in
16109 * {@link ng.directive:ngController controllers} or in
16110 * {@link ng.$compileProvider#directive directives}.
16111 * Instead, you should call {@link ng.$rootScope.Scope#$apply $apply()} (typically from within
16112 * a {@link ng.$compileProvider#directive directive}), which will force a `$digest()`.
16114 * If you want to be notified whenever `$digest()` is called,
16115 * you can register a `watchExpression` function with
16116 * {@link ng.$rootScope.Scope#$watch $watch()} with no `listener`.
16118 * In unit tests, you may need to call `$digest()` to simulate the scope life cycle.
16123 scope.name = 'misko';
16126 expect(scope.counter).toEqual(0);
16127 scope.$watch('name', function(newValue, oldValue) {
16128 scope.counter = scope.counter + 1;
16130 expect(scope.counter).toEqual(0);
16133 // the listener is always called during the first $digest loop after it was registered
16134 expect(scope.counter).toEqual(1);
16137 // but now it will not be called unless the value changes
16138 expect(scope.counter).toEqual(1);
16140 scope.name = 'adam';
16142 expect(scope.counter).toEqual(2);
16146 $digest: function() {
16147 var watch, value, last, fn, get,
16151 next, current, target = this,
16153 logIdx, logMsg, asyncTask;
16155 beginPhase('$digest');
16156 // Check for changes to browser url that happened in sync before the call to $digest
16157 $browser.$$checkUrlChange();
16159 if (this === $rootScope && applyAsyncId !== null) {
16160 // If this is the root scope, and $applyAsync has scheduled a deferred $apply(), then
16161 // cancel the scheduled $apply and flush the queue of expressions to be evaluated.
16162 $browser.defer.cancel(applyAsyncId);
16166 lastDirtyWatch = null;
16168 do { // "while dirty" loop
16172 while (asyncQueue.length) {
16174 asyncTask = asyncQueue.shift();
16175 asyncTask.scope.$eval(asyncTask.expression, asyncTask.locals);
16177 $exceptionHandler(e);
16179 lastDirtyWatch = null;
16182 traverseScopesLoop:
16183 do { // "traverse the scopes" loop
16184 if ((watchers = current.$$watchers)) {
16185 // process our watches
16186 length = watchers.length;
16189 watch = watchers[length];
16190 // Most common watches are on primitives, in which case we can short
16191 // circuit it with === operator, only when === fails do we use .equals
16194 if ((value = get(current)) !== (last = watch.last) &&
16196 ? equals(value, last)
16197 : (typeof value === 'number' && typeof last === 'number'
16198 && isNaN(value) && isNaN(last)))) {
16200 lastDirtyWatch = watch;
16201 watch.last = watch.eq ? copy(value, null) : value;
16203 fn(value, ((last === initWatchVal) ? value : last), current);
16206 if (!watchLog[logIdx]) watchLog[logIdx] = [];
16207 watchLog[logIdx].push({
16208 msg: isFunction(watch.exp) ? 'fn: ' + (watch.exp.name || watch.exp.toString()) : watch.exp,
16213 } else if (watch === lastDirtyWatch) {
16214 // If the most recently dirty watcher is now clean, short circuit since the remaining watchers
16215 // have already been tested.
16217 break traverseScopesLoop;
16221 $exceptionHandler(e);
16226 // Insanity Warning: scope depth-first traversal
16227 // yes, this code is a bit crazy, but it works and we have tests to prove it!
16228 // this piece should be kept in sync with the traversal in $broadcast
16229 if (!(next = ((current.$$watchersCount && current.$$childHead) ||
16230 (current !== target && current.$$nextSibling)))) {
16231 while (current !== target && !(next = current.$$nextSibling)) {
16232 current = current.$parent;
16235 } while ((current = next));
16237 // `break traverseScopesLoop;` takes us to here
16239 if ((dirty || asyncQueue.length) && !(ttl--)) {
16241 throw $rootScopeMinErr('infdig',
16242 '{0} $digest() iterations reached. Aborting!\n' +
16243 'Watchers fired in the last 5 iterations: {1}',
16247 } while (dirty || asyncQueue.length);
16251 while (postDigestQueue.length) {
16253 postDigestQueue.shift()();
16255 $exceptionHandler(e);
16263 * @name $rootScope.Scope#$destroy
16264 * @eventType broadcast on scope being destroyed
16267 * Broadcasted when a scope and its children are being destroyed.
16269 * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to
16270 * clean up DOM bindings before an element is removed from the DOM.
16275 * @name $rootScope.Scope#$destroy
16279 * Removes the current scope (and all of its children) from the parent scope. Removal implies
16280 * that calls to {@link ng.$rootScope.Scope#$digest $digest()} will no longer
16281 * propagate to the current scope and its children. Removal also implies that the current
16282 * scope is eligible for garbage collection.
16284 * The `$destroy()` is usually used by directives such as
16285 * {@link ng.directive:ngRepeat ngRepeat} for managing the
16286 * unrolling of the loop.
16288 * Just before a scope is destroyed, a `$destroy` event is broadcasted on this scope.
16289 * Application code can register a `$destroy` event handler that will give it a chance to
16290 * perform any necessary cleanup.
16292 * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to
16293 * clean up DOM bindings before an element is removed from the DOM.
16295 $destroy: function() {
16296 // We can't destroy a scope that has been already destroyed.
16297 if (this.$$destroyed) return;
16298 var parent = this.$parent;
16300 this.$broadcast('$destroy');
16301 this.$$destroyed = true;
16303 if (this === $rootScope) {
16304 //Remove handlers attached to window when $rootScope is removed
16305 $browser.$$applicationDestroyed();
16308 incrementWatchersCount(this, -this.$$watchersCount);
16309 for (var eventName in this.$$listenerCount) {
16310 decrementListenerCount(this, this.$$listenerCount[eventName], eventName);
16313 // sever all the references to parent scopes (after this cleanup, the current scope should
16314 // not be retained by any of our references and should be eligible for garbage collection)
16315 if (parent && parent.$$childHead == this) parent.$$childHead = this.$$nextSibling;
16316 if (parent && parent.$$childTail == this) parent.$$childTail = this.$$prevSibling;
16317 if (this.$$prevSibling) this.$$prevSibling.$$nextSibling = this.$$nextSibling;
16318 if (this.$$nextSibling) this.$$nextSibling.$$prevSibling = this.$$prevSibling;
16320 // Disable listeners, watchers and apply/digest methods
16321 this.$destroy = this.$digest = this.$apply = this.$evalAsync = this.$applyAsync = noop;
16322 this.$on = this.$watch = this.$watchGroup = function() { return noop; };
16323 this.$$listeners = {};
16325 // Disconnect the next sibling to prevent `cleanUpScope` destroying those too
16326 this.$$nextSibling = null;
16327 cleanUpScope(this);
16332 * @name $rootScope.Scope#$eval
16336 * Executes the `expression` on the current scope and returns the result. Any exceptions in
16337 * the expression are propagated (uncaught). This is useful when evaluating Angular
16342 var scope = ng.$rootScope.Scope();
16346 expect(scope.$eval('a+b')).toEqual(3);
16347 expect(scope.$eval(function(scope){ return scope.a + scope.b; })).toEqual(3);
16350 * @param {(string|function())=} expression An angular expression to be executed.
16352 * - `string`: execute using the rules as defined in {@link guide/expression expression}.
16353 * - `function(scope)`: execute the function with the current `scope` parameter.
16355 * @param {(object)=} locals Local variables object, useful for overriding values in scope.
16356 * @returns {*} The result of evaluating the expression.
16358 $eval: function(expr, locals) {
16359 return $parse(expr)(this, locals);
16364 * @name $rootScope.Scope#$evalAsync
16368 * Executes the expression on the current scope at a later point in time.
16370 * The `$evalAsync` makes no guarantees as to when the `expression` will be executed, only
16373 * - it will execute after the function that scheduled the evaluation (preferably before DOM
16375 * - at least one {@link ng.$rootScope.Scope#$digest $digest cycle} will be performed after
16376 * `expression` execution.
16378 * Any exceptions from the execution of the expression are forwarded to the
16379 * {@link ng.$exceptionHandler $exceptionHandler} service.
16381 * __Note:__ if this function is called outside of a `$digest` cycle, a new `$digest` cycle
16382 * will be scheduled. However, it is encouraged to always call code that changes the model
16383 * from within an `$apply` call. That includes code evaluated via `$evalAsync`.
16385 * @param {(string|function())=} expression An angular expression to be executed.
16387 * - `string`: execute using the rules as defined in {@link guide/expression expression}.
16388 * - `function(scope)`: execute the function with the current `scope` parameter.
16390 * @param {(object)=} locals Local variables object, useful for overriding values in scope.
16392 $evalAsync: function(expr, locals) {
16393 // if we are outside of an $digest loop and this is the first time we are scheduling async
16394 // task also schedule async auto-flush
16395 if (!$rootScope.$$phase && !asyncQueue.length) {
16396 $browser.defer(function() {
16397 if (asyncQueue.length) {
16398 $rootScope.$digest();
16403 asyncQueue.push({scope: this, expression: $parse(expr), locals: locals});
16406 $$postDigest: function(fn) {
16407 postDigestQueue.push(fn);
16412 * @name $rootScope.Scope#$apply
16416 * `$apply()` is used to execute an expression in angular from outside of the angular
16417 * framework. (For example from browser DOM events, setTimeout, XHR or third party libraries).
16418 * Because we are calling into the angular framework we need to perform proper scope life
16419 * cycle of {@link ng.$exceptionHandler exception handling},
16420 * {@link ng.$rootScope.Scope#$digest executing watches}.
16424 * # Pseudo-Code of `$apply()`
16426 function $apply(expr) {
16428 return $eval(expr);
16430 $exceptionHandler(e);
16438 * Scope's `$apply()` method transitions through the following stages:
16440 * 1. The {@link guide/expression expression} is executed using the
16441 * {@link ng.$rootScope.Scope#$eval $eval()} method.
16442 * 2. Any exceptions from the execution of the expression are forwarded to the
16443 * {@link ng.$exceptionHandler $exceptionHandler} service.
16444 * 3. The {@link ng.$rootScope.Scope#$watch watch} listeners are fired immediately after the
16445 * expression was executed using the {@link ng.$rootScope.Scope#$digest $digest()} method.
16448 * @param {(string|function())=} exp An angular expression to be executed.
16450 * - `string`: execute using the rules as defined in {@link guide/expression expression}.
16451 * - `function(scope)`: execute the function with current `scope` parameter.
16453 * @returns {*} The result of evaluating the expression.
16455 $apply: function(expr) {
16457 beginPhase('$apply');
16459 return this.$eval(expr);
16464 $exceptionHandler(e);
16467 $rootScope.$digest();
16469 $exceptionHandler(e);
16477 * @name $rootScope.Scope#$applyAsync
16481 * Schedule the invocation of $apply to occur at a later time. The actual time difference
16482 * varies across browsers, but is typically around ~10 milliseconds.
16484 * This can be used to queue up multiple expressions which need to be evaluated in the same
16487 * @param {(string|function())=} exp An angular expression to be executed.
16489 * - `string`: execute using the rules as defined in {@link guide/expression expression}.
16490 * - `function(scope)`: execute the function with current `scope` parameter.
16492 $applyAsync: function(expr) {
16494 expr && applyAsyncQueue.push($applyAsyncExpression);
16495 expr = $parse(expr);
16496 scheduleApplyAsync();
16498 function $applyAsyncExpression() {
16505 * @name $rootScope.Scope#$on
16509 * Listens on events of a given type. See {@link ng.$rootScope.Scope#$emit $emit} for
16510 * discussion of event life cycle.
16512 * The event listener function format is: `function(event, args...)`. The `event` object
16513 * passed into the listener has the following attributes:
16515 * - `targetScope` - `{Scope}`: the scope on which the event was `$emit`-ed or
16517 * - `currentScope` - `{Scope}`: the scope that is currently handling the event. Once the
16518 * event propagates through the scope hierarchy, this property is set to null.
16519 * - `name` - `{string}`: name of the event.
16520 * - `stopPropagation` - `{function=}`: calling `stopPropagation` function will cancel
16521 * further event propagation (available only for events that were `$emit`-ed).
16522 * - `preventDefault` - `{function}`: calling `preventDefault` sets `defaultPrevented` flag
16524 * - `defaultPrevented` - `{boolean}`: true if `preventDefault` was called.
16526 * @param {string} name Event name to listen on.
16527 * @param {function(event, ...args)} listener Function to call when the event is emitted.
16528 * @returns {function()} Returns a deregistration function for this listener.
16530 $on: function(name, listener) {
16531 var namedListeners = this.$$listeners[name];
16532 if (!namedListeners) {
16533 this.$$listeners[name] = namedListeners = [];
16535 namedListeners.push(listener);
16537 var current = this;
16539 if (!current.$$listenerCount[name]) {
16540 current.$$listenerCount[name] = 0;
16542 current.$$listenerCount[name]++;
16543 } while ((current = current.$parent));
16546 return function() {
16547 var indexOfListener = namedListeners.indexOf(listener);
16548 if (indexOfListener !== -1) {
16549 namedListeners[indexOfListener] = null;
16550 decrementListenerCount(self, 1, name);
16558 * @name $rootScope.Scope#$emit
16562 * Dispatches an event `name` upwards through the scope hierarchy notifying the
16563 * registered {@link ng.$rootScope.Scope#$on} listeners.
16565 * The event life cycle starts at the scope on which `$emit` was called. All
16566 * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get
16567 * notified. Afterwards, the event traverses upwards toward the root scope and calls all
16568 * registered listeners along the way. The event will stop propagating if one of the listeners
16571 * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
16572 * onto the {@link ng.$exceptionHandler $exceptionHandler} service.
16574 * @param {string} name Event name to emit.
16575 * @param {...*} args Optional one or more arguments which will be passed onto the event listeners.
16576 * @return {Object} Event object (see {@link ng.$rootScope.Scope#$on}).
16578 $emit: function(name, args) {
16582 stopPropagation = false,
16585 targetScope: scope,
16586 stopPropagation: function() {stopPropagation = true;},
16587 preventDefault: function() {
16588 event.defaultPrevented = true;
16590 defaultPrevented: false
16592 listenerArgs = concat([event], arguments, 1),
16596 namedListeners = scope.$$listeners[name] || empty;
16597 event.currentScope = scope;
16598 for (i = 0, length = namedListeners.length; i < length; i++) {
16600 // if listeners were deregistered, defragment the array
16601 if (!namedListeners[i]) {
16602 namedListeners.splice(i, 1);
16608 //allow all listeners attached to the current scope to run
16609 namedListeners[i].apply(null, listenerArgs);
16611 $exceptionHandler(e);
16614 //if any listener on the current scope stops propagation, prevent bubbling
16615 if (stopPropagation) {
16616 event.currentScope = null;
16620 scope = scope.$parent;
16623 event.currentScope = null;
16631 * @name $rootScope.Scope#$broadcast
16635 * Dispatches an event `name` downwards to all child scopes (and their children) notifying the
16636 * registered {@link ng.$rootScope.Scope#$on} listeners.
16638 * The event life cycle starts at the scope on which `$broadcast` was called. All
16639 * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get
16640 * notified. Afterwards, the event propagates to all direct and indirect scopes of the current
16641 * scope and calls all registered listeners along the way. The event cannot be canceled.
16643 * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
16644 * onto the {@link ng.$exceptionHandler $exceptionHandler} service.
16646 * @param {string} name Event name to broadcast.
16647 * @param {...*} args Optional one or more arguments which will be passed onto the event listeners.
16648 * @return {Object} Event object, see {@link ng.$rootScope.Scope#$on}
16650 $broadcast: function(name, args) {
16656 targetScope: target,
16657 preventDefault: function() {
16658 event.defaultPrevented = true;
16660 defaultPrevented: false
16663 if (!target.$$listenerCount[name]) return event;
16665 var listenerArgs = concat([event], arguments, 1),
16666 listeners, i, length;
16668 //down while you can, then up and next sibling or up and next sibling until back at root
16669 while ((current = next)) {
16670 event.currentScope = current;
16671 listeners = current.$$listeners[name] || [];
16672 for (i = 0, length = listeners.length; i < length; i++) {
16673 // if listeners were deregistered, defragment the array
16674 if (!listeners[i]) {
16675 listeners.splice(i, 1);
16682 listeners[i].apply(null, listenerArgs);
16684 $exceptionHandler(e);
16688 // Insanity Warning: scope depth-first traversal
16689 // yes, this code is a bit crazy, but it works and we have tests to prove it!
16690 // this piece should be kept in sync with the traversal in $digest
16691 // (though it differs due to having the extra check for $$listenerCount)
16692 if (!(next = ((current.$$listenerCount[name] && current.$$childHead) ||
16693 (current !== target && current.$$nextSibling)))) {
16694 while (current !== target && !(next = current.$$nextSibling)) {
16695 current = current.$parent;
16700 event.currentScope = null;
16705 var $rootScope = new Scope();
16707 //The internal queues. Expose them on the $rootScope for debugging/testing purposes.
16708 var asyncQueue = $rootScope.$$asyncQueue = [];
16709 var postDigestQueue = $rootScope.$$postDigestQueue = [];
16710 var applyAsyncQueue = $rootScope.$$applyAsyncQueue = [];
16715 function beginPhase(phase) {
16716 if ($rootScope.$$phase) {
16717 throw $rootScopeMinErr('inprog', '{0} already in progress', $rootScope.$$phase);
16720 $rootScope.$$phase = phase;
16723 function clearPhase() {
16724 $rootScope.$$phase = null;
16727 function incrementWatchersCount(current, count) {
16729 current.$$watchersCount += count;
16730 } while ((current = current.$parent));
16733 function decrementListenerCount(current, count, name) {
16735 current.$$listenerCount[name] -= count;
16737 if (current.$$listenerCount[name] === 0) {
16738 delete current.$$listenerCount[name];
16740 } while ((current = current.$parent));
16744 * function used as an initial value for watchers.
16745 * because it's unique we can easily tell it apart from other values
16747 function initWatchVal() {}
16749 function flushApplyAsync() {
16750 while (applyAsyncQueue.length) {
16752 applyAsyncQueue.shift()();
16754 $exceptionHandler(e);
16757 applyAsyncId = null;
16760 function scheduleApplyAsync() {
16761 if (applyAsyncId === null) {
16762 applyAsyncId = $browser.defer(function() {
16763 $rootScope.$apply(flushApplyAsync);
16772 * @name $rootElement
16775 * The root element of Angular application. This is either the element where {@link
16776 * ng.directive:ngApp ngApp} was declared or the element passed into
16777 * {@link angular.bootstrap}. The element represents the root element of application. It is also the
16778 * location where the application's {@link auto.$injector $injector} service gets
16779 * published, and can be retrieved using `$rootElement.injector()`.
16783 // the implementation is in angular.bootstrap
16787 * Private service to sanitize uris for links and images. Used by $compile and $sanitize.
16789 function $$SanitizeUriProvider() {
16790 var aHrefSanitizationWhitelist = /^\s*(https?|ftp|mailto|tel|file):/,
16791 imgSrcSanitizationWhitelist = /^\s*((https?|ftp|file|blob):|data:image\/)/;
16795 * Retrieves or overrides the default regular expression that is used for whitelisting of safe
16796 * urls during a[href] sanitization.
16798 * The sanitization is a security measure aimed at prevent XSS attacks via html links.
16800 * Any url about to be assigned to a[href] via data-binding is first normalized and turned into
16801 * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist`
16802 * regular expression. If a match is found, the original url is written into the dom. Otherwise,
16803 * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
16805 * @param {RegExp=} regexp New regexp to whitelist urls with.
16806 * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
16807 * chaining otherwise.
16809 this.aHrefSanitizationWhitelist = function(regexp) {
16810 if (isDefined(regexp)) {
16811 aHrefSanitizationWhitelist = regexp;
16814 return aHrefSanitizationWhitelist;
16820 * Retrieves or overrides the default regular expression that is used for whitelisting of safe
16821 * urls during img[src] sanitization.
16823 * The sanitization is a security measure aimed at prevent XSS attacks via html links.
16825 * Any url about to be assigned to img[src] via data-binding is first normalized and turned into
16826 * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist`
16827 * regular expression. If a match is found, the original url is written into the dom. Otherwise,
16828 * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
16830 * @param {RegExp=} regexp New regexp to whitelist urls with.
16831 * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
16832 * chaining otherwise.
16834 this.imgSrcSanitizationWhitelist = function(regexp) {
16835 if (isDefined(regexp)) {
16836 imgSrcSanitizationWhitelist = regexp;
16839 return imgSrcSanitizationWhitelist;
16842 this.$get = function() {
16843 return function sanitizeUri(uri, isImage) {
16844 var regex = isImage ? imgSrcSanitizationWhitelist : aHrefSanitizationWhitelist;
16846 normalizedVal = urlResolve(uri).href;
16847 if (normalizedVal !== '' && !normalizedVal.match(regex)) {
16848 return 'unsafe:' + normalizedVal;
16855 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
16856 * Any commits to this file should be reviewed with security in mind. *
16857 * Changes to this file can potentially create security vulnerabilities. *
16858 * An approval from 2 Core members with history of modifying *
16859 * this file is required. *
16861 * Does the change somehow allow for arbitrary javascript to be executed? *
16862 * Or allows for someone to change the prototype of built-in objects? *
16863 * Or gives undesired access to variables likes document or window? *
16864 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
16866 var $sceMinErr = minErr('$sce');
16868 var SCE_CONTEXTS = {
16872 // RESOURCE_URL is a subtype of URL used in contexts where a privileged resource is sourced from a
16873 // url. (e.g. ng-include, script src, templateUrl)
16874 RESOURCE_URL: 'resourceUrl',
16878 // Helper functions follow.
16880 function adjustMatcher(matcher) {
16881 if (matcher === 'self') {
16883 } else if (isString(matcher)) {
16884 // Strings match exactly except for 2 wildcards - '*' and '**'.
16885 // '*' matches any character except those from the set ':/.?&'.
16886 // '**' matches any character (like .* in a RegExp).
16887 // More than 2 *'s raises an error as it's ill defined.
16888 if (matcher.indexOf('***') > -1) {
16889 throw $sceMinErr('iwcard',
16890 'Illegal sequence *** in string matcher. String: {0}', matcher);
16892 matcher = escapeForRegexp(matcher).
16893 replace('\\*\\*', '.*').
16894 replace('\\*', '[^:/.?&;]*');
16895 return new RegExp('^' + matcher + '$');
16896 } else if (isRegExp(matcher)) {
16897 // The only other type of matcher allowed is a Regexp.
16898 // Match entire URL / disallow partial matches.
16899 // Flags are reset (i.e. no global, ignoreCase or multiline)
16900 return new RegExp('^' + matcher.source + '$');
16902 throw $sceMinErr('imatcher',
16903 'Matchers may only be "self", string patterns or RegExp objects');
16908 function adjustMatchers(matchers) {
16909 var adjustedMatchers = [];
16910 if (isDefined(matchers)) {
16911 forEach(matchers, function(matcher) {
16912 adjustedMatchers.push(adjustMatcher(matcher));
16915 return adjustedMatchers;
16921 * @name $sceDelegate
16926 * `$sceDelegate` is a service that is used by the `$sce` service to provide {@link ng.$sce Strict
16927 * Contextual Escaping (SCE)} services to AngularJS.
16929 * Typically, you would configure or override the {@link ng.$sceDelegate $sceDelegate} instead of
16930 * the `$sce` service to customize the way Strict Contextual Escaping works in AngularJS. This is
16931 * because, while the `$sce` provides numerous shorthand methods, etc., you really only need to
16932 * override 3 core functions (`trustAs`, `getTrusted` and `valueOf`) to replace the way things
16933 * work because `$sce` delegates to `$sceDelegate` for these operations.
16935 * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} to configure this service.
16937 * The default instance of `$sceDelegate` should work out of the box with little pain. While you
16938 * can override it completely to change the behavior of `$sce`, the common case would
16939 * involve configuring the {@link ng.$sceDelegateProvider $sceDelegateProvider} instead by setting
16940 * your own whitelists and blacklists for trusting URLs used for loading AngularJS resources such as
16941 * templates. Refer {@link ng.$sceDelegateProvider#resourceUrlWhitelist
16942 * $sceDelegateProvider.resourceUrlWhitelist} and {@link
16943 * ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist}
16948 * @name $sceDelegateProvider
16951 * The `$sceDelegateProvider` provider allows developers to configure the {@link ng.$sceDelegate
16952 * $sceDelegate} service. This allows one to get/set the whitelists and blacklists used to ensure
16953 * that the URLs used for sourcing Angular templates are safe. Refer {@link
16954 * ng.$sceDelegateProvider#resourceUrlWhitelist $sceDelegateProvider.resourceUrlWhitelist} and
16955 * {@link ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist}
16957 * For the general details about this service in Angular, read the main page for {@link ng.$sce
16958 * Strict Contextual Escaping (SCE)}.
16960 * **Example**: Consider the following case. <a name="example"></a>
16962 * - your app is hosted at url `http://myapp.example.com/`
16963 * - but some of your templates are hosted on other domains you control such as
16964 * `http://srv01.assets.example.com/`, `http://srv02.assets.example.com/`, etc.
16965 * - and you have an open redirect at `http://myapp.example.com/clickThru?...`.
16967 * Here is what a secure configuration for this scenario might look like:
16970 * angular.module('myApp', []).config(function($sceDelegateProvider) {
16971 * $sceDelegateProvider.resourceUrlWhitelist([
16972 * // Allow same origin resource loads.
16974 * // Allow loading from our assets domain. Notice the difference between * and **.
16975 * 'http://srv*.assets.example.com/**'
16978 * // The blacklist overrides the whitelist so the open redirect here is blocked.
16979 * $sceDelegateProvider.resourceUrlBlacklist([
16980 * 'http://myapp.example.com/clickThru**'
16986 function $SceDelegateProvider() {
16987 this.SCE_CONTEXTS = SCE_CONTEXTS;
16989 // Resource URLs can also be trusted by policy.
16990 var resourceUrlWhitelist = ['self'],
16991 resourceUrlBlacklist = [];
16995 * @name $sceDelegateProvider#resourceUrlWhitelist
16998 * @param {Array=} whitelist When provided, replaces the resourceUrlWhitelist with the value
16999 * provided. This must be an array or null. A snapshot of this array is used so further
17000 * changes to the array are ignored.
17002 * Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
17003 * allowed in this array.
17005 * <div class="alert alert-warning">
17006 * **Note:** an empty whitelist array will block all URLs!
17009 * @return {Array} the currently set whitelist array.
17011 * The **default value** when no whitelist has been explicitly set is `['self']` allowing only
17012 * same origin resource requests.
17015 * Sets/Gets the whitelist of trusted resource URLs.
17017 this.resourceUrlWhitelist = function(value) {
17018 if (arguments.length) {
17019 resourceUrlWhitelist = adjustMatchers(value);
17021 return resourceUrlWhitelist;
17026 * @name $sceDelegateProvider#resourceUrlBlacklist
17029 * @param {Array=} blacklist When provided, replaces the resourceUrlBlacklist with the value
17030 * provided. This must be an array or null. A snapshot of this array is used so further
17031 * changes to the array are ignored.
17033 * Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
17034 * allowed in this array.
17036 * The typical usage for the blacklist is to **block
17037 * [open redirects](http://cwe.mitre.org/data/definitions/601.html)** served by your domain as
17038 * these would otherwise be trusted but actually return content from the redirected domain.
17040 * Finally, **the blacklist overrides the whitelist** and has the final say.
17042 * @return {Array} the currently set blacklist array.
17044 * The **default value** when no whitelist has been explicitly set is the empty array (i.e. there
17045 * is no blacklist.)
17048 * Sets/Gets the blacklist of trusted resource URLs.
17051 this.resourceUrlBlacklist = function(value) {
17052 if (arguments.length) {
17053 resourceUrlBlacklist = adjustMatchers(value);
17055 return resourceUrlBlacklist;
17058 this.$get = ['$injector', function($injector) {
17060 var htmlSanitizer = function htmlSanitizer(html) {
17061 throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.');
17064 if ($injector.has('$sanitize')) {
17065 htmlSanitizer = $injector.get('$sanitize');
17069 function matchUrl(matcher, parsedUrl) {
17070 if (matcher === 'self') {
17071 return urlIsSameOrigin(parsedUrl);
17073 // definitely a regex. See adjustMatchers()
17074 return !!matcher.exec(parsedUrl.href);
17078 function isResourceUrlAllowedByPolicy(url) {
17079 var parsedUrl = urlResolve(url.toString());
17080 var i, n, allowed = false;
17081 // Ensure that at least one item from the whitelist allows this url.
17082 for (i = 0, n = resourceUrlWhitelist.length; i < n; i++) {
17083 if (matchUrl(resourceUrlWhitelist[i], parsedUrl)) {
17089 // Ensure that no item from the blacklist blocked this url.
17090 for (i = 0, n = resourceUrlBlacklist.length; i < n; i++) {
17091 if (matchUrl(resourceUrlBlacklist[i], parsedUrl)) {
17100 function generateHolderType(Base) {
17101 var holderType = function TrustedValueHolderType(trustedValue) {
17102 this.$$unwrapTrustedValue = function() {
17103 return trustedValue;
17107 holderType.prototype = new Base();
17109 holderType.prototype.valueOf = function sceValueOf() {
17110 return this.$$unwrapTrustedValue();
17112 holderType.prototype.toString = function sceToString() {
17113 return this.$$unwrapTrustedValue().toString();
17118 var trustedValueHolderBase = generateHolderType(),
17121 byType[SCE_CONTEXTS.HTML] = generateHolderType(trustedValueHolderBase);
17122 byType[SCE_CONTEXTS.CSS] = generateHolderType(trustedValueHolderBase);
17123 byType[SCE_CONTEXTS.URL] = generateHolderType(trustedValueHolderBase);
17124 byType[SCE_CONTEXTS.JS] = generateHolderType(trustedValueHolderBase);
17125 byType[SCE_CONTEXTS.RESOURCE_URL] = generateHolderType(byType[SCE_CONTEXTS.URL]);
17129 * @name $sceDelegate#trustAs
17132 * Returns an object that is trusted by angular for use in specified strict
17133 * contextual escaping contexts (such as ng-bind-html, ng-include, any src
17134 * attribute interpolation, any dom event binding attribute interpolation
17135 * such as for onclick, etc.) that uses the provided value.
17136 * See {@link ng.$sce $sce} for enabling strict contextual escaping.
17138 * @param {string} type The kind of context in which this value is safe for use. e.g. url,
17139 * resourceUrl, html, js and css.
17140 * @param {*} value The value that that should be considered trusted/safe.
17141 * @returns {*} A value that can be used to stand in for the provided `value` in places
17142 * where Angular expects a $sce.trustAs() return value.
17144 function trustAs(type, trustedValue) {
17145 var Constructor = (byType.hasOwnProperty(type) ? byType[type] : null);
17146 if (!Constructor) {
17147 throw $sceMinErr('icontext',
17148 'Attempted to trust a value in invalid context. Context: {0}; Value: {1}',
17149 type, trustedValue);
17151 if (trustedValue === null || isUndefined(trustedValue) || trustedValue === '') {
17152 return trustedValue;
17154 // All the current contexts in SCE_CONTEXTS happen to be strings. In order to avoid trusting
17155 // mutable objects, we ensure here that the value passed in is actually a string.
17156 if (typeof trustedValue !== 'string') {
17157 throw $sceMinErr('itype',
17158 'Attempted to trust a non-string value in a content requiring a string: Context: {0}',
17161 return new Constructor(trustedValue);
17166 * @name $sceDelegate#valueOf
17169 * If the passed parameter had been returned by a prior call to {@link ng.$sceDelegate#trustAs
17170 * `$sceDelegate.trustAs`}, returns the value that had been passed to {@link
17171 * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}.
17173 * If the passed parameter is not a value that had been returned by {@link
17174 * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}, returns it as-is.
17176 * @param {*} value The result of a prior {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}
17177 * call or anything else.
17178 * @returns {*} The `value` that was originally provided to {@link ng.$sceDelegate#trustAs
17179 * `$sceDelegate.trustAs`} if `value` is the result of such a call. Otherwise, returns
17180 * `value` unchanged.
17182 function valueOf(maybeTrusted) {
17183 if (maybeTrusted instanceof trustedValueHolderBase) {
17184 return maybeTrusted.$$unwrapTrustedValue();
17186 return maybeTrusted;
17192 * @name $sceDelegate#getTrusted
17195 * Takes the result of a {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`} call and
17196 * returns the originally supplied value if the queried context type is a supertype of the
17197 * created type. If this condition isn't satisfied, throws an exception.
17199 * @param {string} type The kind of context in which this value is to be used.
17200 * @param {*} maybeTrusted The result of a prior {@link ng.$sceDelegate#trustAs
17201 * `$sceDelegate.trustAs`} call.
17202 * @returns {*} The value the was originally provided to {@link ng.$sceDelegate#trustAs
17203 * `$sceDelegate.trustAs`} if valid in this context. Otherwise, throws an exception.
17205 function getTrusted(type, maybeTrusted) {
17206 if (maybeTrusted === null || isUndefined(maybeTrusted) || maybeTrusted === '') {
17207 return maybeTrusted;
17209 var constructor = (byType.hasOwnProperty(type) ? byType[type] : null);
17210 if (constructor && maybeTrusted instanceof constructor) {
17211 return maybeTrusted.$$unwrapTrustedValue();
17213 // If we get here, then we may only take one of two actions.
17214 // 1. sanitize the value for the requested type, or
17215 // 2. throw an exception.
17216 if (type === SCE_CONTEXTS.RESOURCE_URL) {
17217 if (isResourceUrlAllowedByPolicy(maybeTrusted)) {
17218 return maybeTrusted;
17220 throw $sceMinErr('insecurl',
17221 'Blocked loading resource from url not allowed by $sceDelegate policy. URL: {0}',
17222 maybeTrusted.toString());
17224 } else if (type === SCE_CONTEXTS.HTML) {
17225 return htmlSanitizer(maybeTrusted);
17227 throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.');
17230 return { trustAs: trustAs,
17231 getTrusted: getTrusted,
17232 valueOf: valueOf };
17239 * @name $sceProvider
17242 * The $sceProvider provider allows developers to configure the {@link ng.$sce $sce} service.
17243 * - enable/disable Strict Contextual Escaping (SCE) in a module
17244 * - override the default implementation with a custom delegate
17246 * Read more about {@link ng.$sce Strict Contextual Escaping (SCE)}.
17249 /* jshint maxlen: false*/
17258 * `$sce` is a service that provides Strict Contextual Escaping services to AngularJS.
17260 * # Strict Contextual Escaping
17262 * Strict Contextual Escaping (SCE) is a mode in which AngularJS requires bindings in certain
17263 * contexts to result in a value that is marked as safe to use for that context. One example of
17264 * such a context is binding arbitrary html controlled by the user via `ng-bind-html`. We refer
17265 * to these contexts as privileged or SCE contexts.
17267 * As of version 1.2, Angular ships with SCE enabled by default.
17269 * Note: When enabled (the default), IE<11 in quirks mode is not supported. In this mode, IE<11 allow
17270 * one to execute arbitrary javascript by the use of the expression() syntax. Refer
17271 * <http://blogs.msdn.com/b/ie/archive/2008/10/16/ending-expressions.aspx> to learn more about them.
17272 * You can ensure your document is in standards mode and not quirks mode by adding `<!doctype html>`
17273 * to the top of your HTML document.
17275 * SCE assists in writing code in way that (a) is secure by default and (b) makes auditing for
17276 * security vulnerabilities such as XSS, clickjacking, etc. a lot easier.
17278 * Here's an example of a binding in a privileged context:
17281 * <input ng-model="userHtml" aria-label="User input">
17282 * <div ng-bind-html="userHtml"></div>
17285 * Notice that `ng-bind-html` is bound to `userHtml` controlled by the user. With SCE
17286 * disabled, this application allows the user to render arbitrary HTML into the DIV.
17287 * In a more realistic example, one may be rendering user comments, blog articles, etc. via
17288 * bindings. (HTML is just one example of a context where rendering user controlled input creates
17289 * security vulnerabilities.)
17291 * For the case of HTML, you might use a library, either on the client side, or on the server side,
17292 * to sanitize unsafe HTML before binding to the value and rendering it in the document.
17294 * How would you ensure that every place that used these types of bindings was bound to a value that
17295 * was sanitized by your library (or returned as safe for rendering by your server?) How can you
17296 * ensure that you didn't accidentally delete the line that sanitized the value, or renamed some
17297 * properties/fields and forgot to update the binding to the sanitized value?
17299 * To be secure by default, you want to ensure that any such bindings are disallowed unless you can
17300 * determine that something explicitly says it's safe to use a value for binding in that
17301 * context. You can then audit your code (a simple grep would do) to ensure that this is only done
17302 * for those values that you can easily tell are safe - because they were received from your server,
17303 * sanitized by your library, etc. You can organize your codebase to help with this - perhaps
17304 * allowing only the files in a specific directory to do this. Ensuring that the internal API
17305 * exposed by that code doesn't markup arbitrary values as safe then becomes a more manageable task.
17307 * In the case of AngularJS' SCE service, one uses {@link ng.$sce#trustAs $sce.trustAs}
17308 * (and shorthand methods such as {@link ng.$sce#trustAsHtml $sce.trustAsHtml}, etc.) to
17309 * obtain values that will be accepted by SCE / privileged contexts.
17312 * ## How does it work?
17314 * In privileged contexts, directives and code will bind to the result of {@link ng.$sce#getTrusted
17315 * $sce.getTrusted(context, value)} rather than to the value directly. Directives use {@link
17316 * ng.$sce#parseAs $sce.parseAs} rather than `$parse` to watch attribute bindings, which performs the
17317 * {@link ng.$sce#getTrusted $sce.getTrusted} behind the scenes on non-constant literals.
17319 * As an example, {@link ng.directive:ngBindHtml ngBindHtml} uses {@link
17320 * ng.$sce#parseAsHtml $sce.parseAsHtml(binding expression)}. Here's the actual code (slightly
17324 * var ngBindHtmlDirective = ['$sce', function($sce) {
17325 * return function(scope, element, attr) {
17326 * scope.$watch($sce.parseAsHtml(attr.ngBindHtml), function(value) {
17327 * element.html(value || '');
17333 * ## Impact on loading templates
17335 * This applies both to the {@link ng.directive:ngInclude `ng-include`} directive as well as
17336 * `templateUrl`'s specified by {@link guide/directive directives}.
17338 * By default, Angular only loads templates from the same domain and protocol as the application
17339 * document. This is done by calling {@link ng.$sce#getTrustedResourceUrl
17340 * $sce.getTrustedResourceUrl} on the template URL. To load templates from other domains and/or
17341 * protocols, you may either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist
17342 * them} or {@link ng.$sce#trustAsResourceUrl wrap it} into a trusted value.
17346 * [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest)
17347 * and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/)
17348 * policy apply in addition to this and may further restrict whether the template is successfully
17349 * loaded. This means that without the right CORS policy, loading templates from a different domain
17350 * won't work on all browsers. Also, loading templates from `file://` URL does not work on some
17353 * ## This feels like too much overhead
17355 * It's important to remember that SCE only applies to interpolation expressions.
17357 * If your expressions are constant literals, they're automatically trusted and you don't need to
17358 * call `$sce.trustAs` on them (remember to include the `ngSanitize` module) (e.g.
17359 * `<div ng-bind-html="'<b>implicitly trusted</b>'"></div>`) just works.
17361 * Additionally, `a[href]` and `img[src]` automatically sanitize their URLs and do not pass them
17362 * through {@link ng.$sce#getTrusted $sce.getTrusted}. SCE doesn't play a role here.
17364 * The included {@link ng.$sceDelegate $sceDelegate} comes with sane defaults to allow you to load
17365 * templates in `ng-include` from your application's domain without having to even know about SCE.
17366 * It blocks loading templates from other domains or loading templates over http from an https
17367 * served document. You can change these by setting your own custom {@link
17368 * ng.$sceDelegateProvider#resourceUrlWhitelist whitelists} and {@link
17369 * ng.$sceDelegateProvider#resourceUrlBlacklist blacklists} for matching such URLs.
17371 * This significantly reduces the overhead. It is far easier to pay the small overhead and have an
17372 * application that's secure and can be audited to verify that with much more ease than bolting
17373 * security onto an application later.
17375 * <a name="contexts"></a>
17376 * ## What trusted context types are supported?
17378 * | Context | Notes |
17379 * |---------------------|----------------|
17380 * | `$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. |
17381 * | `$sce.CSS` | For CSS that's safe to source into the application. Currently unused. Feel free to use it in your own directives. |
17382 * | `$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. |
17383 * | `$sce.RESOURCE_URL` | For URLs that are not only safe to follow as links, but whose contents are also safe to include in your application. Examples include `ng-include`, `src` / `ngSrc` bindings for tags other than `IMG` (e.g. `IFRAME`, `OBJECT`, etc.) <br><br>Note that `$sce.RESOURCE_URL` makes a stronger statement about the URL than `$sce.URL` does and therefore contexts requiring values trusted for `$sce.RESOURCE_URL` can be used anywhere that values trusted for `$sce.URL` are required. |
17384 * | `$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. |
17386 * ## Format of items in {@link ng.$sceDelegateProvider#resourceUrlWhitelist resourceUrlWhitelist}/{@link ng.$sceDelegateProvider#resourceUrlBlacklist Blacklist} <a name="resourceUrlPatternItem"></a>
17388 * Each element in these arrays must be one of the following:
17391 * - The special **string**, `'self'`, can be used to match against all URLs of the **same
17392 * domain** as the application document using the **same protocol**.
17393 * - **String** (except the special value `'self'`)
17394 * - The string is matched against the full *normalized / absolute URL* of the resource
17395 * being tested (substring matches are not good enough.)
17396 * - There are exactly **two wildcard sequences** - `*` and `**`. All other characters
17397 * match themselves.
17398 * - `*`: matches zero or more occurrences of any character other than one of the following 6
17399 * characters: '`:`', '`/`', '`.`', '`?`', '`&`' and '`;`'. It's a useful wildcard for use
17401 * - `**`: matches zero or more occurrences of *any* character. As such, it's not
17402 * appropriate for use in a scheme, domain, etc. as it would match too much. (e.g.
17403 * http://**.example.com/ would match http://evil.com/?ignore=.example.com/ and that might
17404 * not have been the intention.) Its usage at the very end of the path is ok. (e.g.
17405 * http://foo.example.com/templates/**).
17406 * - **RegExp** (*see caveat below*)
17407 * - *Caveat*: While regular expressions are powerful and offer great flexibility, their syntax
17408 * (and all the inevitable escaping) makes them *harder to maintain*. It's easy to
17409 * accidentally introduce a bug when one updates a complex expression (imho, all regexes should
17410 * have good test coverage). For instance, the use of `.` in the regex is correct only in a
17411 * small number of cases. A `.` character in the regex used when matching the scheme or a
17412 * subdomain could be matched against a `:` or literal `.` that was likely not intended. It
17413 * is highly recommended to use the string patterns and only fall back to regular expressions
17414 * as a last resort.
17415 * - The regular expression must be an instance of RegExp (i.e. not a string.) It is
17416 * matched against the **entire** *normalized / absolute URL* of the resource being tested
17417 * (even when the RegExp did not have the `^` and `$` codes.) In addition, any flags
17418 * present on the RegExp (such as multiline, global, ignoreCase) are ignored.
17419 * - If you are generating your JavaScript from some other templating engine (not
17420 * recommended, e.g. in issue [#4006](https://github.com/angular/angular.js/issues/4006)),
17421 * remember to escape your regular expression (and be aware that you might need more than
17422 * one level of escaping depending on your templating engine and the way you interpolated
17423 * the value.) Do make use of your platform's escaping mechanism as it might be good
17424 * enough before coding your own. E.g. Ruby has
17425 * [Regexp.escape(str)](http://www.ruby-doc.org/core-2.0.0/Regexp.html#method-c-escape)
17426 * and Python has [re.escape](http://docs.python.org/library/re.html#re.escape).
17427 * Javascript lacks a similar built in function for escaping. Take a look at Google
17428 * Closure library's [goog.string.regExpEscape(s)](
17429 * http://docs.closure-library.googlecode.com/git/closure_goog_string_string.js.source.html#line962).
17431 * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} for an example.
17433 * ## Show me an example using SCE.
17435 * <example module="mySceApp" deps="angular-sanitize.js">
17436 * <file name="index.html">
17437 * <div ng-controller="AppController as myCtrl">
17438 * <i ng-bind-html="myCtrl.explicitlyTrustedHtml" id="explicitlyTrustedHtml"></i><br><br>
17439 * <b>User comments</b><br>
17440 * By default, HTML that isn't explicitly trusted (e.g. Alice's comment) is sanitized when
17441 * $sanitize is available. If $sanitize isn't available, this results in an error instead of an
17443 * <div class="well">
17444 * <div ng-repeat="userComment in myCtrl.userComments">
17445 * <b>{{userComment.name}}</b>:
17446 * <span ng-bind-html="userComment.htmlComment" class="htmlComment"></span>
17453 * <file name="script.js">
17454 * angular.module('mySceApp', ['ngSanitize'])
17455 * .controller('AppController', ['$http', '$templateCache', '$sce',
17456 * function($http, $templateCache, $sce) {
17458 * $http.get("test_data.json", {cache: $templateCache}).success(function(userComments) {
17459 * self.userComments = userComments;
17461 * self.explicitlyTrustedHtml = $sce.trustAsHtml(
17462 * '<span onmouseover="this.textContent="Explicitly trusted HTML bypasses ' +
17463 * 'sanitization."">Hover over this text.</span>');
17467 * <file name="test_data.json">
17469 * { "name": "Alice",
17471 * "<span onmouseover='this.textContent=\"PWN3D!\"'>Is <i>anyone</i> reading this?</span>"
17474 * "htmlComment": "<i>Yes!</i> Am I the only other one?"
17479 * <file name="protractor.js" type="protractor">
17480 * describe('SCE doc demo', function() {
17481 * it('should sanitize untrusted values', function() {
17482 * expect(element.all(by.css('.htmlComment')).first().getInnerHtml())
17483 * .toBe('<span>Is <i>anyone</i> reading this?</span>');
17486 * it('should NOT sanitize explicitly trusted values', function() {
17487 * expect(element(by.id('explicitlyTrustedHtml')).getInnerHtml()).toBe(
17488 * '<span onmouseover="this.textContent="Explicitly trusted HTML bypasses ' +
17489 * 'sanitization."">Hover over this text.</span>');
17497 * ## Can I disable SCE completely?
17499 * Yes, you can. However, this is strongly discouraged. SCE gives you a lot of security benefits
17500 * for little coding overhead. It will be much harder to take an SCE disabled application and
17501 * either secure it on your own or enable SCE at a later stage. It might make sense to disable SCE
17502 * for cases where you have a lot of existing code that was written before SCE was introduced and
17503 * you're migrating them a module at a time.
17505 * That said, here's how you can completely disable SCE:
17508 * angular.module('myAppWithSceDisabledmyApp', []).config(function($sceProvider) {
17509 * // Completely disable SCE. For demonstration purposes only!
17510 * // Do not use in new projects.
17511 * $sceProvider.enabled(false);
17516 /* jshint maxlen: 100 */
17518 function $SceProvider() {
17519 var enabled = true;
17523 * @name $sceProvider#enabled
17526 * @param {boolean=} value If provided, then enables/disables SCE.
17527 * @return {boolean} true if SCE is enabled, false otherwise.
17530 * Enables/disables SCE and returns the current value.
17532 this.enabled = function(value) {
17533 if (arguments.length) {
17540 /* Design notes on the default implementation for SCE.
17542 * The API contract for the SCE delegate
17543 * -------------------------------------
17544 * The SCE delegate object must provide the following 3 methods:
17546 * - trustAs(contextEnum, value)
17547 * This method is used to tell the SCE service that the provided value is OK to use in the
17548 * contexts specified by contextEnum. It must return an object that will be accepted by
17549 * getTrusted() for a compatible contextEnum and return this value.
17552 * For values that were not produced by trustAs(), return them as is. For values that were
17553 * produced by trustAs(), return the corresponding input value to trustAs. Basically, if
17554 * trustAs is wrapping the given values into some type, this operation unwraps it when given
17557 * - getTrusted(contextEnum, value)
17558 * This function should return the a value that is safe to use in the context specified by
17559 * contextEnum or throw and exception otherwise.
17561 * NOTE: This contract deliberately does NOT state that values returned by trustAs() must be
17562 * opaque or wrapped in some holder object. That happens to be an implementation detail. For
17563 * instance, an implementation could maintain a registry of all trusted objects by context. In
17564 * such a case, trustAs() would return the same object that was passed in. getTrusted() would
17565 * return the same object passed in if it was found in the registry under a compatible context or
17566 * throw an exception otherwise. An implementation might only wrap values some of the time based
17567 * on some criteria. getTrusted() might return a value and not throw an exception for special
17568 * constants or objects even if not wrapped. All such implementations fulfill this contract.
17571 * A note on the inheritance model for SCE contexts
17572 * ------------------------------------------------
17573 * I've used inheritance and made RESOURCE_URL wrapped types a subtype of URL wrapped types. This
17574 * is purely an implementation details.
17576 * The contract is simply this:
17578 * getTrusted($sce.RESOURCE_URL, value) succeeding implies that getTrusted($sce.URL, value)
17579 * will also succeed.
17581 * Inheritance happens to capture this in a natural way. In some future, we
17582 * may not use inheritance anymore. That is OK because no code outside of
17583 * sce.js and sceSpecs.js would need to be aware of this detail.
17586 this.$get = ['$parse', '$sceDelegate', function(
17587 $parse, $sceDelegate) {
17588 // Prereq: Ensure that we're not running in IE<11 quirks mode. In that mode, IE < 11 allow
17589 // the "expression(javascript expression)" syntax which is insecure.
17590 if (enabled && msie < 8) {
17591 throw $sceMinErr('iequirks',
17592 'Strict Contextual Escaping does not support Internet Explorer version < 11 in quirks ' +
17593 'mode. You can fix this by adding the text <!doctype html> to the top of your HTML ' +
17594 'document. See http://docs.angularjs.org/api/ng.$sce for more information.');
17597 var sce = shallowCopy(SCE_CONTEXTS);
17601 * @name $sce#isEnabled
17604 * @return {Boolean} true if SCE is enabled, false otherwise. If you want to set the value, you
17605 * have to do it at module config time on {@link ng.$sceProvider $sceProvider}.
17608 * Returns a boolean indicating if SCE is enabled.
17610 sce.isEnabled = function() {
17613 sce.trustAs = $sceDelegate.trustAs;
17614 sce.getTrusted = $sceDelegate.getTrusted;
17615 sce.valueOf = $sceDelegate.valueOf;
17618 sce.trustAs = sce.getTrusted = function(type, value) { return value; };
17619 sce.valueOf = identity;
17624 * @name $sce#parseAs
17627 * Converts Angular {@link guide/expression expression} into a function. This is like {@link
17628 * ng.$parse $parse} and is identical when the expression is a literal constant. Otherwise, it
17629 * wraps the expression in a call to {@link ng.$sce#getTrusted $sce.getTrusted(*type*,
17632 * @param {string} type The kind of SCE context in which this result will be used.
17633 * @param {string} expression String expression to compile.
17634 * @returns {function(context, locals)} a function which represents the compiled expression:
17636 * * `context` – `{object}` – an object against which any expressions embedded in the strings
17637 * are evaluated against (typically a scope object).
17638 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
17641 sce.parseAs = function sceParseAs(type, expr) {
17642 var parsed = $parse(expr);
17643 if (parsed.literal && parsed.constant) {
17646 return $parse(expr, function(value) {
17647 return sce.getTrusted(type, value);
17654 * @name $sce#trustAs
17657 * Delegates to {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}. As such,
17658 * returns an object that is trusted by angular for use in specified strict contextual
17659 * escaping contexts (such as ng-bind-html, ng-include, any src attribute
17660 * interpolation, any dom event binding attribute interpolation such as for onclick, etc.)
17661 * that uses the provided value. See * {@link ng.$sce $sce} for enabling strict contextual
17664 * @param {string} type The kind of context in which this value is safe for use. e.g. url,
17665 * resourceUrl, html, js and css.
17666 * @param {*} value The value that that should be considered trusted/safe.
17667 * @returns {*} A value that can be used to stand in for the provided `value` in places
17668 * where Angular expects a $sce.trustAs() return value.
17673 * @name $sce#trustAsHtml
17676 * Shorthand method. `$sce.trustAsHtml(value)` →
17677 * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.HTML, value)`}
17679 * @param {*} value The value to trustAs.
17680 * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedHtml
17681 * $sce.getTrustedHtml(value)} to obtain the original value. (privileged directives
17682 * only accept expressions that are either literal constants or are the
17683 * return value of {@link ng.$sce#trustAs $sce.trustAs}.)
17688 * @name $sce#trustAsUrl
17691 * Shorthand method. `$sce.trustAsUrl(value)` →
17692 * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.URL, value)`}
17694 * @param {*} value The value to trustAs.
17695 * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedUrl
17696 * $sce.getTrustedUrl(value)} to obtain the original value. (privileged directives
17697 * only accept expressions that are either literal constants or are the
17698 * return value of {@link ng.$sce#trustAs $sce.trustAs}.)
17703 * @name $sce#trustAsResourceUrl
17706 * Shorthand method. `$sce.trustAsResourceUrl(value)` →
17707 * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.RESOURCE_URL, value)`}
17709 * @param {*} value The value to trustAs.
17710 * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedResourceUrl
17711 * $sce.getTrustedResourceUrl(value)} to obtain the original value. (privileged directives
17712 * only accept expressions that are either literal constants or are the return
17713 * value of {@link ng.$sce#trustAs $sce.trustAs}.)
17718 * @name $sce#trustAsJs
17721 * Shorthand method. `$sce.trustAsJs(value)` →
17722 * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.JS, value)`}
17724 * @param {*} value The value to trustAs.
17725 * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedJs
17726 * $sce.getTrustedJs(value)} to obtain the original value. (privileged directives
17727 * only accept expressions that are either literal constants or are the
17728 * return value of {@link ng.$sce#trustAs $sce.trustAs}.)
17733 * @name $sce#getTrusted
17736 * Delegates to {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted`}. As such,
17737 * takes the result of a {@link ng.$sce#trustAs `$sce.trustAs`}() call and returns the
17738 * originally supplied value if the queried context type is a supertype of the created type.
17739 * If this condition isn't satisfied, throws an exception.
17741 * @param {string} type The kind of context in which this value is to be used.
17742 * @param {*} maybeTrusted The result of a prior {@link ng.$sce#trustAs `$sce.trustAs`}
17744 * @returns {*} The value the was originally provided to
17745 * {@link ng.$sce#trustAs `$sce.trustAs`} if valid in this context.
17746 * Otherwise, throws an exception.
17751 * @name $sce#getTrustedHtml
17754 * Shorthand method. `$sce.getTrustedHtml(value)` →
17755 * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.HTML, value)`}
17757 * @param {*} value The value to pass to `$sce.getTrusted`.
17758 * @returns {*} The return value of `$sce.getTrusted($sce.HTML, value)`
17763 * @name $sce#getTrustedCss
17766 * Shorthand method. `$sce.getTrustedCss(value)` →
17767 * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.CSS, value)`}
17769 * @param {*} value The value to pass to `$sce.getTrusted`.
17770 * @returns {*} The return value of `$sce.getTrusted($sce.CSS, value)`
17775 * @name $sce#getTrustedUrl
17778 * Shorthand method. `$sce.getTrustedUrl(value)` →
17779 * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.URL, value)`}
17781 * @param {*} value The value to pass to `$sce.getTrusted`.
17782 * @returns {*} The return value of `$sce.getTrusted($sce.URL, value)`
17787 * @name $sce#getTrustedResourceUrl
17790 * Shorthand method. `$sce.getTrustedResourceUrl(value)` →
17791 * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.RESOURCE_URL, value)`}
17793 * @param {*} value The value to pass to `$sceDelegate.getTrusted`.
17794 * @returns {*} The return value of `$sce.getTrusted($sce.RESOURCE_URL, value)`
17799 * @name $sce#getTrustedJs
17802 * Shorthand method. `$sce.getTrustedJs(value)` →
17803 * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.JS, value)`}
17805 * @param {*} value The value to pass to `$sce.getTrusted`.
17806 * @returns {*} The return value of `$sce.getTrusted($sce.JS, value)`
17811 * @name $sce#parseAsHtml
17814 * Shorthand method. `$sce.parseAsHtml(expression string)` →
17815 * {@link ng.$sce#parseAs `$sce.parseAs($sce.HTML, value)`}
17817 * @param {string} expression String expression to compile.
17818 * @returns {function(context, locals)} a function which represents the compiled expression:
17820 * * `context` – `{object}` – an object against which any expressions embedded in the strings
17821 * are evaluated against (typically a scope object).
17822 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
17828 * @name $sce#parseAsCss
17831 * Shorthand method. `$sce.parseAsCss(value)` →
17832 * {@link ng.$sce#parseAs `$sce.parseAs($sce.CSS, value)`}
17834 * @param {string} expression String expression to compile.
17835 * @returns {function(context, locals)} a function which represents the compiled expression:
17837 * * `context` – `{object}` – an object against which any expressions embedded in the strings
17838 * are evaluated against (typically a scope object).
17839 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
17845 * @name $sce#parseAsUrl
17848 * Shorthand method. `$sce.parseAsUrl(value)` →
17849 * {@link ng.$sce#parseAs `$sce.parseAs($sce.URL, value)`}
17851 * @param {string} expression String expression to compile.
17852 * @returns {function(context, locals)} a function which represents the compiled expression:
17854 * * `context` – `{object}` – an object against which any expressions embedded in the strings
17855 * are evaluated against (typically a scope object).
17856 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
17862 * @name $sce#parseAsResourceUrl
17865 * Shorthand method. `$sce.parseAsResourceUrl(value)` →
17866 * {@link ng.$sce#parseAs `$sce.parseAs($sce.RESOURCE_URL, value)`}
17868 * @param {string} expression String expression to compile.
17869 * @returns {function(context, locals)} a function which represents the compiled expression:
17871 * * `context` – `{object}` – an object against which any expressions embedded in the strings
17872 * are evaluated against (typically a scope object).
17873 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
17879 * @name $sce#parseAsJs
17882 * Shorthand method. `$sce.parseAsJs(value)` →
17883 * {@link ng.$sce#parseAs `$sce.parseAs($sce.JS, value)`}
17885 * @param {string} expression String expression to compile.
17886 * @returns {function(context, locals)} a function which represents the compiled expression:
17888 * * `context` – `{object}` – an object against which any expressions embedded in the strings
17889 * are evaluated against (typically a scope object).
17890 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
17894 // Shorthand delegations.
17895 var parse = sce.parseAs,
17896 getTrusted = sce.getTrusted,
17897 trustAs = sce.trustAs;
17899 forEach(SCE_CONTEXTS, function(enumValue, name) {
17900 var lName = lowercase(name);
17901 sce[camelCase("parse_as_" + lName)] = function(expr) {
17902 return parse(enumValue, expr);
17904 sce[camelCase("get_trusted_" + lName)] = function(value) {
17905 return getTrusted(enumValue, value);
17907 sce[camelCase("trust_as_" + lName)] = function(value) {
17908 return trustAs(enumValue, value);
17917 * !!! This is an undocumented "private" service !!!
17920 * @requires $window
17921 * @requires $document
17923 * @property {boolean} history Does the browser support html5 history api ?
17924 * @property {boolean} transitions Does the browser support CSS transition events ?
17925 * @property {boolean} animations Does the browser support CSS animation events ?
17928 * This is very simple implementation of testing browser's features.
17930 function $SnifferProvider() {
17931 this.$get = ['$window', '$document', function($window, $document) {
17932 var eventSupport = {},
17934 toInt((/android (\d+)/.exec(lowercase(($window.navigator || {}).userAgent)) || [])[1]),
17935 boxee = /Boxee/i.test(($window.navigator || {}).userAgent),
17936 document = $document[0] || {},
17938 vendorRegex = /^(Moz|webkit|ms)(?=[A-Z])/,
17939 bodyStyle = document.body && document.body.style,
17940 transitions = false,
17941 animations = false,
17945 for (var prop in bodyStyle) {
17946 if (match = vendorRegex.exec(prop)) {
17947 vendorPrefix = match[0];
17948 vendorPrefix = vendorPrefix.substr(0, 1).toUpperCase() + vendorPrefix.substr(1);
17953 if (!vendorPrefix) {
17954 vendorPrefix = ('WebkitOpacity' in bodyStyle) && 'webkit';
17957 transitions = !!(('transition' in bodyStyle) || (vendorPrefix + 'Transition' in bodyStyle));
17958 animations = !!(('animation' in bodyStyle) || (vendorPrefix + 'Animation' in bodyStyle));
17960 if (android && (!transitions || !animations)) {
17961 transitions = isString(bodyStyle.webkitTransition);
17962 animations = isString(bodyStyle.webkitAnimation);
17968 // Android has history.pushState, but it does not update location correctly
17969 // so let's not use the history API at all.
17970 // http://code.google.com/p/android/issues/detail?id=17471
17971 // https://github.com/angular/angular.js/issues/904
17973 // older webkit browser (533.9) on Boxee box has exactly the same problem as Android has
17974 // so let's not use the history API also
17975 // We are purposefully using `!(android < 4)` to cover the case when `android` is undefined
17977 history: !!($window.history && $window.history.pushState && !(android < 4) && !boxee),
17979 hasEvent: function(event) {
17980 // IE9 implements 'input' event it's so fubared that we rather pretend that it doesn't have
17981 // it. In particular the event is not fired when backspace or delete key are pressed or
17982 // when cut operation is performed.
17983 // IE10+ implements 'input' event but it erroneously fires under various situations,
17984 // e.g. when placeholder changes, or a form is focused.
17985 if (event === 'input' && msie <= 11) return false;
17987 if (isUndefined(eventSupport[event])) {
17988 var divElm = document.createElement('div');
17989 eventSupport[event] = 'on' + event in divElm;
17992 return eventSupport[event];
17995 vendorPrefix: vendorPrefix,
17996 transitions: transitions,
17997 animations: animations,
18003 var $compileMinErr = minErr('$compile');
18007 * @name $templateRequest
18010 * The `$templateRequest` service runs security checks then downloads the provided template using
18011 * `$http` and, upon success, stores the contents inside of `$templateCache`. If the HTTP request
18012 * fails or the response data of the HTTP request is empty, a `$compile` error will be thrown (the
18013 * exception can be thwarted by setting the 2nd parameter of the function to true). Note that the
18014 * contents of `$templateCache` are trusted, so the call to `$sce.getTrustedUrl(tpl)` is omitted
18015 * when `tpl` is of type string and `$templateCache` has the matching entry.
18017 * @param {string|TrustedResourceUrl} tpl The HTTP request template URL
18018 * @param {boolean=} ignoreRequestError Whether or not to ignore the exception when the request fails or the template is empty
18020 * @return {Promise} a promise for the HTTP response data of the given URL.
18022 * @property {number} totalPendingRequests total amount of pending template requests being downloaded.
18024 function $TemplateRequestProvider() {
18025 this.$get = ['$templateCache', '$http', '$q', '$sce', function($templateCache, $http, $q, $sce) {
18026 function handleRequestFn(tpl, ignoreRequestError) {
18027 handleRequestFn.totalPendingRequests++;
18029 // We consider the template cache holds only trusted templates, so
18030 // there's no need to go through whitelisting again for keys that already
18031 // are included in there. This also makes Angular accept any script
18032 // directive, no matter its name. However, we still need to unwrap trusted
18034 if (!isString(tpl) || !$templateCache.get(tpl)) {
18035 tpl = $sce.getTrustedResourceUrl(tpl);
18038 var transformResponse = $http.defaults && $http.defaults.transformResponse;
18040 if (isArray(transformResponse)) {
18041 transformResponse = transformResponse.filter(function(transformer) {
18042 return transformer !== defaultHttpResponseTransform;
18044 } else if (transformResponse === defaultHttpResponseTransform) {
18045 transformResponse = null;
18048 var httpOptions = {
18049 cache: $templateCache,
18050 transformResponse: transformResponse
18053 return $http.get(tpl, httpOptions)
18054 ['finally'](function() {
18055 handleRequestFn.totalPendingRequests--;
18057 .then(function(response) {
18058 $templateCache.put(tpl, response.data);
18059 return response.data;
18062 function handleError(resp) {
18063 if (!ignoreRequestError) {
18064 throw $compileMinErr('tpload', 'Failed to load template: {0} (HTTP status: {1} {2})',
18065 tpl, resp.status, resp.statusText);
18067 return $q.reject(resp);
18071 handleRequestFn.totalPendingRequests = 0;
18073 return handleRequestFn;
18077 function $$TestabilityProvider() {
18078 this.$get = ['$rootScope', '$browser', '$location',
18079 function($rootScope, $browser, $location) {
18082 * @name $testability
18085 * The private $$testability service provides a collection of methods for use when debugging
18086 * or by automated test and debugging tools.
18088 var testability = {};
18091 * @name $$testability#findBindings
18094 * Returns an array of elements that are bound (via ng-bind or {{}})
18095 * to expressions matching the input.
18097 * @param {Element} element The element root to search from.
18098 * @param {string} expression The binding expression to match.
18099 * @param {boolean} opt_exactMatch If true, only returns exact matches
18100 * for the expression. Filters and whitespace are ignored.
18102 testability.findBindings = function(element, expression, opt_exactMatch) {
18103 var bindings = element.getElementsByClassName('ng-binding');
18105 forEach(bindings, function(binding) {
18106 var dataBinding = angular.element(binding).data('$binding');
18108 forEach(dataBinding, function(bindingName) {
18109 if (opt_exactMatch) {
18110 var matcher = new RegExp('(^|\\s)' + escapeForRegexp(expression) + '(\\s|\\||$)');
18111 if (matcher.test(bindingName)) {
18112 matches.push(binding);
18115 if (bindingName.indexOf(expression) != -1) {
18116 matches.push(binding);
18126 * @name $$testability#findModels
18129 * Returns an array of elements that are two-way found via ng-model to
18130 * expressions matching the input.
18132 * @param {Element} element The element root to search from.
18133 * @param {string} expression The model expression to match.
18134 * @param {boolean} opt_exactMatch If true, only returns exact matches
18135 * for the expression.
18137 testability.findModels = function(element, expression, opt_exactMatch) {
18138 var prefixes = ['ng-', 'data-ng-', 'ng\\:'];
18139 for (var p = 0; p < prefixes.length; ++p) {
18140 var attributeEquals = opt_exactMatch ? '=' : '*=';
18141 var selector = '[' + prefixes[p] + 'model' + attributeEquals + '"' + expression + '"]';
18142 var elements = element.querySelectorAll(selector);
18143 if (elements.length) {
18150 * @name $$testability#getLocation
18153 * Shortcut for getting the location in a browser agnostic way. Returns
18154 * the path, search, and hash. (e.g. /path?a=b#hash)
18156 testability.getLocation = function() {
18157 return $location.url();
18161 * @name $$testability#setLocation
18164 * Shortcut for navigating to a location without doing a full page reload.
18166 * @param {string} url The location url (path, search and hash,
18167 * e.g. /path?a=b#hash) to go to.
18169 testability.setLocation = function(url) {
18170 if (url !== $location.url()) {
18171 $location.url(url);
18172 $rootScope.$digest();
18177 * @name $$testability#whenStable
18180 * Calls the callback when $timeout and $http requests are completed.
18182 * @param {function} callback
18184 testability.whenStable = function(callback) {
18185 $browser.notifyWhenNoOutstandingRequests(callback);
18188 return testability;
18192 function $TimeoutProvider() {
18193 this.$get = ['$rootScope', '$browser', '$q', '$$q', '$exceptionHandler',
18194 function($rootScope, $browser, $q, $$q, $exceptionHandler) {
18196 var deferreds = {};
18204 * Angular's wrapper for `window.setTimeout`. The `fn` function is wrapped into a try/catch
18205 * block and delegates any exceptions to
18206 * {@link ng.$exceptionHandler $exceptionHandler} service.
18208 * The return value of calling `$timeout` is a promise, which will be resolved when
18209 * the delay has passed and the timeout function, if provided, is executed.
18211 * To cancel a timeout request, call `$timeout.cancel(promise)`.
18213 * In tests you can use {@link ngMock.$timeout `$timeout.flush()`} to
18214 * synchronously flush the queue of deferred functions.
18216 * If you only want a promise that will be resolved after some specified delay
18217 * then you can call `$timeout` without the `fn` function.
18219 * @param {function()=} fn A function, whose execution should be delayed.
18220 * @param {number=} [delay=0] Delay in milliseconds.
18221 * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
18222 * will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
18223 * @param {...*=} Pass additional parameters to the executed function.
18224 * @returns {Promise} Promise that will be resolved when the timeout is reached. The promise
18225 * will be resolved with the return value of the `fn` function.
18228 function timeout(fn, delay, invokeApply) {
18229 if (!isFunction(fn)) {
18230 invokeApply = delay;
18235 var args = sliceArgs(arguments, 3),
18236 skipApply = (isDefined(invokeApply) && !invokeApply),
18237 deferred = (skipApply ? $$q : $q).defer(),
18238 promise = deferred.promise,
18241 timeoutId = $browser.defer(function() {
18243 deferred.resolve(fn.apply(null, args));
18245 deferred.reject(e);
18246 $exceptionHandler(e);
18249 delete deferreds[promise.$$timeoutId];
18252 if (!skipApply) $rootScope.$apply();
18255 promise.$$timeoutId = timeoutId;
18256 deferreds[timeoutId] = deferred;
18264 * @name $timeout#cancel
18267 * Cancels a task associated with the `promise`. As a result of this, the promise will be
18268 * resolved with a rejection.
18270 * @param {Promise=} promise Promise returned by the `$timeout` function.
18271 * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully
18274 timeout.cancel = function(promise) {
18275 if (promise && promise.$$timeoutId in deferreds) {
18276 deferreds[promise.$$timeoutId].reject('canceled');
18277 delete deferreds[promise.$$timeoutId];
18278 return $browser.defer.cancel(promise.$$timeoutId);
18287 // NOTE: The usage of window and document instead of $window and $document here is
18288 // deliberate. This service depends on the specific behavior of anchor nodes created by the
18289 // browser (resolving and parsing URLs) that is unlikely to be provided by mock objects and
18290 // cause us to break tests. In addition, when the browser resolves a URL for XHR, it
18291 // doesn't know about mocked locations and resolves URLs to the real document - which is
18292 // exactly the behavior needed here. There is little value is mocking these out for this
18294 var urlParsingNode = document.createElement("a");
18295 var originUrl = urlResolve(window.location.href);
18300 * Implementation Notes for non-IE browsers
18301 * ----------------------------------------
18302 * Assigning a URL to the href property of an anchor DOM node, even one attached to the DOM,
18303 * results both in the normalizing and parsing of the URL. Normalizing means that a relative
18304 * URL will be resolved into an absolute URL in the context of the application document.
18305 * Parsing means that the anchor node's host, hostname, protocol, port, pathname and related
18306 * properties are all populated to reflect the normalized URL. This approach has wide
18307 * compatibility - Safari 1+, Mozilla 1+, Opera 7+,e etc. See
18308 * http://www.aptana.com/reference/html/api/HTMLAnchorElement.html
18310 * Implementation Notes for IE
18311 * ---------------------------
18312 * IE <= 10 normalizes the URL when assigned to the anchor node similar to the other
18313 * browsers. However, the parsed components will not be set if the URL assigned did not specify
18314 * them. (e.g. if you assign a.href = "foo", then a.protocol, a.host, etc. will be empty.) We
18315 * work around that by performing the parsing in a 2nd step by taking a previously normalized
18316 * URL (e.g. by assigning to a.href) and assigning it a.href again. This correctly populates the
18317 * properties such as protocol, hostname, port, etc.
18320 * http://developer.mozilla.org/en-US/docs/Web/API/HTMLAnchorElement
18321 * http://www.aptana.com/reference/html/api/HTMLAnchorElement.html
18322 * http://url.spec.whatwg.org/#urlutils
18323 * https://github.com/angular/angular.js/pull/2902
18324 * http://james.padolsey.com/javascript/parsing-urls-with-the-dom/
18327 * @param {string} url The URL to be parsed.
18328 * @description Normalizes and parses a URL.
18329 * @returns {object} Returns the normalized URL as a dictionary.
18331 * | member name | Description |
18332 * |---------------|----------------|
18333 * | href | A normalized version of the provided URL if it was not an absolute URL |
18334 * | protocol | The protocol including the trailing colon |
18335 * | host | The host and port (if the port is non-default) of the normalizedUrl |
18336 * | search | The search params, minus the question mark |
18337 * | hash | The hash string, minus the hash symbol
18338 * | hostname | The hostname
18339 * | port | The port, without ":"
18340 * | pathname | The pathname, beginning with "/"
18343 function urlResolve(url) {
18347 // Normalize before parse. Refer Implementation Notes on why this is
18348 // done in two steps on IE.
18349 urlParsingNode.setAttribute("href", href);
18350 href = urlParsingNode.href;
18353 urlParsingNode.setAttribute('href', href);
18355 // urlParsingNode provides the UrlUtils interface - http://url.spec.whatwg.org/#urlutils
18357 href: urlParsingNode.href,
18358 protocol: urlParsingNode.protocol ? urlParsingNode.protocol.replace(/:$/, '') : '',
18359 host: urlParsingNode.host,
18360 search: urlParsingNode.search ? urlParsingNode.search.replace(/^\?/, '') : '',
18361 hash: urlParsingNode.hash ? urlParsingNode.hash.replace(/^#/, '') : '',
18362 hostname: urlParsingNode.hostname,
18363 port: urlParsingNode.port,
18364 pathname: (urlParsingNode.pathname.charAt(0) === '/')
18365 ? urlParsingNode.pathname
18366 : '/' + urlParsingNode.pathname
18371 * Parse a request URL and determine whether this is a same-origin request as the application document.
18373 * @param {string|object} requestUrl The url of the request as a string that will be resolved
18374 * or a parsed URL object.
18375 * @returns {boolean} Whether the request is for the same origin as the application document.
18377 function urlIsSameOrigin(requestUrl) {
18378 var parsed = (isString(requestUrl)) ? urlResolve(requestUrl) : requestUrl;
18379 return (parsed.protocol === originUrl.protocol &&
18380 parsed.host === originUrl.host);
18388 * A reference to the browser's `window` object. While `window`
18389 * is globally available in JavaScript, it causes testability problems, because
18390 * it is a global variable. In angular we always refer to it through the
18391 * `$window` service, so it may be overridden, removed or mocked for testing.
18393 * Expressions, like the one defined for the `ngClick` directive in the example
18394 * below, are evaluated with respect to the current scope. Therefore, there is
18395 * no risk of inadvertently coding in a dependency on a global value in such an
18399 <example module="windowExample">
18400 <file name="index.html">
18402 angular.module('windowExample', [])
18403 .controller('ExampleController', ['$scope', '$window', function($scope, $window) {
18404 $scope.greeting = 'Hello, World!';
18405 $scope.doGreeting = function(greeting) {
18406 $window.alert(greeting);
18410 <div ng-controller="ExampleController">
18411 <input type="text" ng-model="greeting" aria-label="greeting" />
18412 <button ng-click="doGreeting(greeting)">ALERT</button>
18415 <file name="protractor.js" type="protractor">
18416 it('should display the greeting in the input box', function() {
18417 element(by.model('greeting')).sendKeys('Hello, E2E Tests');
18418 // If we click the button it will block the test runner
18419 // element(':button').click();
18424 function $WindowProvider() {
18425 this.$get = valueFn(window);
18429 * @name $$cookieReader
18430 * @requires $document
18433 * This is a private service for reading cookies used by $http and ngCookies
18435 * @return {Object} a key/value map of the current cookies
18437 function $$CookieReader($document) {
18438 var rawDocument = $document[0] || {};
18439 var lastCookies = {};
18440 var lastCookieString = '';
18442 function safeDecodeURIComponent(str) {
18444 return decodeURIComponent(str);
18450 return function() {
18451 var cookieArray, cookie, i, index, name;
18452 var currentCookieString = rawDocument.cookie || '';
18454 if (currentCookieString !== lastCookieString) {
18455 lastCookieString = currentCookieString;
18456 cookieArray = lastCookieString.split('; ');
18459 for (i = 0; i < cookieArray.length; i++) {
18460 cookie = cookieArray[i];
18461 index = cookie.indexOf('=');
18462 if (index > 0) { //ignore nameless cookies
18463 name = safeDecodeURIComponent(cookie.substring(0, index));
18464 // the first value that is seen for a cookie is the most
18465 // specific one. values for the same cookie name that
18466 // follow are for less specific paths.
18467 if (isUndefined(lastCookies[name])) {
18468 lastCookies[name] = safeDecodeURIComponent(cookie.substring(index + 1));
18473 return lastCookies;
18477 $$CookieReader.$inject = ['$document'];
18479 function $$CookieReaderProvider() {
18480 this.$get = $$CookieReader;
18483 /* global currencyFilter: true,
18485 filterFilter: true,
18487 limitToFilter: true,
18488 lowercaseFilter: true,
18489 numberFilter: true,
18490 orderByFilter: true,
18491 uppercaseFilter: true,
18496 * @name $filterProvider
18499 * Filters are just functions which transform input to an output. However filters need to be
18500 * Dependency Injected. To achieve this a filter definition consists of a factory function which is
18501 * annotated with dependencies and is responsible for creating a filter function.
18503 * <div class="alert alert-warning">
18504 * **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`.
18505 * Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace
18506 * your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
18507 * (`myapp_subsection_filterx`).
18511 * // Filter registration
18512 * function MyModule($provide, $filterProvider) {
18513 * // create a service to demonstrate injection (not always needed)
18514 * $provide.value('greet', function(name){
18515 * return 'Hello ' + name + '!';
18518 * // register a filter factory which uses the
18519 * // greet service to demonstrate DI.
18520 * $filterProvider.register('greet', function(greet){
18521 * // return the filter function which uses the greet service
18522 * // to generate salutation
18523 * return function(text) {
18524 * // filters need to be forgiving so check input validity
18525 * return text && greet(text) || text;
18531 * The filter function is registered with the `$injector` under the filter name suffix with
18535 * it('should be the same instance', inject(
18536 * function($filterProvider) {
18537 * $filterProvider.register('reverse', function(){
18541 * function($filter, reverseFilter) {
18542 * expect($filter('reverse')).toBe(reverseFilter);
18547 * For more information about how angular filters work, and how to create your own filters, see
18548 * {@link guide/filter Filters} in the Angular Developer Guide.
18556 * Filters are used for formatting data displayed to the user.
18558 * The general syntax in templates is as follows:
18560 * {{ expression [| filter_name[:parameter_value] ... ] }}
18562 * @param {String} name Name of the filter function to retrieve
18563 * @return {Function} the filter function
18565 <example name="$filter" module="filterExample">
18566 <file name="index.html">
18567 <div ng-controller="MainCtrl">
18568 <h3>{{ originalText }}</h3>
18569 <h3>{{ filteredText }}</h3>
18573 <file name="script.js">
18574 angular.module('filterExample', [])
18575 .controller('MainCtrl', function($scope, $filter) {
18576 $scope.originalText = 'hello';
18577 $scope.filteredText = $filter('uppercase')($scope.originalText);
18582 $FilterProvider.$inject = ['$provide'];
18583 function $FilterProvider($provide) {
18584 var suffix = 'Filter';
18588 * @name $filterProvider#register
18589 * @param {string|Object} name Name of the filter function, or an object map of filters where
18590 * the keys are the filter names and the values are the filter factories.
18592 * <div class="alert alert-warning">
18593 * **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`.
18594 * Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace
18595 * your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
18596 * (`myapp_subsection_filterx`).
18598 * @param {Function} factory If the first argument was a string, a factory function for the filter to be registered.
18599 * @returns {Object} Registered filter instance, or if a map of filters was provided then a map
18600 * of the registered filter instances.
18602 function register(name, factory) {
18603 if (isObject(name)) {
18605 forEach(name, function(filter, key) {
18606 filters[key] = register(key, filter);
18610 return $provide.factory(name + suffix, factory);
18613 this.register = register;
18615 this.$get = ['$injector', function($injector) {
18616 return function(name) {
18617 return $injector.get(name + suffix);
18621 ////////////////////////////////////////
18624 currencyFilter: false,
18626 filterFilter: false,
18628 limitToFilter: false,
18629 lowercaseFilter: false,
18630 numberFilter: false,
18631 orderByFilter: false,
18632 uppercaseFilter: false,
18635 register('currency', currencyFilter);
18636 register('date', dateFilter);
18637 register('filter', filterFilter);
18638 register('json', jsonFilter);
18639 register('limitTo', limitToFilter);
18640 register('lowercase', lowercaseFilter);
18641 register('number', numberFilter);
18642 register('orderBy', orderByFilter);
18643 register('uppercase', uppercaseFilter);
18652 * Selects a subset of items from `array` and returns it as a new array.
18654 * @param {Array} array The source array.
18655 * @param {string|Object|function()} expression The predicate to be used for selecting items from
18660 * - `string`: The string is used for matching against the contents of the `array`. All strings or
18661 * objects with string properties in `array` that match this string will be returned. This also
18662 * applies to nested object properties.
18663 * The predicate can be negated by prefixing the string with `!`.
18665 * - `Object`: A pattern object can be used to filter specific properties on objects contained
18666 * by `array`. For example `{name:"M", phone:"1"}` predicate will return an array of items
18667 * which have property `name` containing "M" and property `phone` containing "1". A special
18668 * property name `$` can be used (as in `{$:"text"}`) to accept a match against any
18669 * property of the object or its nested object properties. That's equivalent to the simple
18670 * substring match with a `string` as described above. The predicate can be negated by prefixing
18671 * the string with `!`.
18672 * For example `{name: "!M"}` predicate will return an array of items which have property `name`
18673 * not containing "M".
18675 * Note that a named property will match properties on the same level only, while the special
18676 * `$` property will match properties on the same level or deeper. E.g. an array item like
18677 * `{name: {first: 'John', last: 'Doe'}}` will **not** be matched by `{name: 'John'}`, but
18678 * **will** be matched by `{$: 'John'}`.
18680 * - `function(value, index, array)`: A predicate function can be used to write arbitrary filters.
18681 * The function is called for each element of the array, with the element, its index, and
18682 * the entire array itself as arguments.
18684 * The final result is an array of those elements that the predicate returned true for.
18686 * @param {function(actual, expected)|true|undefined} comparator Comparator which is used in
18687 * determining if the expected value (from the filter expression) and actual value (from
18688 * the object in the array) should be considered a match.
18692 * - `function(actual, expected)`:
18693 * The function will be given the object value and the predicate value to compare and
18694 * should return true if both values should be considered equal.
18696 * - `true`: A shorthand for `function(actual, expected) { return angular.equals(actual, expected)}`.
18697 * This is essentially strict comparison of expected and actual.
18699 * - `false|undefined`: A short hand for a function which will look for a substring match in case
18702 * Primitive values are converted to strings. Objects are not compared against primitives,
18703 * unless they have a custom `toString` method (e.g. `Date` objects).
18707 <file name="index.html">
18708 <div ng-init="friends = [{name:'John', phone:'555-1276'},
18709 {name:'Mary', phone:'800-BIG-MARY'},
18710 {name:'Mike', phone:'555-4321'},
18711 {name:'Adam', phone:'555-5678'},
18712 {name:'Julie', phone:'555-8765'},
18713 {name:'Juliette', phone:'555-5678'}]"></div>
18715 <label>Search: <input ng-model="searchText"></label>
18716 <table id="searchTextResults">
18717 <tr><th>Name</th><th>Phone</th></tr>
18718 <tr ng-repeat="friend in friends | filter:searchText">
18719 <td>{{friend.name}}</td>
18720 <td>{{friend.phone}}</td>
18724 <label>Any: <input ng-model="search.$"></label> <br>
18725 <label>Name only <input ng-model="search.name"></label><br>
18726 <label>Phone only <input ng-model="search.phone"></label><br>
18727 <label>Equality <input type="checkbox" ng-model="strict"></label><br>
18728 <table id="searchObjResults">
18729 <tr><th>Name</th><th>Phone</th></tr>
18730 <tr ng-repeat="friendObj in friends | filter:search:strict">
18731 <td>{{friendObj.name}}</td>
18732 <td>{{friendObj.phone}}</td>
18736 <file name="protractor.js" type="protractor">
18737 var expectFriendNames = function(expectedNames, key) {
18738 element.all(by.repeater(key + ' in friends').column(key + '.name')).then(function(arr) {
18739 arr.forEach(function(wd, i) {
18740 expect(wd.getText()).toMatch(expectedNames[i]);
18745 it('should search across all fields when filtering with a string', function() {
18746 var searchText = element(by.model('searchText'));
18747 searchText.clear();
18748 searchText.sendKeys('m');
18749 expectFriendNames(['Mary', 'Mike', 'Adam'], 'friend');
18751 searchText.clear();
18752 searchText.sendKeys('76');
18753 expectFriendNames(['John', 'Julie'], 'friend');
18756 it('should search in specific fields when filtering with a predicate object', function() {
18757 var searchAny = element(by.model('search.$'));
18759 searchAny.sendKeys('i');
18760 expectFriendNames(['Mary', 'Mike', 'Julie', 'Juliette'], 'friendObj');
18762 it('should use a equal comparison when comparator is true', function() {
18763 var searchName = element(by.model('search.name'));
18764 var strict = element(by.model('strict'));
18765 searchName.clear();
18766 searchName.sendKeys('Julie');
18768 expectFriendNames(['Julie'], 'friendObj');
18773 function filterFilter() {
18774 return function(array, expression, comparator) {
18775 if (!isArrayLike(array)) {
18776 if (array == null) {
18779 throw minErr('filter')('notarray', 'Expected array but received: {0}', array);
18783 var expressionType = getTypeForFilter(expression);
18785 var matchAgainstAnyProp;
18787 switch (expressionType) {
18789 predicateFn = expression;
18795 matchAgainstAnyProp = true;
18799 predicateFn = createPredicateFn(expression, comparator, matchAgainstAnyProp);
18805 return Array.prototype.filter.call(array, predicateFn);
18809 // Helper functions for `filterFilter`
18810 function createPredicateFn(expression, comparator, matchAgainstAnyProp) {
18811 var shouldMatchPrimitives = isObject(expression) && ('$' in expression);
18814 if (comparator === true) {
18815 comparator = equals;
18816 } else if (!isFunction(comparator)) {
18817 comparator = function(actual, expected) {
18818 if (isUndefined(actual)) {
18819 // No substring matching against `undefined`
18822 if ((actual === null) || (expected === null)) {
18823 // No substring matching against `null`; only match against `null`
18824 return actual === expected;
18826 if (isObject(expected) || (isObject(actual) && !hasCustomToString(actual))) {
18827 // Should not compare primitives against objects, unless they have custom `toString` method
18831 actual = lowercase('' + actual);
18832 expected = lowercase('' + expected);
18833 return actual.indexOf(expected) !== -1;
18837 predicateFn = function(item) {
18838 if (shouldMatchPrimitives && !isObject(item)) {
18839 return deepCompare(item, expression.$, comparator, false);
18841 return deepCompare(item, expression, comparator, matchAgainstAnyProp);
18844 return predicateFn;
18847 function deepCompare(actual, expected, comparator, matchAgainstAnyProp, dontMatchWholeObject) {
18848 var actualType = getTypeForFilter(actual);
18849 var expectedType = getTypeForFilter(expected);
18851 if ((expectedType === 'string') && (expected.charAt(0) === '!')) {
18852 return !deepCompare(actual, expected.substring(1), comparator, matchAgainstAnyProp);
18853 } else if (isArray(actual)) {
18854 // In case `actual` is an array, consider it a match
18855 // if ANY of it's items matches `expected`
18856 return actual.some(function(item) {
18857 return deepCompare(item, expected, comparator, matchAgainstAnyProp);
18861 switch (actualType) {
18864 if (matchAgainstAnyProp) {
18865 for (key in actual) {
18866 if ((key.charAt(0) !== '$') && deepCompare(actual[key], expected, comparator, true)) {
18870 return dontMatchWholeObject ? false : deepCompare(actual, expected, comparator, false);
18871 } else if (expectedType === 'object') {
18872 for (key in expected) {
18873 var expectedVal = expected[key];
18874 if (isFunction(expectedVal) || isUndefined(expectedVal)) {
18878 var matchAnyProperty = key === '$';
18879 var actualVal = matchAnyProperty ? actual : actual[key];
18880 if (!deepCompare(actualVal, expectedVal, comparator, matchAnyProperty, matchAnyProperty)) {
18886 return comparator(actual, expected);
18892 return comparator(actual, expected);
18896 // Used for easily differentiating between `null` and actual `object`
18897 function getTypeForFilter(val) {
18898 return (val === null) ? 'null' : typeof val;
18901 var MAX_DIGITS = 22;
18902 var DECIMAL_SEP = '.';
18903 var ZERO_CHAR = '0';
18911 * Formats a number as a currency (ie $1,234.56). When no currency symbol is provided, default
18912 * symbol for current locale is used.
18914 * @param {number} amount Input to filter.
18915 * @param {string=} symbol Currency symbol or identifier to be displayed.
18916 * @param {number=} fractionSize Number of decimal places to round the amount to, defaults to default max fraction size for current locale
18917 * @returns {string} Formatted number.
18921 <example module="currencyExample">
18922 <file name="index.html">
18924 angular.module('currencyExample', [])
18925 .controller('ExampleController', ['$scope', function($scope) {
18926 $scope.amount = 1234.56;
18929 <div ng-controller="ExampleController">
18930 <input type="number" ng-model="amount" aria-label="amount"> <br>
18931 default currency symbol ($): <span id="currency-default">{{amount | currency}}</span><br>
18932 custom currency identifier (USD$): <span id="currency-custom">{{amount | currency:"USD$"}}</span>
18933 no fractions (0): <span id="currency-no-fractions">{{amount | currency:"USD$":0}}</span>
18936 <file name="protractor.js" type="protractor">
18937 it('should init with 1234.56', function() {
18938 expect(element(by.id('currency-default')).getText()).toBe('$1,234.56');
18939 expect(element(by.id('currency-custom')).getText()).toBe('USD$1,234.56');
18940 expect(element(by.id('currency-no-fractions')).getText()).toBe('USD$1,235');
18942 it('should update', function() {
18943 if (browser.params.browser == 'safari') {
18944 // Safari does not understand the minus key. See
18945 // https://github.com/angular/protractor/issues/481
18948 element(by.model('amount')).clear();
18949 element(by.model('amount')).sendKeys('-1234');
18950 expect(element(by.id('currency-default')).getText()).toBe('-$1,234.00');
18951 expect(element(by.id('currency-custom')).getText()).toBe('-USD$1,234.00');
18952 expect(element(by.id('currency-no-fractions')).getText()).toBe('-USD$1,234');
18957 currencyFilter.$inject = ['$locale'];
18958 function currencyFilter($locale) {
18959 var formats = $locale.NUMBER_FORMATS;
18960 return function(amount, currencySymbol, fractionSize) {
18961 if (isUndefined(currencySymbol)) {
18962 currencySymbol = formats.CURRENCY_SYM;
18965 if (isUndefined(fractionSize)) {
18966 fractionSize = formats.PATTERNS[1].maxFrac;
18969 // if null or undefined pass it through
18970 return (amount == null)
18972 : formatNumber(amount, formats.PATTERNS[1], formats.GROUP_SEP, formats.DECIMAL_SEP, fractionSize).
18973 replace(/\u00A4/g, currencySymbol);
18983 * Formats a number as text.
18985 * If the input is null or undefined, it will just be returned.
18986 * If the input is infinite (Infinity or -Infinity), the Infinity symbol '∞' or '-∞' is returned, respectively.
18987 * If the input is not a number an empty string is returned.
18990 * @param {number|string} number Number to format.
18991 * @param {(number|string)=} fractionSize Number of decimal places to round the number to.
18992 * If this is not provided then the fraction size is computed from the current locale's number
18993 * formatting pattern. In the case of the default locale, it will be 3.
18994 * @returns {string} Number rounded to fractionSize and places a “,” after each third digit.
18997 <example module="numberFilterExample">
18998 <file name="index.html">
19000 angular.module('numberFilterExample', [])
19001 .controller('ExampleController', ['$scope', function($scope) {
19002 $scope.val = 1234.56789;
19005 <div ng-controller="ExampleController">
19006 <label>Enter number: <input ng-model='val'></label><br>
19007 Default formatting: <span id='number-default'>{{val | number}}</span><br>
19008 No fractions: <span>{{val | number:0}}</span><br>
19009 Negative number: <span>{{-val | number:4}}</span>
19012 <file name="protractor.js" type="protractor">
19013 it('should format numbers', function() {
19014 expect(element(by.id('number-default')).getText()).toBe('1,234.568');
19015 expect(element(by.binding('val | number:0')).getText()).toBe('1,235');
19016 expect(element(by.binding('-val | number:4')).getText()).toBe('-1,234.5679');
19019 it('should update', function() {
19020 element(by.model('val')).clear();
19021 element(by.model('val')).sendKeys('3374.333');
19022 expect(element(by.id('number-default')).getText()).toBe('3,374.333');
19023 expect(element(by.binding('val | number:0')).getText()).toBe('3,374');
19024 expect(element(by.binding('-val | number:4')).getText()).toBe('-3,374.3330');
19029 numberFilter.$inject = ['$locale'];
19030 function numberFilter($locale) {
19031 var formats = $locale.NUMBER_FORMATS;
19032 return function(number, fractionSize) {
19034 // if null or undefined pass it through
19035 return (number == null)
19037 : formatNumber(number, formats.PATTERNS[0], formats.GROUP_SEP, formats.DECIMAL_SEP,
19043 * Parse a number (as a string) into three components that can be used
19044 * for formatting the number.
19046 * (Significant bits of this parse algorithm came from https://github.com/MikeMcl/big.js/)
19048 * @param {string} numStr The number to parse
19049 * @return {object} An object describing this number, containing the following keys:
19050 * - d : an array of digits containing leading zeros as necessary
19051 * - i : the number of the digits in `d` that are to the left of the decimal point
19052 * - e : the exponent for numbers that would need more than `MAX_DIGITS` digits in `d`
19055 function parse(numStr) {
19056 var exponent = 0, digits, numberOfIntegerDigits;
19060 if ((numberOfIntegerDigits = numStr.indexOf(DECIMAL_SEP)) > -1) {
19061 numStr = numStr.replace(DECIMAL_SEP, '');
19064 // Exponential form?
19065 if ((i = numStr.search(/e/i)) > 0) {
19066 // Work out the exponent.
19067 if (numberOfIntegerDigits < 0) numberOfIntegerDigits = i;
19068 numberOfIntegerDigits += +numStr.slice(i + 1);
19069 numStr = numStr.substring(0, i);
19070 } else if (numberOfIntegerDigits < 0) {
19071 // There was no decimal point or exponent so it is an integer.
19072 numberOfIntegerDigits = numStr.length;
19075 // Count the number of leading zeros.
19076 for (i = 0; numStr.charAt(i) == ZERO_CHAR; i++) {/* jshint noempty: false */}
19078 if (i == (zeros = numStr.length)) {
19079 // The digits are all zero.
19081 numberOfIntegerDigits = 1;
19083 // Count the number of trailing zeros
19085 while (numStr.charAt(zeros) == ZERO_CHAR) zeros--;
19087 // Trailing zeros are insignificant so ignore them
19088 numberOfIntegerDigits -= i;
19090 // Convert string to array of digits without leading/trailing zeros.
19091 for (j = 0; i <= zeros; i++, j++) {
19092 digits[j] = +numStr.charAt(i);
19096 // If the number overflows the maximum allowed digits then use an exponent.
19097 if (numberOfIntegerDigits > MAX_DIGITS) {
19098 digits = digits.splice(0, MAX_DIGITS - 1);
19099 exponent = numberOfIntegerDigits - 1;
19100 numberOfIntegerDigits = 1;
19103 return { d: digits, e: exponent, i: numberOfIntegerDigits };
19107 * Round the parsed number to the specified number of decimal places
19108 * This function changed the parsedNumber in-place
19110 function roundNumber(parsedNumber, fractionSize, minFrac, maxFrac) {
19111 var digits = parsedNumber.d;
19112 var fractionLen = digits.length - parsedNumber.i;
19114 // determine fractionSize if it is not specified; `+fractionSize` converts it to a number
19115 fractionSize = (isUndefined(fractionSize)) ? Math.min(Math.max(minFrac, fractionLen), maxFrac) : +fractionSize;
19117 // The index of the digit to where rounding is to occur
19118 var roundAt = fractionSize + parsedNumber.i;
19119 var digit = digits[roundAt];
19122 digits.splice(roundAt);
19124 // We rounded to zero so reset the parsedNumber
19125 parsedNumber.i = 1;
19126 digits.length = roundAt = fractionSize + 1;
19127 for (var i=0; i < roundAt; i++) digits[i] = 0;
19130 if (digit >= 5) digits[roundAt - 1]++;
19132 // Pad out with zeros to get the required fraction length
19133 for (; fractionLen < fractionSize; fractionLen++) digits.push(0);
19136 // Do any carrying, e.g. a digit was rounded up to 10
19137 var carry = digits.reduceRight(function(carry, d, i, digits) {
19139 digits[i] = d % 10;
19140 return Math.floor(d / 10);
19143 digits.unshift(carry);
19149 * Format a number into a string
19150 * @param {number} number The number to format
19152 * minFrac, // the minimum number of digits required in the fraction part of the number
19153 * maxFrac, // the maximum number of digits required in the fraction part of the number
19154 * gSize, // number of digits in each group of separated digits
19155 * lgSize, // number of digits in the last group of digits before the decimal separator
19156 * negPre, // the string to go in front of a negative number (e.g. `-` or `(`))
19157 * posPre, // the string to go in front of a positive number
19158 * negSuf, // the string to go after a negative number (e.g. `)`)
19159 * posSuf // the string to go after a positive number
19161 * @param {string} groupSep The string to separate groups of number (e.g. `,`)
19162 * @param {string} decimalSep The string to act as the decimal separator (e.g. `.`)
19163 * @param {[type]} fractionSize The size of the fractional part of the number
19164 * @return {string} The number formatted as a string
19166 function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) {
19168 if (!(isString(number) || isNumber(number)) || isNaN(number)) return '';
19170 var isInfinity = !isFinite(number);
19171 var isZero = false;
19172 var numStr = Math.abs(number) + '',
19173 formattedText = '',
19177 formattedText = '\u221e';
19179 parsedNumber = parse(numStr);
19181 roundNumber(parsedNumber, fractionSize, pattern.minFrac, pattern.maxFrac);
19183 var digits = parsedNumber.d;
19184 var integerLen = parsedNumber.i;
19185 var exponent = parsedNumber.e;
19187 isZero = digits.reduce(function(isZero, d) { return isZero && !d; }, true);
19189 // pad zeros for small numbers
19190 while (integerLen < 0) {
19195 // extract decimals digits
19196 if (integerLen > 0) {
19197 decimals = digits.splice(integerLen);
19203 // format the integer digits with grouping separators
19205 if (digits.length > pattern.lgSize) {
19206 groups.unshift(digits.splice(-pattern.lgSize).join(''));
19208 while (digits.length > pattern.gSize) {
19209 groups.unshift(digits.splice(-pattern.gSize).join(''));
19211 if (digits.length) {
19212 groups.unshift(digits.join(''));
19214 formattedText = groups.join(groupSep);
19216 // append the decimal digits
19217 if (decimals.length) {
19218 formattedText += decimalSep + decimals.join('');
19222 formattedText += 'e+' + exponent;
19225 if (number < 0 && !isZero) {
19226 return pattern.negPre + formattedText + pattern.negSuf;
19228 return pattern.posPre + formattedText + pattern.posSuf;
19232 function padNumber(num, digits, trim) {
19239 while (num.length < digits) num = ZERO_CHAR + num;
19241 num = num.substr(num.length - digits);
19247 function dateGetter(name, size, offset, trim) {
19248 offset = offset || 0;
19249 return function(date) {
19250 var value = date['get' + name]();
19251 if (offset > 0 || value > -offset) {
19254 if (value === 0 && offset == -12) value = 12;
19255 return padNumber(value, size, trim);
19259 function dateStrGetter(name, shortForm) {
19260 return function(date, formats) {
19261 var value = date['get' + name]();
19262 var get = uppercase(shortForm ? ('SHORT' + name) : name);
19264 return formats[get][value];
19268 function timeZoneGetter(date, formats, offset) {
19269 var zone = -1 * offset;
19270 var paddedZone = (zone >= 0) ? "+" : "";
19272 paddedZone += padNumber(Math[zone > 0 ? 'floor' : 'ceil'](zone / 60), 2) +
19273 padNumber(Math.abs(zone % 60), 2);
19278 function getFirstThursdayOfYear(year) {
19279 // 0 = index of January
19280 var dayOfWeekOnFirst = (new Date(year, 0, 1)).getDay();
19281 // 4 = index of Thursday (+1 to account for 1st = 5)
19282 // 11 = index of *next* Thursday (+1 account for 1st = 12)
19283 return new Date(year, 0, ((dayOfWeekOnFirst <= 4) ? 5 : 12) - dayOfWeekOnFirst);
19286 function getThursdayThisWeek(datetime) {
19287 return new Date(datetime.getFullYear(), datetime.getMonth(),
19288 // 4 = index of Thursday
19289 datetime.getDate() + (4 - datetime.getDay()));
19292 function weekGetter(size) {
19293 return function(date) {
19294 var firstThurs = getFirstThursdayOfYear(date.getFullYear()),
19295 thisThurs = getThursdayThisWeek(date);
19297 var diff = +thisThurs - +firstThurs,
19298 result = 1 + Math.round(diff / 6.048e8); // 6.048e8 ms per week
19300 return padNumber(result, size);
19304 function ampmGetter(date, formats) {
19305 return date.getHours() < 12 ? formats.AMPMS[0] : formats.AMPMS[1];
19308 function eraGetter(date, formats) {
19309 return date.getFullYear() <= 0 ? formats.ERAS[0] : formats.ERAS[1];
19312 function longEraGetter(date, formats) {
19313 return date.getFullYear() <= 0 ? formats.ERANAMES[0] : formats.ERANAMES[1];
19316 var DATE_FORMATS = {
19317 yyyy: dateGetter('FullYear', 4),
19318 yy: dateGetter('FullYear', 2, 0, true),
19319 y: dateGetter('FullYear', 1),
19320 MMMM: dateStrGetter('Month'),
19321 MMM: dateStrGetter('Month', true),
19322 MM: dateGetter('Month', 2, 1),
19323 M: dateGetter('Month', 1, 1),
19324 dd: dateGetter('Date', 2),
19325 d: dateGetter('Date', 1),
19326 HH: dateGetter('Hours', 2),
19327 H: dateGetter('Hours', 1),
19328 hh: dateGetter('Hours', 2, -12),
19329 h: dateGetter('Hours', 1, -12),
19330 mm: dateGetter('Minutes', 2),
19331 m: dateGetter('Minutes', 1),
19332 ss: dateGetter('Seconds', 2),
19333 s: dateGetter('Seconds', 1),
19334 // while ISO 8601 requires fractions to be prefixed with `.` or `,`
19335 // we can be just safely rely on using `sss` since we currently don't support single or two digit fractions
19336 sss: dateGetter('Milliseconds', 3),
19337 EEEE: dateStrGetter('Day'),
19338 EEE: dateStrGetter('Day', true),
19346 GGGG: longEraGetter
19349 var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZEwG']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z|G+|w+))(.*)/,
19350 NUMBER_STRING = /^\-?\d+$/;
19358 * Formats `date` to a string based on the requested `format`.
19360 * `format` string can be composed of the following elements:
19362 * * `'yyyy'`: 4 digit representation of year (e.g. AD 1 => 0001, AD 2010 => 2010)
19363 * * `'yy'`: 2 digit representation of year, padded (00-99). (e.g. AD 2001 => 01, AD 2010 => 10)
19364 * * `'y'`: 1 digit representation of year, e.g. (AD 1 => 1, AD 199 => 199)
19365 * * `'MMMM'`: Month in year (January-December)
19366 * * `'MMM'`: Month in year (Jan-Dec)
19367 * * `'MM'`: Month in year, padded (01-12)
19368 * * `'M'`: Month in year (1-12)
19369 * * `'dd'`: Day in month, padded (01-31)
19370 * * `'d'`: Day in month (1-31)
19371 * * `'EEEE'`: Day in Week,(Sunday-Saturday)
19372 * * `'EEE'`: Day in Week, (Sun-Sat)
19373 * * `'HH'`: Hour in day, padded (00-23)
19374 * * `'H'`: Hour in day (0-23)
19375 * * `'hh'`: Hour in AM/PM, padded (01-12)
19376 * * `'h'`: Hour in AM/PM, (1-12)
19377 * * `'mm'`: Minute in hour, padded (00-59)
19378 * * `'m'`: Minute in hour (0-59)
19379 * * `'ss'`: Second in minute, padded (00-59)
19380 * * `'s'`: Second in minute (0-59)
19381 * * `'sss'`: Millisecond in second, padded (000-999)
19382 * * `'a'`: AM/PM marker
19383 * * `'Z'`: 4 digit (+sign) representation of the timezone offset (-1200-+1200)
19384 * * `'ww'`: Week of year, padded (00-53). Week 01 is the week with the first Thursday of the year
19385 * * `'w'`: Week of year (0-53). Week 1 is the week with the first Thursday of the year
19386 * * `'G'`, `'GG'`, `'GGG'`: The abbreviated form of the era string (e.g. 'AD')
19387 * * `'GGGG'`: The long form of the era string (e.g. 'Anno Domini')
19389 * `format` string can also be one of the following predefined
19390 * {@link guide/i18n localizable formats}:
19392 * * `'medium'`: equivalent to `'MMM d, y h:mm:ss a'` for en_US locale
19393 * (e.g. Sep 3, 2010 12:05:08 PM)
19394 * * `'short'`: equivalent to `'M/d/yy h:mm a'` for en_US locale (e.g. 9/3/10 12:05 PM)
19395 * * `'fullDate'`: equivalent to `'EEEE, MMMM d, y'` for en_US locale
19396 * (e.g. Friday, September 3, 2010)
19397 * * `'longDate'`: equivalent to `'MMMM d, y'` for en_US locale (e.g. September 3, 2010)
19398 * * `'mediumDate'`: equivalent to `'MMM d, y'` for en_US locale (e.g. Sep 3, 2010)
19399 * * `'shortDate'`: equivalent to `'M/d/yy'` for en_US locale (e.g. 9/3/10)
19400 * * `'mediumTime'`: equivalent to `'h:mm:ss a'` for en_US locale (e.g. 12:05:08 PM)
19401 * * `'shortTime'`: equivalent to `'h:mm a'` for en_US locale (e.g. 12:05 PM)
19403 * `format` string can contain literal values. These need to be escaped by surrounding with single quotes (e.g.
19404 * `"h 'in the morning'"`). In order to output a single quote, escape it - i.e., two single quotes in a sequence
19405 * (e.g. `"h 'o''clock'"`).
19407 * @param {(Date|number|string)} date Date to format either as Date object, milliseconds (string or
19408 * number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.sssZ and its
19409 * shorter versions like yyyy-MM-ddTHH:mmZ, yyyy-MM-dd or yyyyMMddTHHmmssZ). If no timezone is
19410 * specified in the string input, the time is considered to be in the local timezone.
19411 * @param {string=} format Formatting rules (see Description). If not specified,
19412 * `mediumDate` is used.
19413 * @param {string=} timezone Timezone to be used for formatting. It understands UTC/GMT and the
19414 * continental US time zone abbreviations, but for general use, use a time zone offset, for
19415 * example, `'+0430'` (4 hours, 30 minutes east of the Greenwich meridian)
19416 * If not specified, the timezone of the browser will be used.
19417 * @returns {string} Formatted string or the input if input is not recognized as date/millis.
19421 <file name="index.html">
19422 <span ng-non-bindable>{{1288323623006 | date:'medium'}}</span>:
19423 <span>{{1288323623006 | date:'medium'}}</span><br>
19424 <span ng-non-bindable>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span>:
19425 <span>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span><br>
19426 <span ng-non-bindable>{{1288323623006 | date:'MM/dd/yyyy @ h:mma'}}</span>:
19427 <span>{{'1288323623006' | date:'MM/dd/yyyy @ h:mma'}}</span><br>
19428 <span ng-non-bindable>{{1288323623006 | date:"MM/dd/yyyy 'at' h:mma"}}</span>:
19429 <span>{{'1288323623006' | date:"MM/dd/yyyy 'at' h:mma"}}</span><br>
19431 <file name="protractor.js" type="protractor">
19432 it('should format date', function() {
19433 expect(element(by.binding("1288323623006 | date:'medium'")).getText()).
19434 toMatch(/Oct 2\d, 2010 \d{1,2}:\d{2}:\d{2} (AM|PM)/);
19435 expect(element(by.binding("1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'")).getText()).
19436 toMatch(/2010\-10\-2\d \d{2}:\d{2}:\d{2} (\-|\+)?\d{4}/);
19437 expect(element(by.binding("'1288323623006' | date:'MM/dd/yyyy @ h:mma'")).getText()).
19438 toMatch(/10\/2\d\/2010 @ \d{1,2}:\d{2}(AM|PM)/);
19439 expect(element(by.binding("'1288323623006' | date:\"MM/dd/yyyy 'at' h:mma\"")).getText()).
19440 toMatch(/10\/2\d\/2010 at \d{1,2}:\d{2}(AM|PM)/);
19445 dateFilter.$inject = ['$locale'];
19446 function dateFilter($locale) {
19449 var R_ISO8601_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/;
19450 // 1 2 3 4 5 6 7 8 9 10 11
19451 function jsonStringToDate(string) {
19453 if (match = string.match(R_ISO8601_STR)) {
19454 var date = new Date(0),
19457 dateSetter = match[8] ? date.setUTCFullYear : date.setFullYear,
19458 timeSetter = match[8] ? date.setUTCHours : date.setHours;
19461 tzHour = toInt(match[9] + match[10]);
19462 tzMin = toInt(match[9] + match[11]);
19464 dateSetter.call(date, toInt(match[1]), toInt(match[2]) - 1, toInt(match[3]));
19465 var h = toInt(match[4] || 0) - tzHour;
19466 var m = toInt(match[5] || 0) - tzMin;
19467 var s = toInt(match[6] || 0);
19468 var ms = Math.round(parseFloat('0.' + (match[7] || 0)) * 1000);
19469 timeSetter.call(date, h, m, s, ms);
19476 return function(date, format, timezone) {
19481 format = format || 'mediumDate';
19482 format = $locale.DATETIME_FORMATS[format] || format;
19483 if (isString(date)) {
19484 date = NUMBER_STRING.test(date) ? toInt(date) : jsonStringToDate(date);
19487 if (isNumber(date)) {
19488 date = new Date(date);
19491 if (!isDate(date) || !isFinite(date.getTime())) {
19496 match = DATE_FORMATS_SPLIT.exec(format);
19498 parts = concat(parts, match, 1);
19499 format = parts.pop();
19501 parts.push(format);
19506 var dateTimezoneOffset = date.getTimezoneOffset();
19508 dateTimezoneOffset = timezoneToOffset(timezone, dateTimezoneOffset);
19509 date = convertTimezoneToLocal(date, timezone, true);
19511 forEach(parts, function(value) {
19512 fn = DATE_FORMATS[value];
19513 text += fn ? fn(date, $locale.DATETIME_FORMATS, dateTimezoneOffset)
19514 : value === "''" ? "'" : value.replace(/(^'|'$)/g, '').replace(/''/g, "'");
19528 * Allows you to convert a JavaScript object into JSON string.
19530 * This filter is mostly useful for debugging. When using the double curly {{value}} notation
19531 * the binding is automatically converted to JSON.
19533 * @param {*} object Any JavaScript object (including arrays and primitive types) to filter.
19534 * @param {number=} spacing The number of spaces to use per indentation, defaults to 2.
19535 * @returns {string} JSON string.
19540 <file name="index.html">
19541 <pre id="default-spacing">{{ {'name':'value'} | json }}</pre>
19542 <pre id="custom-spacing">{{ {'name':'value'} | json:4 }}</pre>
19544 <file name="protractor.js" type="protractor">
19545 it('should jsonify filtered objects', function() {
19546 expect(element(by.id('default-spacing')).getText()).toMatch(/\{\n "name": ?"value"\n}/);
19547 expect(element(by.id('custom-spacing')).getText()).toMatch(/\{\n "name": ?"value"\n}/);
19553 function jsonFilter() {
19554 return function(object, spacing) {
19555 if (isUndefined(spacing)) {
19558 return toJson(object, spacing);
19568 * Converts string to lowercase.
19569 * @see angular.lowercase
19571 var lowercaseFilter = valueFn(lowercase);
19579 * Converts string to uppercase.
19580 * @see angular.uppercase
19582 var uppercaseFilter = valueFn(uppercase);
19590 * Creates a new array or string containing only a specified number of elements. The elements
19591 * are taken from either the beginning or the end of the source array, string or number, as specified by
19592 * the value and sign (positive or negative) of `limit`. If a number is used as input, it is
19593 * converted to a string.
19595 * @param {Array|string|number} input Source array, string or number to be limited.
19596 * @param {string|number} limit The length of the returned array or string. If the `limit` number
19597 * is positive, `limit` number of items from the beginning of the source array/string are copied.
19598 * If the number is negative, `limit` number of items from the end of the source array/string
19599 * are copied. The `limit` will be trimmed if it exceeds `array.length`. If `limit` is undefined,
19600 * the input will be returned unchanged.
19601 * @param {(string|number)=} begin Index at which to begin limitation. As a negative index, `begin`
19602 * indicates an offset from the end of `input`. Defaults to `0`.
19603 * @returns {Array|string} A new sub-array or substring of length `limit` or less if input array
19604 * had less than `limit` elements.
19607 <example module="limitToExample">
19608 <file name="index.html">
19610 angular.module('limitToExample', [])
19611 .controller('ExampleController', ['$scope', function($scope) {
19612 $scope.numbers = [1,2,3,4,5,6,7,8,9];
19613 $scope.letters = "abcdefghi";
19614 $scope.longNumber = 2345432342;
19615 $scope.numLimit = 3;
19616 $scope.letterLimit = 3;
19617 $scope.longNumberLimit = 3;
19620 <div ng-controller="ExampleController">
19622 Limit {{numbers}} to:
19623 <input type="number" step="1" ng-model="numLimit">
19625 <p>Output numbers: {{ numbers | limitTo:numLimit }}</p>
19627 Limit {{letters}} to:
19628 <input type="number" step="1" ng-model="letterLimit">
19630 <p>Output letters: {{ letters | limitTo:letterLimit }}</p>
19632 Limit {{longNumber}} to:
19633 <input type="number" step="1" ng-model="longNumberLimit">
19635 <p>Output long number: {{ longNumber | limitTo:longNumberLimit }}</p>
19638 <file name="protractor.js" type="protractor">
19639 var numLimitInput = element(by.model('numLimit'));
19640 var letterLimitInput = element(by.model('letterLimit'));
19641 var longNumberLimitInput = element(by.model('longNumberLimit'));
19642 var limitedNumbers = element(by.binding('numbers | limitTo:numLimit'));
19643 var limitedLetters = element(by.binding('letters | limitTo:letterLimit'));
19644 var limitedLongNumber = element(by.binding('longNumber | limitTo:longNumberLimit'));
19646 it('should limit the number array to first three items', function() {
19647 expect(numLimitInput.getAttribute('value')).toBe('3');
19648 expect(letterLimitInput.getAttribute('value')).toBe('3');
19649 expect(longNumberLimitInput.getAttribute('value')).toBe('3');
19650 expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3]');
19651 expect(limitedLetters.getText()).toEqual('Output letters: abc');
19652 expect(limitedLongNumber.getText()).toEqual('Output long number: 234');
19655 // There is a bug in safari and protractor that doesn't like the minus key
19656 // it('should update the output when -3 is entered', function() {
19657 // numLimitInput.clear();
19658 // numLimitInput.sendKeys('-3');
19659 // letterLimitInput.clear();
19660 // letterLimitInput.sendKeys('-3');
19661 // longNumberLimitInput.clear();
19662 // longNumberLimitInput.sendKeys('-3');
19663 // expect(limitedNumbers.getText()).toEqual('Output numbers: [7,8,9]');
19664 // expect(limitedLetters.getText()).toEqual('Output letters: ghi');
19665 // expect(limitedLongNumber.getText()).toEqual('Output long number: 342');
19668 it('should not exceed the maximum size of input array', function() {
19669 numLimitInput.clear();
19670 numLimitInput.sendKeys('100');
19671 letterLimitInput.clear();
19672 letterLimitInput.sendKeys('100');
19673 longNumberLimitInput.clear();
19674 longNumberLimitInput.sendKeys('100');
19675 expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3,4,5,6,7,8,9]');
19676 expect(limitedLetters.getText()).toEqual('Output letters: abcdefghi');
19677 expect(limitedLongNumber.getText()).toEqual('Output long number: 2345432342');
19682 function limitToFilter() {
19683 return function(input, limit, begin) {
19684 if (Math.abs(Number(limit)) === Infinity) {
19685 limit = Number(limit);
19687 limit = toInt(limit);
19689 if (isNaN(limit)) return input;
19691 if (isNumber(input)) input = input.toString();
19692 if (!isArray(input) && !isString(input)) return input;
19694 begin = (!begin || isNaN(begin)) ? 0 : toInt(begin);
19695 begin = (begin < 0) ? Math.max(0, input.length + begin) : begin;
19698 return input.slice(begin, begin + limit);
19701 return input.slice(limit, input.length);
19703 return input.slice(Math.max(0, begin + limit), begin);
19715 * Orders a specified `array` by the `expression` predicate. It is ordered alphabetically
19716 * for strings and numerically for numbers. Note: if you notice numbers are not being sorted
19717 * as expected, make sure they are actually being saved as numbers and not strings.
19719 * @param {Array} array The array to sort.
19720 * @param {function(*)|string|Array.<(function(*)|string)>=} expression A predicate to be
19721 * used by the comparator to determine the order of elements.
19725 * - `function`: Getter function. The result of this function will be sorted using the
19726 * `<`, `===`, `>` operator.
19727 * - `string`: An Angular expression. The result of this expression is used to compare elements
19728 * (for example `name` to sort by a property called `name` or `name.substr(0, 3)` to sort by
19729 * 3 first characters of a property called `name`). The result of a constant expression
19730 * is interpreted as a property name to be used in comparisons (for example `"special name"`
19731 * to sort object by the value of their `special name` property). An expression can be
19732 * optionally prefixed with `+` or `-` to control ascending or descending sort order
19733 * (for example, `+name` or `-name`). If no property is provided, (e.g. `'+'`) then the array
19734 * element itself is used to compare where sorting.
19735 * - `Array`: An array of function or string predicates. The first predicate in the array
19736 * is used for sorting, but when two items are equivalent, the next predicate is used.
19738 * If the predicate is missing or empty then it defaults to `'+'`.
19740 * @param {boolean=} reverse Reverse the order of the array.
19741 * @returns {Array} Sorted copy of the source array.
19745 * The example below demonstrates a simple ngRepeat, where the data is sorted
19746 * by age in descending order (predicate is set to `'-age'`).
19747 * `reverse` is not set, which means it defaults to `false`.
19748 <example module="orderByExample">
19749 <file name="index.html">
19750 <div ng-controller="ExampleController">
19751 <table class="friend">
19754 <th>Phone Number</th>
19757 <tr ng-repeat="friend in friends | orderBy:'-age'">
19758 <td>{{friend.name}}</td>
19759 <td>{{friend.phone}}</td>
19760 <td>{{friend.age}}</td>
19765 <file name="script.js">
19766 angular.module('orderByExample', [])
19767 .controller('ExampleController', ['$scope', function($scope) {
19769 [{name:'John', phone:'555-1212', age:10},
19770 {name:'Mary', phone:'555-9876', age:19},
19771 {name:'Mike', phone:'555-4321', age:21},
19772 {name:'Adam', phone:'555-5678', age:35},
19773 {name:'Julie', phone:'555-8765', age:29}];
19778 * The predicate and reverse parameters can be controlled dynamically through scope properties,
19779 * as shown in the next example.
19781 <example module="orderByExample">
19782 <file name="index.html">
19783 <div ng-controller="ExampleController">
19784 <pre>Sorting predicate = {{predicate}}; reverse = {{reverse}}</pre>
19786 <button ng-click="predicate=''">Set to unsorted</button>
19787 <table class="friend">
19790 <button ng-click="order('name')">Name</button>
19791 <span class="sortorder" ng-show="predicate === 'name'" ng-class="{reverse:reverse}"></span>
19794 <button ng-click="order('phone')">Phone Number</button>
19795 <span class="sortorder" ng-show="predicate === 'phone'" ng-class="{reverse:reverse}"></span>
19798 <button ng-click="order('age')">Age</button>
19799 <span class="sortorder" ng-show="predicate === 'age'" ng-class="{reverse:reverse}"></span>
19802 <tr ng-repeat="friend in friends | orderBy:predicate:reverse">
19803 <td>{{friend.name}}</td>
19804 <td>{{friend.phone}}</td>
19805 <td>{{friend.age}}</td>
19810 <file name="script.js">
19811 angular.module('orderByExample', [])
19812 .controller('ExampleController', ['$scope', function($scope) {
19814 [{name:'John', phone:'555-1212', age:10},
19815 {name:'Mary', phone:'555-9876', age:19},
19816 {name:'Mike', phone:'555-4321', age:21},
19817 {name:'Adam', phone:'555-5678', age:35},
19818 {name:'Julie', phone:'555-8765', age:29}];
19819 $scope.predicate = 'age';
19820 $scope.reverse = true;
19821 $scope.order = function(predicate) {
19822 $scope.reverse = ($scope.predicate === predicate) ? !$scope.reverse : false;
19823 $scope.predicate = predicate;
19827 <file name="style.css">
19831 .sortorder.reverse:after {
19837 * It's also possible to call the orderBy filter manually, by injecting `$filter`, retrieving the
19838 * filter routine with `$filter('orderBy')`, and calling the returned filter routine with the
19839 * desired parameters.
19844 <example module="orderByExample">
19845 <file name="index.html">
19846 <div ng-controller="ExampleController">
19847 <pre>Sorting predicate = {{predicate}}; reverse = {{reverse}}</pre>
19848 <table class="friend">
19851 <button ng-click="order('name')">Name</button>
19852 <span class="sortorder" ng-show="predicate === 'name'" ng-class="{reverse:reverse}"></span>
19855 <button ng-click="order('phone')">Phone Number</button>
19856 <span class="sortorder" ng-show="predicate === 'phone'" ng-class="{reverse:reverse}"></span>
19859 <button ng-click="order('age')">Age</button>
19860 <span class="sortorder" ng-show="predicate === 'age'" ng-class="{reverse:reverse}"></span>
19863 <tr ng-repeat="friend in friends">
19864 <td>{{friend.name}}</td>
19865 <td>{{friend.phone}}</td>
19866 <td>{{friend.age}}</td>
19872 <file name="script.js">
19873 angular.module('orderByExample', [])
19874 .controller('ExampleController', ['$scope', '$filter', function($scope, $filter) {
19875 var orderBy = $filter('orderBy');
19877 { name: 'John', phone: '555-1212', age: 10 },
19878 { name: 'Mary', phone: '555-9876', age: 19 },
19879 { name: 'Mike', phone: '555-4321', age: 21 },
19880 { name: 'Adam', phone: '555-5678', age: 35 },
19881 { name: 'Julie', phone: '555-8765', age: 29 }
19883 $scope.order = function(predicate) {
19884 $scope.predicate = predicate;
19885 $scope.reverse = ($scope.predicate === predicate) ? !$scope.reverse : false;
19886 $scope.friends = orderBy($scope.friends, predicate, $scope.reverse);
19888 $scope.order('age', true);
19892 <file name="style.css">
19896 .sortorder.reverse:after {
19902 orderByFilter.$inject = ['$parse'];
19903 function orderByFilter($parse) {
19904 return function(array, sortPredicate, reverseOrder) {
19906 if (!(isArrayLike(array))) return array;
19908 if (!isArray(sortPredicate)) { sortPredicate = [sortPredicate]; }
19909 if (sortPredicate.length === 0) { sortPredicate = ['+']; }
19911 var predicates = processPredicates(sortPredicate, reverseOrder);
19912 // Add a predicate at the end that evaluates to the element index. This makes the
19913 // sort stable as it works as a tie-breaker when all the input predicates cannot
19914 // distinguish between two elements.
19915 predicates.push({ get: function() { return {}; }, descending: reverseOrder ? -1 : 1});
19917 // The next three lines are a version of a Swartzian Transform idiom from Perl
19918 // (sometimes called the Decorate-Sort-Undecorate idiom)
19919 // See https://en.wikipedia.org/wiki/Schwartzian_transform
19920 var compareValues = Array.prototype.map.call(array, getComparisonObject);
19921 compareValues.sort(doComparison);
19922 array = compareValues.map(function(item) { return item.value; });
19926 function getComparisonObject(value, index) {
19929 predicateValues: predicates.map(function(predicate) {
19930 return getPredicateValue(predicate.get(value), index);
19935 function doComparison(v1, v2) {
19937 for (var index=0, length = predicates.length; index < length; ++index) {
19938 result = compare(v1.predicateValues[index], v2.predicateValues[index]) * predicates[index].descending;
19945 function processPredicates(sortPredicate, reverseOrder) {
19946 reverseOrder = reverseOrder ? -1 : 1;
19947 return sortPredicate.map(function(predicate) {
19948 var descending = 1, get = identity;
19950 if (isFunction(predicate)) {
19952 } else if (isString(predicate)) {
19953 if ((predicate.charAt(0) == '+' || predicate.charAt(0) == '-')) {
19954 descending = predicate.charAt(0) == '-' ? -1 : 1;
19955 predicate = predicate.substring(1);
19957 if (predicate !== '') {
19958 get = $parse(predicate);
19959 if (get.constant) {
19961 get = function(value) { return value[key]; };
19965 return { get: get, descending: descending * reverseOrder };
19969 function isPrimitive(value) {
19970 switch (typeof value) {
19971 case 'number': /* falls through */
19972 case 'boolean': /* falls through */
19980 function objectValue(value, index) {
19981 // If `valueOf` is a valid function use that
19982 if (typeof value.valueOf === 'function') {
19983 value = value.valueOf();
19984 if (isPrimitive(value)) return value;
19986 // If `toString` is a valid function and not the one from `Object.prototype` use that
19987 if (hasCustomToString(value)) {
19988 value = value.toString();
19989 if (isPrimitive(value)) return value;
19991 // We have a basic object so we use the position of the object in the collection
19995 function getPredicateValue(value, index) {
19996 var type = typeof value;
19997 if (value === null) {
20000 } else if (type === 'string') {
20001 value = value.toLowerCase();
20002 } else if (type === 'object') {
20003 value = objectValue(value, index);
20005 return { value: value, type: type };
20008 function compare(v1, v2) {
20010 if (v1.type === v2.type) {
20011 if (v1.value !== v2.value) {
20012 result = v1.value < v2.value ? -1 : 1;
20015 result = v1.type < v2.type ? -1 : 1;
20021 function ngDirective(directive) {
20022 if (isFunction(directive)) {
20027 directive.restrict = directive.restrict || 'AC';
20028 return valueFn(directive);
20037 * Modifies the default behavior of the html A tag so that the default action is prevented when
20038 * the href attribute is empty.
20040 * This change permits the easy creation of action links with the `ngClick` directive
20041 * without changing the location or causing page reloads, e.g.:
20042 * `<a href="" ng-click="list.addItem()">Add Item</a>`
20044 var htmlAnchorDirective = valueFn({
20046 compile: function(element, attr) {
20047 if (!attr.href && !attr.xlinkHref) {
20048 return function(scope, element) {
20049 // If the linked element is not an anchor tag anymore, do nothing
20050 if (element[0].nodeName.toLowerCase() !== 'a') return;
20052 // SVGAElement does not use the href attribute, but rather the 'xlinkHref' attribute.
20053 var href = toString.call(element.prop('href')) === '[object SVGAnimatedString]' ?
20054 'xlink:href' : 'href';
20055 element.on('click', function(event) {
20056 // if we have no href url, then don't navigate anywhere.
20057 if (!element.attr(href)) {
20058 event.preventDefault();
20073 * Using Angular markup like `{{hash}}` in an href attribute will
20074 * make the link go to the wrong URL if the user clicks it before
20075 * Angular has a chance to replace the `{{hash}}` markup with its
20076 * value. Until Angular replaces the markup the link will be broken
20077 * and will most likely return a 404 error. The `ngHref` directive
20078 * solves this problem.
20080 * The wrong way to write it:
20082 * <a href="http://www.gravatar.com/avatar/{{hash}}">link1</a>
20085 * The correct way to write it:
20087 * <a ng-href="http://www.gravatar.com/avatar/{{hash}}">link1</a>
20091 * @param {template} ngHref any string which can contain `{{}}` markup.
20094 * This example shows various combinations of `href`, `ng-href` and `ng-click` attributes
20095 * in links and their different behaviors:
20097 <file name="index.html">
20098 <input ng-model="value" /><br />
20099 <a id="link-1" href ng-click="value = 1">link 1</a> (link, don't reload)<br />
20100 <a id="link-2" href="" ng-click="value = 2">link 2</a> (link, don't reload)<br />
20101 <a id="link-3" ng-href="/{{'123'}}">link 3</a> (link, reload!)<br />
20102 <a id="link-4" href="" name="xx" ng-click="value = 4">anchor</a> (link, don't reload)<br />
20103 <a id="link-5" name="xxx" ng-click="value = 5">anchor</a> (no link)<br />
20104 <a id="link-6" ng-href="{{value}}">link</a> (link, change location)
20106 <file name="protractor.js" type="protractor">
20107 it('should execute ng-click but not reload when href without value', function() {
20108 element(by.id('link-1')).click();
20109 expect(element(by.model('value')).getAttribute('value')).toEqual('1');
20110 expect(element(by.id('link-1')).getAttribute('href')).toBe('');
20113 it('should execute ng-click but not reload when href empty string', function() {
20114 element(by.id('link-2')).click();
20115 expect(element(by.model('value')).getAttribute('value')).toEqual('2');
20116 expect(element(by.id('link-2')).getAttribute('href')).toBe('');
20119 it('should execute ng-click and change url when ng-href specified', function() {
20120 expect(element(by.id('link-3')).getAttribute('href')).toMatch(/\/123$/);
20122 element(by.id('link-3')).click();
20124 // At this point, we navigate away from an Angular page, so we need
20125 // to use browser.driver to get the base webdriver.
20127 browser.wait(function() {
20128 return browser.driver.getCurrentUrl().then(function(url) {
20129 return url.match(/\/123$/);
20131 }, 5000, 'page should navigate to /123');
20134 it('should execute ng-click but not reload when href empty string and name specified', function() {
20135 element(by.id('link-4')).click();
20136 expect(element(by.model('value')).getAttribute('value')).toEqual('4');
20137 expect(element(by.id('link-4')).getAttribute('href')).toBe('');
20140 it('should execute ng-click but not reload when no href but name specified', function() {
20141 element(by.id('link-5')).click();
20142 expect(element(by.model('value')).getAttribute('value')).toEqual('5');
20143 expect(element(by.id('link-5')).getAttribute('href')).toBe(null);
20146 it('should only change url when only ng-href', function() {
20147 element(by.model('value')).clear();
20148 element(by.model('value')).sendKeys('6');
20149 expect(element(by.id('link-6')).getAttribute('href')).toMatch(/\/6$/);
20151 element(by.id('link-6')).click();
20153 // At this point, we navigate away from an Angular page, so we need
20154 // to use browser.driver to get the base webdriver.
20155 browser.wait(function() {
20156 return browser.driver.getCurrentUrl().then(function(url) {
20157 return url.match(/\/6$/);
20159 }, 5000, 'page should navigate to /6');
20172 * Using Angular markup like `{{hash}}` in a `src` attribute doesn't
20173 * work right: The browser will fetch from the URL with the literal
20174 * text `{{hash}}` until Angular replaces the expression inside
20175 * `{{hash}}`. The `ngSrc` directive solves this problem.
20177 * The buggy way to write it:
20179 * <img src="http://www.gravatar.com/avatar/{{hash}}" alt="Description"/>
20182 * The correct way to write it:
20184 * <img ng-src="http://www.gravatar.com/avatar/{{hash}}" alt="Description" />
20188 * @param {template} ngSrc any string which can contain `{{}}` markup.
20198 * Using Angular markup like `{{hash}}` in a `srcset` attribute doesn't
20199 * work right: The browser will fetch from the URL with the literal
20200 * text `{{hash}}` until Angular replaces the expression inside
20201 * `{{hash}}`. The `ngSrcset` directive solves this problem.
20203 * The buggy way to write it:
20205 * <img srcset="http://www.gravatar.com/avatar/{{hash}} 2x" alt="Description"/>
20208 * The correct way to write it:
20210 * <img ng-srcset="http://www.gravatar.com/avatar/{{hash}} 2x" alt="Description" />
20214 * @param {template} ngSrcset any string which can contain `{{}}` markup.
20225 * This directive sets the `disabled` attribute on the element if the
20226 * {@link guide/expression expression} inside `ngDisabled` evaluates to truthy.
20228 * A special directive is necessary because we cannot use interpolation inside the `disabled`
20229 * attribute. See the {@link guide/interpolation interpolation guide} for more info.
20233 <file name="index.html">
20234 <label>Click me to toggle: <input type="checkbox" ng-model="checked"></label><br/>
20235 <button ng-model="button" ng-disabled="checked">Button</button>
20237 <file name="protractor.js" type="protractor">
20238 it('should toggle button', function() {
20239 expect(element(by.css('button')).getAttribute('disabled')).toBeFalsy();
20240 element(by.model('checked')).click();
20241 expect(element(by.css('button')).getAttribute('disabled')).toBeTruthy();
20247 * @param {expression} ngDisabled If the {@link guide/expression expression} is truthy,
20248 * then the `disabled` attribute will be set on the element
20259 * Sets the `checked` attribute on the element, if the expression inside `ngChecked` is truthy.
20261 * Note that this directive should not be used together with {@link ngModel `ngModel`},
20262 * as this can lead to unexpected behavior.
20264 * A special directive is necessary because we cannot use interpolation inside the `checked`
20265 * attribute. See the {@link guide/interpolation interpolation guide} for more info.
20269 <file name="index.html">
20270 <label>Check me to check both: <input type="checkbox" ng-model="master"></label><br/>
20271 <input id="checkSlave" type="checkbox" ng-checked="master" aria-label="Slave input">
20273 <file name="protractor.js" type="protractor">
20274 it('should check both checkBoxes', function() {
20275 expect(element(by.id('checkSlave')).getAttribute('checked')).toBeFalsy();
20276 element(by.model('master')).click();
20277 expect(element(by.id('checkSlave')).getAttribute('checked')).toBeTruthy();
20283 * @param {expression} ngChecked If the {@link guide/expression expression} is truthy,
20284 * then the `checked` attribute will be set on the element
20296 * Sets the `readOnly` attribute on the element, if the expression inside `ngReadonly` is truthy.
20298 * A special directive is necessary because we cannot use interpolation inside the `readOnly`
20299 * attribute. See the {@link guide/interpolation interpolation guide} for more info.
20303 <file name="index.html">
20304 <label>Check me to make text readonly: <input type="checkbox" ng-model="checked"></label><br/>
20305 <input type="text" ng-readonly="checked" value="I'm Angular" aria-label="Readonly field" />
20307 <file name="protractor.js" type="protractor">
20308 it('should toggle readonly attr', function() {
20309 expect(element(by.css('[type="text"]')).getAttribute('readonly')).toBeFalsy();
20310 element(by.model('checked')).click();
20311 expect(element(by.css('[type="text"]')).getAttribute('readonly')).toBeTruthy();
20317 * @param {expression} ngReadonly If the {@link guide/expression expression} is truthy,
20318 * then special attribute "readonly" will be set on the element
20330 * Sets the `selected` attribute on the element, if the expression inside `ngSelected` is truthy.
20332 * A special directive is necessary because we cannot use interpolation inside the `selected`
20333 * attribute. See the {@link guide/interpolation interpolation guide} for more info.
20337 <file name="index.html">
20338 <label>Check me to select: <input type="checkbox" ng-model="selected"></label><br/>
20339 <select aria-label="ngSelected demo">
20340 <option>Hello!</option>
20341 <option id="greet" ng-selected="selected">Greetings!</option>
20344 <file name="protractor.js" type="protractor">
20345 it('should select Greetings!', function() {
20346 expect(element(by.id('greet')).getAttribute('selected')).toBeFalsy();
20347 element(by.model('selected')).click();
20348 expect(element(by.id('greet')).getAttribute('selected')).toBeTruthy();
20354 * @param {expression} ngSelected If the {@link guide/expression expression} is truthy,
20355 * then special attribute "selected" will be set on the element
20366 * Sets the `open` attribute on the element, if the expression inside `ngOpen` is truthy.
20368 * A special directive is necessary because we cannot use interpolation inside the `open`
20369 * attribute. See the {@link guide/interpolation interpolation guide} for more info.
20373 <file name="index.html">
20374 <label>Check me check multiple: <input type="checkbox" ng-model="open"></label><br/>
20375 <details id="details" ng-open="open">
20376 <summary>Show/Hide me</summary>
20379 <file name="protractor.js" type="protractor">
20380 it('should toggle open', function() {
20381 expect(element(by.id('details')).getAttribute('open')).toBeFalsy();
20382 element(by.model('open')).click();
20383 expect(element(by.id('details')).getAttribute('open')).toBeTruthy();
20389 * @param {expression} ngOpen If the {@link guide/expression expression} is truthy,
20390 * then special attribute "open" will be set on the element
20393 var ngAttributeAliasDirectives = {};
20395 // boolean attrs are evaluated
20396 forEach(BOOLEAN_ATTR, function(propName, attrName) {
20397 // binding to multiple is not supported
20398 if (propName == "multiple") return;
20400 function defaultLinkFn(scope, element, attr) {
20401 scope.$watch(attr[normalized], function ngBooleanAttrWatchAction(value) {
20402 attr.$set(attrName, !!value);
20406 var normalized = directiveNormalize('ng-' + attrName);
20407 var linkFn = defaultLinkFn;
20409 if (propName === 'checked') {
20410 linkFn = function(scope, element, attr) {
20411 // ensuring ngChecked doesn't interfere with ngModel when both are set on the same input
20412 if (attr.ngModel !== attr[normalized]) {
20413 defaultLinkFn(scope, element, attr);
20418 ngAttributeAliasDirectives[normalized] = function() {
20427 // aliased input attrs are evaluated
20428 forEach(ALIASED_ATTR, function(htmlAttr, ngAttr) {
20429 ngAttributeAliasDirectives[ngAttr] = function() {
20432 link: function(scope, element, attr) {
20433 //special case ngPattern when a literal regular expression value
20434 //is used as the expression (this way we don't have to watch anything).
20435 if (ngAttr === "ngPattern" && attr.ngPattern.charAt(0) == "/") {
20436 var match = attr.ngPattern.match(REGEX_STRING_REGEXP);
20438 attr.$set("ngPattern", new RegExp(match[1], match[2]));
20443 scope.$watch(attr[ngAttr], function ngAttrAliasWatchAction(value) {
20444 attr.$set(ngAttr, value);
20451 // ng-src, ng-srcset, ng-href are interpolated
20452 forEach(['src', 'srcset', 'href'], function(attrName) {
20453 var normalized = directiveNormalize('ng-' + attrName);
20454 ngAttributeAliasDirectives[normalized] = function() {
20456 priority: 99, // it needs to run after the attributes are interpolated
20457 link: function(scope, element, attr) {
20458 var propName = attrName,
20461 if (attrName === 'href' &&
20462 toString.call(element.prop('href')) === '[object SVGAnimatedString]') {
20463 name = 'xlinkHref';
20464 attr.$attr[name] = 'xlink:href';
20468 attr.$observe(normalized, function(value) {
20470 if (attrName === 'href') {
20471 attr.$set(name, null);
20476 attr.$set(name, value);
20478 // on IE, if "ng:src" directive declaration is used and "src" attribute doesn't exist
20479 // then calling element.setAttribute('src', 'foo') doesn't do anything, so we need
20480 // to set the property as well to achieve the desired effect.
20481 // we use attr[attrName] value since $set can sanitize the url.
20482 if (msie && propName) element.prop(propName, attr[name]);
20489 /* global -nullFormCtrl, -SUBMITTED_CLASS, addSetValidityMethod: true
20491 var nullFormCtrl = {
20493 $$renameControl: nullFormRenameControl,
20494 $removeControl: noop,
20495 $setValidity: noop,
20497 $setPristine: noop,
20498 $setSubmitted: noop
20500 SUBMITTED_CLASS = 'ng-submitted';
20502 function nullFormRenameControl(control, name) {
20503 control.$name = name;
20508 * @name form.FormController
20510 * @property {boolean} $pristine True if user has not interacted with the form yet.
20511 * @property {boolean} $dirty True if user has already interacted with the form.
20512 * @property {boolean} $valid True if all of the containing forms and controls are valid.
20513 * @property {boolean} $invalid True if at least one containing control or form is invalid.
20514 * @property {boolean} $pending True if at least one containing control or form is pending.
20515 * @property {boolean} $submitted True if user has submitted the form even if its invalid.
20517 * @property {Object} $error Is an object hash, containing references to controls or
20518 * forms with failing validators, where:
20520 * - keys are validation tokens (error names),
20521 * - values are arrays of controls or forms that have a failing validator for given error name.
20523 * Built-in validation tokens:
20535 * - `datetimelocal`
20541 * `FormController` keeps track of all its controls and nested forms as well as the state of them,
20542 * such as being valid/invalid or dirty/pristine.
20544 * Each {@link ng.directive:form form} directive creates an instance
20545 * of `FormController`.
20548 //asks for $scope to fool the BC controller module
20549 FormController.$inject = ['$element', '$attrs', '$scope', '$animate', '$interpolate'];
20550 function FormController(element, attrs, $scope, $animate, $interpolate) {
20556 form.$$success = {};
20557 form.$pending = undefined;
20558 form.$name = $interpolate(attrs.name || attrs.ngForm || '')($scope);
20559 form.$dirty = false;
20560 form.$pristine = true;
20561 form.$valid = true;
20562 form.$invalid = false;
20563 form.$submitted = false;
20564 form.$$parentForm = nullFormCtrl;
20568 * @name form.FormController#$rollbackViewValue
20571 * Rollback all form controls pending updates to the `$modelValue`.
20573 * Updates may be pending by a debounced event or because the input is waiting for a some future
20574 * event defined in `ng-model-options`. This method is typically needed by the reset button of
20575 * a form that uses `ng-model-options` to pend updates.
20577 form.$rollbackViewValue = function() {
20578 forEach(controls, function(control) {
20579 control.$rollbackViewValue();
20585 * @name form.FormController#$commitViewValue
20588 * Commit all form controls pending updates to the `$modelValue`.
20590 * Updates may be pending by a debounced event or because the input is waiting for a some future
20591 * event defined in `ng-model-options`. This method is rarely needed as `NgModelController`
20592 * usually handles calling this in response to input events.
20594 form.$commitViewValue = function() {
20595 forEach(controls, function(control) {
20596 control.$commitViewValue();
20602 * @name form.FormController#$addControl
20603 * @param {object} control control object, either a {@link form.FormController} or an
20604 * {@link ngModel.NgModelController}
20607 * Register a control with the form. Input elements using ngModelController do this automatically
20608 * when they are linked.
20610 * Note that the current state of the control will not be reflected on the new parent form. This
20611 * is not an issue with normal use, as freshly compiled and linked controls are in a `$pristine`
20614 * However, if the method is used programmatically, for example by adding dynamically created controls,
20615 * or controls that have been previously removed without destroying their corresponding DOM element,
20616 * it's the developers responsiblity to make sure the current state propagates to the parent form.
20618 * For example, if an input control is added that is already `$dirty` and has `$error` properties,
20619 * calling `$setDirty()` and `$validate()` afterwards will propagate the state to the parent form.
20621 form.$addControl = function(control) {
20622 // Breaking change - before, inputs whose name was "hasOwnProperty" were quietly ignored
20623 // and not added to the scope. Now we throw an error.
20624 assertNotHasOwnProperty(control.$name, 'input');
20625 controls.push(control);
20627 if (control.$name) {
20628 form[control.$name] = control;
20631 control.$$parentForm = form;
20634 // Private API: rename a form control
20635 form.$$renameControl = function(control, newName) {
20636 var oldName = control.$name;
20638 if (form[oldName] === control) {
20639 delete form[oldName];
20641 form[newName] = control;
20642 control.$name = newName;
20647 * @name form.FormController#$removeControl
20648 * @param {object} control control object, either a {@link form.FormController} or an
20649 * {@link ngModel.NgModelController}
20652 * Deregister a control from the form.
20654 * Input elements using ngModelController do this automatically when they are destroyed.
20656 * Note that only the removed control's validation state (`$errors`etc.) will be removed from the
20657 * form. `$dirty`, `$submitted` states will not be changed, because the expected behavior can be
20658 * different from case to case. For example, removing the only `$dirty` control from a form may or
20659 * may not mean that the form is still `$dirty`.
20661 form.$removeControl = function(control) {
20662 if (control.$name && form[control.$name] === control) {
20663 delete form[control.$name];
20665 forEach(form.$pending, function(value, name) {
20666 form.$setValidity(name, null, control);
20668 forEach(form.$error, function(value, name) {
20669 form.$setValidity(name, null, control);
20671 forEach(form.$$success, function(value, name) {
20672 form.$setValidity(name, null, control);
20675 arrayRemove(controls, control);
20676 control.$$parentForm = nullFormCtrl;
20682 * @name form.FormController#$setValidity
20685 * Sets the validity of a form control.
20687 * This method will also propagate to parent forms.
20689 addSetValidityMethod({
20692 set: function(object, property, controller) {
20693 var list = object[property];
20695 object[property] = [controller];
20697 var index = list.indexOf(controller);
20698 if (index === -1) {
20699 list.push(controller);
20703 unset: function(object, property, controller) {
20704 var list = object[property];
20708 arrayRemove(list, controller);
20709 if (list.length === 0) {
20710 delete object[property];
20718 * @name form.FormController#$setDirty
20721 * Sets the form to a dirty state.
20723 * This method can be called to add the 'ng-dirty' class and set the form to a dirty
20724 * state (ng-dirty class). This method will also propagate to parent forms.
20726 form.$setDirty = function() {
20727 $animate.removeClass(element, PRISTINE_CLASS);
20728 $animate.addClass(element, DIRTY_CLASS);
20729 form.$dirty = true;
20730 form.$pristine = false;
20731 form.$$parentForm.$setDirty();
20736 * @name form.FormController#$setPristine
20739 * Sets the form to its pristine state.
20741 * This method can be called to remove the 'ng-dirty' class and set the form to its pristine
20742 * state (ng-pristine class). This method will also propagate to all the controls contained
20745 * Setting a form back to a pristine state is often useful when we want to 'reuse' a form after
20746 * saving or resetting it.
20748 form.$setPristine = function() {
20749 $animate.setClass(element, PRISTINE_CLASS, DIRTY_CLASS + ' ' + SUBMITTED_CLASS);
20750 form.$dirty = false;
20751 form.$pristine = true;
20752 form.$submitted = false;
20753 forEach(controls, function(control) {
20754 control.$setPristine();
20760 * @name form.FormController#$setUntouched
20763 * Sets the form to its untouched state.
20765 * This method can be called to remove the 'ng-touched' class and set the form controls to their
20766 * untouched state (ng-untouched class).
20768 * Setting a form controls back to their untouched state is often useful when setting the form
20769 * back to its pristine state.
20771 form.$setUntouched = function() {
20772 forEach(controls, function(control) {
20773 control.$setUntouched();
20779 * @name form.FormController#$setSubmitted
20782 * Sets the form to its submitted state.
20784 form.$setSubmitted = function() {
20785 $animate.addClass(element, SUBMITTED_CLASS);
20786 form.$submitted = true;
20787 form.$$parentForm.$setSubmitted();
20797 * Nestable alias of {@link ng.directive:form `form`} directive. HTML
20798 * does not allow nesting of form elements. It is useful to nest forms, for example if the validity of a
20799 * sub-group of controls needs to be determined.
20801 * Note: the purpose of `ngForm` is to group controls,
20802 * but not to be a replacement for the `<form>` tag with all of its capabilities
20803 * (e.g. posting to the server, ...).
20805 * @param {string=} ngForm|name Name of the form. If specified, the form controller will be published into
20806 * related scope, under this name.
20816 * Directive that instantiates
20817 * {@link form.FormController FormController}.
20819 * If the `name` attribute is specified, the form controller is published onto the current scope under
20822 * # Alias: {@link ng.directive:ngForm `ngForm`}
20824 * In Angular, forms can be nested. This means that the outer form is valid when all of the child
20825 * forms are valid as well. However, browsers do not allow nesting of `<form>` elements, so
20826 * Angular provides the {@link ng.directive:ngForm `ngForm`} directive, which behaves identically to
20827 * `form` but can be nested. Nested forms can be useful, for example, if the validity of a sub-group
20828 * of controls needs to be determined.
20831 * - `ng-valid` is set if the form is valid.
20832 * - `ng-invalid` is set if the form is invalid.
20833 * - `ng-pending` is set if the form is pending.
20834 * - `ng-pristine` is set if the form is pristine.
20835 * - `ng-dirty` is set if the form is dirty.
20836 * - `ng-submitted` is set if the form was submitted.
20838 * Keep in mind that ngAnimate can detect each of these classes when added and removed.
20841 * # Submitting a form and preventing the default action
20843 * Since the role of forms in client-side Angular applications is different than in classical
20844 * roundtrip apps, it is desirable for the browser not to translate the form submission into a full
20845 * page reload that sends the data to the server. Instead some javascript logic should be triggered
20846 * to handle the form submission in an application-specific way.
20848 * For this reason, Angular prevents the default action (form submission to the server) unless the
20849 * `<form>` element has an `action` attribute specified.
20851 * You can use one of the following two ways to specify what javascript method should be called when
20852 * a form is submitted:
20854 * - {@link ng.directive:ngSubmit ngSubmit} directive on the form element
20855 * - {@link ng.directive:ngClick ngClick} directive on the first
20856 * button or input field of type submit (input[type=submit])
20858 * To prevent double execution of the handler, use only one of the {@link ng.directive:ngSubmit ngSubmit}
20859 * or {@link ng.directive:ngClick ngClick} directives.
20860 * This is because of the following form submission rules in the HTML specification:
20862 * - If a form has only one input field then hitting enter in this field triggers form submit
20864 * - if a form has 2+ input fields and no buttons or input[type=submit] then hitting enter
20865 * doesn't trigger submit
20866 * - if a form has one or more input fields and one or more buttons or input[type=submit] then
20867 * hitting enter in any of the input fields will trigger the click handler on the *first* button or
20868 * input[type=submit] (`ngClick`) *and* a submit handler on the enclosing form (`ngSubmit`)
20870 * Any pending `ngModelOptions` changes will take place immediately when an enclosing form is
20871 * submitted. Note that `ngClick` events will occur before the model is updated. Use `ngSubmit`
20872 * to have access to the updated model.
20874 * ## Animation Hooks
20876 * Animations in ngForm are triggered when any of the associated CSS classes are added and removed.
20877 * These classes are: `.ng-pristine`, `.ng-dirty`, `.ng-invalid` and `.ng-valid` as well as any
20878 * other validations that are performed within the form. Animations in ngForm are similar to how
20879 * they work in ngClass and animations can be hooked into using CSS transitions, keyframes as well
20880 * as JS animations.
20882 * The following example shows a simple way to utilize CSS transitions to style a form element
20883 * that has been rendered as invalid after it has been validated:
20886 * //be sure to include ngAnimate as a module to hook into more
20887 * //advanced animations
20889 * transition:0.5s linear all;
20890 * background: white;
20892 * .my-form.ng-invalid {
20899 <example deps="angular-animate.js" animations="true" fixBase="true" module="formExample">
20900 <file name="index.html">
20902 angular.module('formExample', [])
20903 .controller('FormController', ['$scope', function($scope) {
20904 $scope.userType = 'guest';
20909 transition:all linear 0.5s;
20910 background: transparent;
20912 .my-form.ng-invalid {
20916 <form name="myForm" ng-controller="FormController" class="my-form">
20917 userType: <input name="input" ng-model="userType" required>
20918 <span class="error" ng-show="myForm.input.$error.required">Required!</span><br>
20919 <code>userType = {{userType}}</code><br>
20920 <code>myForm.input.$valid = {{myForm.input.$valid}}</code><br>
20921 <code>myForm.input.$error = {{myForm.input.$error}}</code><br>
20922 <code>myForm.$valid = {{myForm.$valid}}</code><br>
20923 <code>myForm.$error.required = {{!!myForm.$error.required}}</code><br>
20926 <file name="protractor.js" type="protractor">
20927 it('should initialize to model', function() {
20928 var userType = element(by.binding('userType'));
20929 var valid = element(by.binding('myForm.input.$valid'));
20931 expect(userType.getText()).toContain('guest');
20932 expect(valid.getText()).toContain('true');
20935 it('should be invalid if empty', function() {
20936 var userType = element(by.binding('userType'));
20937 var valid = element(by.binding('myForm.input.$valid'));
20938 var userInput = element(by.model('userType'));
20941 userInput.sendKeys('');
20943 expect(userType.getText()).toEqual('userType =');
20944 expect(valid.getText()).toContain('false');
20949 * @param {string=} name Name of the form. If specified, the form controller will be published into
20950 * related scope, under this name.
20952 var formDirectiveFactory = function(isNgForm) {
20953 return ['$timeout', '$parse', function($timeout, $parse) {
20954 var formDirective = {
20956 restrict: isNgForm ? 'EAC' : 'E',
20957 require: ['form', '^^?form'], //first is the form's own ctrl, second is an optional parent form
20958 controller: FormController,
20959 compile: function ngFormCompile(formElement, attr) {
20960 // Setup initial state of the control
20961 formElement.addClass(PRISTINE_CLASS).addClass(VALID_CLASS);
20963 var nameAttr = attr.name ? 'name' : (isNgForm && attr.ngForm ? 'ngForm' : false);
20966 pre: function ngFormPreLink(scope, formElement, attr, ctrls) {
20967 var controller = ctrls[0];
20969 // if `action` attr is not present on the form, prevent the default action (submission)
20970 if (!('action' in attr)) {
20971 // we can't use jq events because if a form is destroyed during submission the default
20972 // action is not prevented. see #1238
20974 // IE 9 is not affected because it doesn't fire a submit event and try to do a full
20975 // page reload if the form was destroyed by submission of the form via a click handler
20976 // on a button in the form. Looks like an IE9 specific bug.
20977 var handleFormSubmission = function(event) {
20978 scope.$apply(function() {
20979 controller.$commitViewValue();
20980 controller.$setSubmitted();
20983 event.preventDefault();
20986 addEventListenerFn(formElement[0], 'submit', handleFormSubmission);
20988 // unregister the preventDefault listener so that we don't not leak memory but in a
20989 // way that will achieve the prevention of the default action.
20990 formElement.on('$destroy', function() {
20991 $timeout(function() {
20992 removeEventListenerFn(formElement[0], 'submit', handleFormSubmission);
20997 var parentFormCtrl = ctrls[1] || controller.$$parentForm;
20998 parentFormCtrl.$addControl(controller);
21000 var setter = nameAttr ? getSetter(controller.$name) : noop;
21003 setter(scope, controller);
21004 attr.$observe(nameAttr, function(newValue) {
21005 if (controller.$name === newValue) return;
21006 setter(scope, undefined);
21007 controller.$$parentForm.$$renameControl(controller, newValue);
21008 setter = getSetter(controller.$name);
21009 setter(scope, controller);
21012 formElement.on('$destroy', function() {
21013 controller.$$parentForm.$removeControl(controller);
21014 setter(scope, undefined);
21015 extend(controller, nullFormCtrl); //stop propagating child destruction handlers upwards
21022 return formDirective;
21024 function getSetter(expression) {
21025 if (expression === '') {
21026 //create an assignable expression, so forms with an empty name can be renamed later
21027 return $parse('this[""]').assign;
21029 return $parse(expression).assign || noop;
21034 var formDirective = formDirectiveFactory();
21035 var ngFormDirective = formDirectiveFactory(true);
21037 /* global VALID_CLASS: false,
21038 INVALID_CLASS: false,
21039 PRISTINE_CLASS: false,
21040 DIRTY_CLASS: false,
21041 UNTOUCHED_CLASS: false,
21042 TOUCHED_CLASS: false,
21043 ngModelMinErr: false,
21046 // Regex code is obtained from SO: https://stackoverflow.com/questions/3143070/javascript-regex-iso-datetime#answer-3143231
21047 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)/;
21048 // See valid URLs in RFC3987 (http://tools.ietf.org/html/rfc3987)
21049 // Note: We are being more lenient, because browsers are too.
21059 // 1111111111111111 222 333333 44444 555555555555555555555555 666 77777777 8888888 999
21060 var URL_REGEXP = /^[a-z][a-z\d.+-]*:\/*(?:[^:@]+(?::[^@]+)?@)?(?:[^\s:/?#]+|\[[a-f\d:]+\])(?::\d+)?(?:\/[^?#]*)?(?:\?[^#]*)?(?:#.*)?$/i;
21061 var EMAIL_REGEXP = /^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i;
21062 var NUMBER_REGEXP = /^\s*(\-|\+)?(\d+|(\d*(\.\d*)))([eE][+-]?\d+)?\s*$/;
21063 var DATE_REGEXP = /^(\d{4})-(\d{2})-(\d{2})$/;
21064 var DATETIMELOCAL_REGEXP = /^(\d{4})-(\d\d)-(\d\d)T(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
21065 var WEEK_REGEXP = /^(\d{4})-W(\d\d)$/;
21066 var MONTH_REGEXP = /^(\d{4})-(\d\d)$/;
21067 var TIME_REGEXP = /^(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
21069 var PARTIAL_VALIDATION_EVENTS = 'keydown wheel mousedown';
21070 var PARTIAL_VALIDATION_TYPES = createMap();
21071 forEach('date,datetime-local,month,time,week'.split(','), function(type) {
21072 PARTIAL_VALIDATION_TYPES[type] = true;
21079 * @name input[text]
21082 * Standard HTML text input with angular data binding, inherited by most of the `input` elements.
21085 * @param {string} ngModel Assignable angular expression to data-bind to.
21086 * @param {string=} name Property name of the form under which the control is published.
21087 * @param {string=} required Adds `required` validation error key if the value is not entered.
21088 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21089 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21090 * `required` when you want to data-bind to the `required` attribute.
21091 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
21093 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
21094 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
21096 * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
21097 * that contains the regular expression body that will be converted to a regular expression
21098 * as in the ngPattern directive.
21099 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
21100 * a RegExp found by evaluating the Angular expression given in the attribute value.
21101 * If the expression evaluates to a RegExp object, then this is used directly.
21102 * If the expression evaluates to a string, then it will be converted to a RegExp
21103 * after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
21104 * `new RegExp('^abc$')`.<br />
21105 * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
21106 * start at the index of the last search's match, thus not taking the whole input value into
21108 * @param {string=} ngChange Angular expression to be executed when input changes due to user
21109 * interaction with the input element.
21110 * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
21111 * This parameter is ignored for input[type=password] controls, which will never trim the
21115 <example name="text-input-directive" module="textInputExample">
21116 <file name="index.html">
21118 angular.module('textInputExample', [])
21119 .controller('ExampleController', ['$scope', function($scope) {
21122 word: /^\s*\w*\s*$/
21126 <form name="myForm" ng-controller="ExampleController">
21127 <label>Single word:
21128 <input type="text" name="input" ng-model="example.text"
21129 ng-pattern="example.word" required ng-trim="false">
21132 <span class="error" ng-show="myForm.input.$error.required">
21134 <span class="error" ng-show="myForm.input.$error.pattern">
21135 Single word only!</span>
21137 <tt>text = {{example.text}}</tt><br/>
21138 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21139 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21140 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21141 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21144 <file name="protractor.js" type="protractor">
21145 var text = element(by.binding('example.text'));
21146 var valid = element(by.binding('myForm.input.$valid'));
21147 var input = element(by.model('example.text'));
21149 it('should initialize to model', function() {
21150 expect(text.getText()).toContain('guest');
21151 expect(valid.getText()).toContain('true');
21154 it('should be invalid if empty', function() {
21156 input.sendKeys('');
21158 expect(text.getText()).toEqual('text =');
21159 expect(valid.getText()).toContain('false');
21162 it('should be invalid if multi word', function() {
21164 input.sendKeys('hello world');
21166 expect(valid.getText()).toContain('false');
21171 'text': textInputType,
21175 * @name input[date]
21178 * Input with date validation and transformation. In browsers that do not yet support
21179 * the HTML5 date input, a text element will be used. In that case, text must be entered in a valid ISO-8601
21180 * date format (yyyy-MM-dd), for example: `2009-01-06`. Since many
21181 * modern browsers do not yet support this input type, it is important to provide cues to users on the
21182 * expected input format via a placeholder or label.
21184 * The model must always be a Date object, otherwise Angular will throw an error.
21185 * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
21187 * The timezone to be used to read/write the `Date` instance in the model can be defined using
21188 * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
21190 * @param {string} ngModel Assignable angular expression to data-bind to.
21191 * @param {string=} name Property name of the form under which the control is published.
21192 * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be a
21193 * valid ISO date string (yyyy-MM-dd). You can also use interpolation inside this attribute
21194 * (e.g. `min="{{minDate | date:'yyyy-MM-dd'}}"`). Note that `min` will also add native HTML5
21195 * constraint validation.
21196 * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must be
21197 * a valid ISO date string (yyyy-MM-dd). You can also use interpolation inside this attribute
21198 * (e.g. `max="{{maxDate | date:'yyyy-MM-dd'}}"`). Note that `max` will also add native HTML5
21199 * constraint validation.
21200 * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO date string
21201 * the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
21202 * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO date string
21203 * the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
21204 * @param {string=} required Sets `required` validation error key if the value is not entered.
21205 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21206 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21207 * `required` when you want to data-bind to the `required` attribute.
21208 * @param {string=} ngChange Angular expression to be executed when input changes due to user
21209 * interaction with the input element.
21212 <example name="date-input-directive" module="dateInputExample">
21213 <file name="index.html">
21215 angular.module('dateInputExample', [])
21216 .controller('DateController', ['$scope', function($scope) {
21218 value: new Date(2013, 9, 22)
21222 <form name="myForm" ng-controller="DateController as dateCtrl">
21223 <label for="exampleInput">Pick a date in 2013:</label>
21224 <input type="date" id="exampleInput" name="input" ng-model="example.value"
21225 placeholder="yyyy-MM-dd" min="2013-01-01" max="2013-12-31" required />
21227 <span class="error" ng-show="myForm.input.$error.required">
21229 <span class="error" ng-show="myForm.input.$error.date">
21230 Not a valid date!</span>
21232 <tt>value = {{example.value | date: "yyyy-MM-dd"}}</tt><br/>
21233 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21234 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21235 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21236 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21239 <file name="protractor.js" type="protractor">
21240 var value = element(by.binding('example.value | date: "yyyy-MM-dd"'));
21241 var valid = element(by.binding('myForm.input.$valid'));
21242 var input = element(by.model('example.value'));
21244 // currently protractor/webdriver does not support
21245 // sending keys to all known HTML5 input controls
21246 // for various browsers (see https://github.com/angular/protractor/issues/562).
21247 function setInput(val) {
21248 // set the value of the element and force validation.
21249 var scr = "var ipt = document.getElementById('exampleInput'); " +
21250 "ipt.value = '" + val + "';" +
21251 "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
21252 browser.executeScript(scr);
21255 it('should initialize to model', function() {
21256 expect(value.getText()).toContain('2013-10-22');
21257 expect(valid.getText()).toContain('myForm.input.$valid = true');
21260 it('should be invalid if empty', function() {
21262 expect(value.getText()).toEqual('value =');
21263 expect(valid.getText()).toContain('myForm.input.$valid = false');
21266 it('should be invalid if over max', function() {
21267 setInput('2015-01-01');
21268 expect(value.getText()).toContain('');
21269 expect(valid.getText()).toContain('myForm.input.$valid = false');
21274 'date': createDateInputType('date', DATE_REGEXP,
21275 createDateParser(DATE_REGEXP, ['yyyy', 'MM', 'dd']),
21280 * @name input[datetime-local]
21283 * Input with datetime validation and transformation. In browsers that do not yet support
21284 * the HTML5 date input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
21285 * local datetime format (yyyy-MM-ddTHH:mm:ss), for example: `2010-12-28T14:57:00`.
21287 * The model must always be a Date object, otherwise Angular will throw an error.
21288 * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
21290 * The timezone to be used to read/write the `Date` instance in the model can be defined using
21291 * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
21293 * @param {string} ngModel Assignable angular expression to data-bind to.
21294 * @param {string=} name Property name of the form under which the control is published.
21295 * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
21296 * This must be a valid ISO datetime format (yyyy-MM-ddTHH:mm:ss). You can also use interpolation
21297 * inside this attribute (e.g. `min="{{minDatetimeLocal | date:'yyyy-MM-ddTHH:mm:ss'}}"`).
21298 * Note that `min` will also add native HTML5 constraint validation.
21299 * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
21300 * This must be a valid ISO datetime format (yyyy-MM-ddTHH:mm:ss). You can also use interpolation
21301 * inside this attribute (e.g. `max="{{maxDatetimeLocal | date:'yyyy-MM-ddTHH:mm:ss'}}"`).
21302 * Note that `max` will also add native HTML5 constraint validation.
21303 * @param {(date|string)=} ngMin Sets the `min` validation error key to the Date / ISO datetime string
21304 * the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
21305 * @param {(date|string)=} ngMax Sets the `max` validation error key to the Date / ISO datetime string
21306 * the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
21307 * @param {string=} required Sets `required` validation error key if the value is not entered.
21308 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21309 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21310 * `required` when you want to data-bind to the `required` attribute.
21311 * @param {string=} ngChange Angular expression to be executed when input changes due to user
21312 * interaction with the input element.
21315 <example name="datetimelocal-input-directive" module="dateExample">
21316 <file name="index.html">
21318 angular.module('dateExample', [])
21319 .controller('DateController', ['$scope', function($scope) {
21321 value: new Date(2010, 11, 28, 14, 57)
21325 <form name="myForm" ng-controller="DateController as dateCtrl">
21326 <label for="exampleInput">Pick a date between in 2013:</label>
21327 <input type="datetime-local" id="exampleInput" name="input" ng-model="example.value"
21328 placeholder="yyyy-MM-ddTHH:mm:ss" min="2001-01-01T00:00:00" max="2013-12-31T00:00:00" required />
21330 <span class="error" ng-show="myForm.input.$error.required">
21332 <span class="error" ng-show="myForm.input.$error.datetimelocal">
21333 Not a valid date!</span>
21335 <tt>value = {{example.value | date: "yyyy-MM-ddTHH:mm:ss"}}</tt><br/>
21336 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21337 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21338 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21339 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21342 <file name="protractor.js" type="protractor">
21343 var value = element(by.binding('example.value | date: "yyyy-MM-ddTHH:mm:ss"'));
21344 var valid = element(by.binding('myForm.input.$valid'));
21345 var input = element(by.model('example.value'));
21347 // currently protractor/webdriver does not support
21348 // sending keys to all known HTML5 input controls
21349 // for various browsers (https://github.com/angular/protractor/issues/562).
21350 function setInput(val) {
21351 // set the value of the element and force validation.
21352 var scr = "var ipt = document.getElementById('exampleInput'); " +
21353 "ipt.value = '" + val + "';" +
21354 "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
21355 browser.executeScript(scr);
21358 it('should initialize to model', function() {
21359 expect(value.getText()).toContain('2010-12-28T14:57:00');
21360 expect(valid.getText()).toContain('myForm.input.$valid = true');
21363 it('should be invalid if empty', function() {
21365 expect(value.getText()).toEqual('value =');
21366 expect(valid.getText()).toContain('myForm.input.$valid = false');
21369 it('should be invalid if over max', function() {
21370 setInput('2015-01-01T23:59:00');
21371 expect(value.getText()).toContain('');
21372 expect(valid.getText()).toContain('myForm.input.$valid = false');
21377 'datetime-local': createDateInputType('datetimelocal', DATETIMELOCAL_REGEXP,
21378 createDateParser(DATETIMELOCAL_REGEXP, ['yyyy', 'MM', 'dd', 'HH', 'mm', 'ss', 'sss']),
21379 'yyyy-MM-ddTHH:mm:ss.sss'),
21383 * @name input[time]
21386 * Input with time validation and transformation. In browsers that do not yet support
21387 * the HTML5 date input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
21388 * local time format (HH:mm:ss), for example: `14:57:00`. Model must be a Date object. This binding will always output a
21389 * Date object to the model of January 1, 1970, or local date `new Date(1970, 0, 1, HH, mm, ss)`.
21391 * The model must always be a Date object, otherwise Angular will throw an error.
21392 * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
21394 * The timezone to be used to read/write the `Date` instance in the model can be defined using
21395 * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
21397 * @param {string} ngModel Assignable angular expression to data-bind to.
21398 * @param {string=} name Property name of the form under which the control is published.
21399 * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
21400 * This must be a valid ISO time format (HH:mm:ss). You can also use interpolation inside this
21401 * attribute (e.g. `min="{{minTime | date:'HH:mm:ss'}}"`). Note that `min` will also add
21402 * native HTML5 constraint validation.
21403 * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
21404 * This must be a valid ISO time format (HH:mm:ss). You can also use interpolation inside this
21405 * attribute (e.g. `max="{{maxTime | date:'HH:mm:ss'}}"`). Note that `max` will also add
21406 * native HTML5 constraint validation.
21407 * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO time string the
21408 * `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
21409 * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO time string the
21410 * `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
21411 * @param {string=} required Sets `required` validation error key if the value is not entered.
21412 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21413 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21414 * `required` when you want to data-bind to the `required` attribute.
21415 * @param {string=} ngChange Angular expression to be executed when input changes due to user
21416 * interaction with the input element.
21419 <example name="time-input-directive" module="timeExample">
21420 <file name="index.html">
21422 angular.module('timeExample', [])
21423 .controller('DateController', ['$scope', function($scope) {
21425 value: new Date(1970, 0, 1, 14, 57, 0)
21429 <form name="myForm" ng-controller="DateController as dateCtrl">
21430 <label for="exampleInput">Pick a between 8am and 5pm:</label>
21431 <input type="time" id="exampleInput" name="input" ng-model="example.value"
21432 placeholder="HH:mm:ss" min="08:00:00" max="17:00:00" required />
21434 <span class="error" ng-show="myForm.input.$error.required">
21436 <span class="error" ng-show="myForm.input.$error.time">
21437 Not a valid date!</span>
21439 <tt>value = {{example.value | date: "HH:mm:ss"}}</tt><br/>
21440 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21441 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21442 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21443 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21446 <file name="protractor.js" type="protractor">
21447 var value = element(by.binding('example.value | date: "HH:mm:ss"'));
21448 var valid = element(by.binding('myForm.input.$valid'));
21449 var input = element(by.model('example.value'));
21451 // currently protractor/webdriver does not support
21452 // sending keys to all known HTML5 input controls
21453 // for various browsers (https://github.com/angular/protractor/issues/562).
21454 function setInput(val) {
21455 // set the value of the element and force validation.
21456 var scr = "var ipt = document.getElementById('exampleInput'); " +
21457 "ipt.value = '" + val + "';" +
21458 "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
21459 browser.executeScript(scr);
21462 it('should initialize to model', function() {
21463 expect(value.getText()).toContain('14:57:00');
21464 expect(valid.getText()).toContain('myForm.input.$valid = true');
21467 it('should be invalid if empty', function() {
21469 expect(value.getText()).toEqual('value =');
21470 expect(valid.getText()).toContain('myForm.input.$valid = false');
21473 it('should be invalid if over max', function() {
21474 setInput('23:59:00');
21475 expect(value.getText()).toContain('');
21476 expect(valid.getText()).toContain('myForm.input.$valid = false');
21481 'time': createDateInputType('time', TIME_REGEXP,
21482 createDateParser(TIME_REGEXP, ['HH', 'mm', 'ss', 'sss']),
21487 * @name input[week]
21490 * Input with week-of-the-year validation and transformation to Date. In browsers that do not yet support
21491 * the HTML5 week input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
21492 * week format (yyyy-W##), for example: `2013-W02`.
21494 * The model must always be a Date object, otherwise Angular will throw an error.
21495 * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
21497 * The timezone to be used to read/write the `Date` instance in the model can be defined using
21498 * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
21500 * @param {string} ngModel Assignable angular expression to data-bind to.
21501 * @param {string=} name Property name of the form under which the control is published.
21502 * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
21503 * This must be a valid ISO week format (yyyy-W##). You can also use interpolation inside this
21504 * attribute (e.g. `min="{{minWeek | date:'yyyy-Www'}}"`). Note that `min` will also add
21505 * native HTML5 constraint validation.
21506 * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
21507 * This must be a valid ISO week format (yyyy-W##). You can also use interpolation inside this
21508 * attribute (e.g. `max="{{maxWeek | date:'yyyy-Www'}}"`). Note that `max` will also add
21509 * native HTML5 constraint validation.
21510 * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO week string
21511 * the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
21512 * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO week string
21513 * the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
21514 * @param {string=} required Sets `required` validation error key if the value is not entered.
21515 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21516 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21517 * `required` when you want to data-bind to the `required` attribute.
21518 * @param {string=} ngChange Angular expression to be executed when input changes due to user
21519 * interaction with the input element.
21522 <example name="week-input-directive" module="weekExample">
21523 <file name="index.html">
21525 angular.module('weekExample', [])
21526 .controller('DateController', ['$scope', function($scope) {
21528 value: new Date(2013, 0, 3)
21532 <form name="myForm" ng-controller="DateController as dateCtrl">
21533 <label>Pick a date between in 2013:
21534 <input id="exampleInput" type="week" name="input" ng-model="example.value"
21535 placeholder="YYYY-W##" min="2012-W32"
21536 max="2013-W52" required />
21539 <span class="error" ng-show="myForm.input.$error.required">
21541 <span class="error" ng-show="myForm.input.$error.week">
21542 Not a valid date!</span>
21544 <tt>value = {{example.value | date: "yyyy-Www"}}</tt><br/>
21545 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21546 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21547 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21548 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21551 <file name="protractor.js" type="protractor">
21552 var value = element(by.binding('example.value | date: "yyyy-Www"'));
21553 var valid = element(by.binding('myForm.input.$valid'));
21554 var input = element(by.model('example.value'));
21556 // currently protractor/webdriver does not support
21557 // sending keys to all known HTML5 input controls
21558 // for various browsers (https://github.com/angular/protractor/issues/562).
21559 function setInput(val) {
21560 // set the value of the element and force validation.
21561 var scr = "var ipt = document.getElementById('exampleInput'); " +
21562 "ipt.value = '" + val + "';" +
21563 "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
21564 browser.executeScript(scr);
21567 it('should initialize to model', function() {
21568 expect(value.getText()).toContain('2013-W01');
21569 expect(valid.getText()).toContain('myForm.input.$valid = true');
21572 it('should be invalid if empty', function() {
21574 expect(value.getText()).toEqual('value =');
21575 expect(valid.getText()).toContain('myForm.input.$valid = false');
21578 it('should be invalid if over max', function() {
21579 setInput('2015-W01');
21580 expect(value.getText()).toContain('');
21581 expect(valid.getText()).toContain('myForm.input.$valid = false');
21586 'week': createDateInputType('week', WEEK_REGEXP, weekParser, 'yyyy-Www'),
21590 * @name input[month]
21593 * Input with month validation and transformation. In browsers that do not yet support
21594 * the HTML5 month input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
21595 * month format (yyyy-MM), for example: `2009-01`.
21597 * The model must always be a Date object, otherwise Angular will throw an error.
21598 * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
21599 * If the model is not set to the first of the month, the next view to model update will set it
21600 * to the first of the month.
21602 * The timezone to be used to read/write the `Date` instance in the model can be defined using
21603 * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
21605 * @param {string} ngModel Assignable angular expression to data-bind to.
21606 * @param {string=} name Property name of the form under which the control is published.
21607 * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
21608 * This must be a valid ISO month format (yyyy-MM). You can also use interpolation inside this
21609 * attribute (e.g. `min="{{minMonth | date:'yyyy-MM'}}"`). Note that `min` will also add
21610 * native HTML5 constraint validation.
21611 * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
21612 * This must be a valid ISO month format (yyyy-MM). You can also use interpolation inside this
21613 * attribute (e.g. `max="{{maxMonth | date:'yyyy-MM'}}"`). Note that `max` will also add
21614 * native HTML5 constraint validation.
21615 * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO week string
21616 * the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
21617 * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO week string
21618 * the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
21620 * @param {string=} required Sets `required` validation error key if the value is not entered.
21621 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21622 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21623 * `required` when you want to data-bind to the `required` attribute.
21624 * @param {string=} ngChange Angular expression to be executed when input changes due to user
21625 * interaction with the input element.
21628 <example name="month-input-directive" module="monthExample">
21629 <file name="index.html">
21631 angular.module('monthExample', [])
21632 .controller('DateController', ['$scope', function($scope) {
21634 value: new Date(2013, 9, 1)
21638 <form name="myForm" ng-controller="DateController as dateCtrl">
21639 <label for="exampleInput">Pick a month in 2013:</label>
21640 <input id="exampleInput" type="month" name="input" ng-model="example.value"
21641 placeholder="yyyy-MM" min="2013-01" max="2013-12" required />
21643 <span class="error" ng-show="myForm.input.$error.required">
21645 <span class="error" ng-show="myForm.input.$error.month">
21646 Not a valid month!</span>
21648 <tt>value = {{example.value | date: "yyyy-MM"}}</tt><br/>
21649 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21650 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21651 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21652 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21655 <file name="protractor.js" type="protractor">
21656 var value = element(by.binding('example.value | date: "yyyy-MM"'));
21657 var valid = element(by.binding('myForm.input.$valid'));
21658 var input = element(by.model('example.value'));
21660 // currently protractor/webdriver does not support
21661 // sending keys to all known HTML5 input controls
21662 // for various browsers (https://github.com/angular/protractor/issues/562).
21663 function setInput(val) {
21664 // set the value of the element and force validation.
21665 var scr = "var ipt = document.getElementById('exampleInput'); " +
21666 "ipt.value = '" + val + "';" +
21667 "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
21668 browser.executeScript(scr);
21671 it('should initialize to model', function() {
21672 expect(value.getText()).toContain('2013-10');
21673 expect(valid.getText()).toContain('myForm.input.$valid = true');
21676 it('should be invalid if empty', function() {
21678 expect(value.getText()).toEqual('value =');
21679 expect(valid.getText()).toContain('myForm.input.$valid = false');
21682 it('should be invalid if over max', function() {
21683 setInput('2015-01');
21684 expect(value.getText()).toContain('');
21685 expect(valid.getText()).toContain('myForm.input.$valid = false');
21690 'month': createDateInputType('month', MONTH_REGEXP,
21691 createDateParser(MONTH_REGEXP, ['yyyy', 'MM']),
21696 * @name input[number]
21699 * Text input with number validation and transformation. Sets the `number` validation
21700 * error if not a valid number.
21702 * <div class="alert alert-warning">
21703 * The model must always be of type `number` otherwise Angular will throw an error.
21704 * Be aware that a string containing a number is not enough. See the {@link ngModel:numfmt}
21705 * error docs for more information and an example of how to convert your model if necessary.
21708 * ## Issues with HTML5 constraint validation
21710 * In browsers that follow the
21711 * [HTML5 specification](https://html.spec.whatwg.org/multipage/forms.html#number-state-%28type=number%29),
21712 * `input[number]` does not work as expected with {@link ngModelOptions `ngModelOptions.allowInvalid`}.
21713 * If a non-number is entered in the input, the browser will report the value as an empty string,
21714 * which means the view / model values in `ngModel` and subsequently the scope value
21715 * will also be an empty string.
21718 * @param {string} ngModel Assignable angular expression to data-bind to.
21719 * @param {string=} name Property name of the form under which the control is published.
21720 * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
21721 * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
21722 * @param {string=} required Sets `required` validation error key if the value is not entered.
21723 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21724 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21725 * `required` when you want to data-bind to the `required` attribute.
21726 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
21728 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
21729 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
21731 * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
21732 * that contains the regular expression body that will be converted to a regular expression
21733 * as in the ngPattern directive.
21734 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
21735 * a RegExp found by evaluating the Angular expression given in the attribute value.
21736 * If the expression evaluates to a RegExp object, then this is used directly.
21737 * If the expression evaluates to a string, then it will be converted to a RegExp
21738 * after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
21739 * `new RegExp('^abc$')`.<br />
21740 * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
21741 * start at the index of the last search's match, thus not taking the whole input value into
21743 * @param {string=} ngChange Angular expression to be executed when input changes due to user
21744 * interaction with the input element.
21747 <example name="number-input-directive" module="numberExample">
21748 <file name="index.html">
21750 angular.module('numberExample', [])
21751 .controller('ExampleController', ['$scope', function($scope) {
21757 <form name="myForm" ng-controller="ExampleController">
21759 <input type="number" name="input" ng-model="example.value"
21760 min="0" max="99" required>
21763 <span class="error" ng-show="myForm.input.$error.required">
21765 <span class="error" ng-show="myForm.input.$error.number">
21766 Not valid number!</span>
21768 <tt>value = {{example.value}}</tt><br/>
21769 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21770 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21771 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21772 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21775 <file name="protractor.js" type="protractor">
21776 var value = element(by.binding('example.value'));
21777 var valid = element(by.binding('myForm.input.$valid'));
21778 var input = element(by.model('example.value'));
21780 it('should initialize to model', function() {
21781 expect(value.getText()).toContain('12');
21782 expect(valid.getText()).toContain('true');
21785 it('should be invalid if empty', function() {
21787 input.sendKeys('');
21788 expect(value.getText()).toEqual('value =');
21789 expect(valid.getText()).toContain('false');
21792 it('should be invalid if over max', function() {
21794 input.sendKeys('123');
21795 expect(value.getText()).toEqual('value =');
21796 expect(valid.getText()).toContain('false');
21801 'number': numberInputType,
21809 * Text input with URL validation. Sets the `url` validation error key if the content is not a
21812 * <div class="alert alert-warning">
21813 * **Note:** `input[url]` uses a regex to validate urls that is derived from the regex
21814 * used in Chromium. If you need stricter validation, you can use `ng-pattern` or modify
21815 * the built-in validators (see the {@link guide/forms Forms guide})
21818 * @param {string} ngModel Assignable angular expression to data-bind to.
21819 * @param {string=} name Property name of the form under which the control is published.
21820 * @param {string=} required Sets `required` validation error key if the value is not entered.
21821 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21822 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21823 * `required` when you want to data-bind to the `required` attribute.
21824 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
21826 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
21827 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
21829 * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
21830 * that contains the regular expression body that will be converted to a regular expression
21831 * as in the ngPattern directive.
21832 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
21833 * a RegExp found by evaluating the Angular expression given in the attribute value.
21834 * If the expression evaluates to a RegExp object, then this is used directly.
21835 * If the expression evaluates to a string, then it will be converted to a RegExp
21836 * after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
21837 * `new RegExp('^abc$')`.<br />
21838 * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
21839 * start at the index of the last search's match, thus not taking the whole input value into
21841 * @param {string=} ngChange Angular expression to be executed when input changes due to user
21842 * interaction with the input element.
21845 <example name="url-input-directive" module="urlExample">
21846 <file name="index.html">
21848 angular.module('urlExample', [])
21849 .controller('ExampleController', ['$scope', function($scope) {
21851 text: 'http://google.com'
21855 <form name="myForm" ng-controller="ExampleController">
21857 <input type="url" name="input" ng-model="url.text" required>
21860 <span class="error" ng-show="myForm.input.$error.required">
21862 <span class="error" ng-show="myForm.input.$error.url">
21863 Not valid url!</span>
21865 <tt>text = {{url.text}}</tt><br/>
21866 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21867 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21868 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21869 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21870 <tt>myForm.$error.url = {{!!myForm.$error.url}}</tt><br/>
21873 <file name="protractor.js" type="protractor">
21874 var text = element(by.binding('url.text'));
21875 var valid = element(by.binding('myForm.input.$valid'));
21876 var input = element(by.model('url.text'));
21878 it('should initialize to model', function() {
21879 expect(text.getText()).toContain('http://google.com');
21880 expect(valid.getText()).toContain('true');
21883 it('should be invalid if empty', function() {
21885 input.sendKeys('');
21887 expect(text.getText()).toEqual('text =');
21888 expect(valid.getText()).toContain('false');
21891 it('should be invalid if not url', function() {
21893 input.sendKeys('box');
21895 expect(valid.getText()).toContain('false');
21900 'url': urlInputType,
21905 * @name input[email]
21908 * Text input with email validation. Sets the `email` validation error key if not a valid email
21911 * <div class="alert alert-warning">
21912 * **Note:** `input[email]` uses a regex to validate email addresses that is derived from the regex
21913 * used in Chromium. If you need stricter validation (e.g. requiring a top-level domain), you can
21914 * use `ng-pattern` or modify the built-in validators (see the {@link guide/forms Forms guide})
21917 * @param {string} ngModel Assignable angular expression to data-bind to.
21918 * @param {string=} name Property name of the form under which the control is published.
21919 * @param {string=} required Sets `required` validation error key if the value is not entered.
21920 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21921 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21922 * `required` when you want to data-bind to the `required` attribute.
21923 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
21925 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
21926 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
21928 * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
21929 * that contains the regular expression body that will be converted to a regular expression
21930 * as in the ngPattern directive.
21931 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
21932 * a RegExp found by evaluating the Angular expression given in the attribute value.
21933 * If the expression evaluates to a RegExp object, then this is used directly.
21934 * If the expression evaluates to a string, then it will be converted to a RegExp
21935 * after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
21936 * `new RegExp('^abc$')`.<br />
21937 * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
21938 * start at the index of the last search's match, thus not taking the whole input value into
21940 * @param {string=} ngChange Angular expression to be executed when input changes due to user
21941 * interaction with the input element.
21944 <example name="email-input-directive" module="emailExample">
21945 <file name="index.html">
21947 angular.module('emailExample', [])
21948 .controller('ExampleController', ['$scope', function($scope) {
21950 text: 'me@example.com'
21954 <form name="myForm" ng-controller="ExampleController">
21956 <input type="email" name="input" ng-model="email.text" required>
21959 <span class="error" ng-show="myForm.input.$error.required">
21961 <span class="error" ng-show="myForm.input.$error.email">
21962 Not valid email!</span>
21964 <tt>text = {{email.text}}</tt><br/>
21965 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21966 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21967 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21968 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21969 <tt>myForm.$error.email = {{!!myForm.$error.email}}</tt><br/>
21972 <file name="protractor.js" type="protractor">
21973 var text = element(by.binding('email.text'));
21974 var valid = element(by.binding('myForm.input.$valid'));
21975 var input = element(by.model('email.text'));
21977 it('should initialize to model', function() {
21978 expect(text.getText()).toContain('me@example.com');
21979 expect(valid.getText()).toContain('true');
21982 it('should be invalid if empty', function() {
21984 input.sendKeys('');
21985 expect(text.getText()).toEqual('text =');
21986 expect(valid.getText()).toContain('false');
21989 it('should be invalid if not email', function() {
21991 input.sendKeys('xxx');
21993 expect(valid.getText()).toContain('false');
21998 'email': emailInputType,
22003 * @name input[radio]
22006 * HTML radio button.
22008 * @param {string} ngModel Assignable angular expression to data-bind to.
22009 * @param {string} value The value to which the `ngModel` expression should be set when selected.
22010 * Note that `value` only supports `string` values, i.e. the scope model needs to be a string,
22011 * too. Use `ngValue` if you need complex models (`number`, `object`, ...).
22012 * @param {string=} name Property name of the form under which the control is published.
22013 * @param {string=} ngChange Angular expression to be executed when input changes due to user
22014 * interaction with the input element.
22015 * @param {string} ngValue Angular expression to which `ngModel` will be be set when the radio
22016 * is selected. Should be used instead of the `value` attribute if you need
22017 * a non-string `ngModel` (`boolean`, `array`, ...).
22020 <example name="radio-input-directive" module="radioExample">
22021 <file name="index.html">
22023 angular.module('radioExample', [])
22024 .controller('ExampleController', ['$scope', function($scope) {
22028 $scope.specialValue = {
22034 <form name="myForm" ng-controller="ExampleController">
22036 <input type="radio" ng-model="color.name" value="red">
22040 <input type="radio" ng-model="color.name" ng-value="specialValue">
22044 <input type="radio" ng-model="color.name" value="blue">
22047 <tt>color = {{color.name | json}}</tt><br/>
22049 Note that `ng-value="specialValue"` sets radio item's value to be the value of `$scope.specialValue`.
22051 <file name="protractor.js" type="protractor">
22052 it('should change state', function() {
22053 var color = element(by.binding('color.name'));
22055 expect(color.getText()).toContain('blue');
22057 element.all(by.model('color.name')).get(0).click();
22059 expect(color.getText()).toContain('red');
22064 'radio': radioInputType,
22069 * @name input[checkbox]
22074 * @param {string} ngModel Assignable angular expression to data-bind to.
22075 * @param {string=} name Property name of the form under which the control is published.
22076 * @param {expression=} ngTrueValue The value to which the expression should be set when selected.
22077 * @param {expression=} ngFalseValue The value to which the expression should be set when not selected.
22078 * @param {string=} ngChange Angular expression to be executed when input changes due to user
22079 * interaction with the input element.
22082 <example name="checkbox-input-directive" module="checkboxExample">
22083 <file name="index.html">
22085 angular.module('checkboxExample', [])
22086 .controller('ExampleController', ['$scope', function($scope) {
22087 $scope.checkboxModel = {
22093 <form name="myForm" ng-controller="ExampleController">
22095 <input type="checkbox" ng-model="checkboxModel.value1">
22098 <input type="checkbox" ng-model="checkboxModel.value2"
22099 ng-true-value="'YES'" ng-false-value="'NO'">
22101 <tt>value1 = {{checkboxModel.value1}}</tt><br/>
22102 <tt>value2 = {{checkboxModel.value2}}</tt><br/>
22105 <file name="protractor.js" type="protractor">
22106 it('should change state', function() {
22107 var value1 = element(by.binding('checkboxModel.value1'));
22108 var value2 = element(by.binding('checkboxModel.value2'));
22110 expect(value1.getText()).toContain('true');
22111 expect(value2.getText()).toContain('YES');
22113 element(by.model('checkboxModel.value1')).click();
22114 element(by.model('checkboxModel.value2')).click();
22116 expect(value1.getText()).toContain('false');
22117 expect(value2.getText()).toContain('NO');
22122 'checkbox': checkboxInputType,
22131 function stringBasedInputType(ctrl) {
22132 ctrl.$formatters.push(function(value) {
22133 return ctrl.$isEmpty(value) ? value : value.toString();
22137 function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
22138 baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
22139 stringBasedInputType(ctrl);
22142 function baseInputType(scope, element, attr, ctrl, $sniffer, $browser) {
22143 var type = lowercase(element[0].type);
22145 // In composition mode, users are still inputing intermediate text buffer,
22146 // hold the listener until composition is done.
22147 // More about composition events: https://developer.mozilla.org/en-US/docs/Web/API/CompositionEvent
22148 if (!$sniffer.android) {
22149 var composing = false;
22151 element.on('compositionstart', function(data) {
22155 element.on('compositionend', function() {
22163 var listener = function(ev) {
22165 $browser.defer.cancel(timeout);
22168 if (composing) return;
22169 var value = element.val(),
22170 event = ev && ev.type;
22172 // By default we will trim the value
22173 // If the attribute ng-trim exists we will avoid trimming
22174 // If input type is 'password', the value is never trimmed
22175 if (type !== 'password' && (!attr.ngTrim || attr.ngTrim !== 'false')) {
22176 value = trim(value);
22179 // If a control is suffering from bad input (due to native validators), browsers discard its
22180 // value, so it may be necessary to revalidate (by calling $setViewValue again) even if the
22181 // control's value is the same empty value twice in a row.
22182 if (ctrl.$viewValue !== value || (value === '' && ctrl.$$hasNativeValidators)) {
22183 ctrl.$setViewValue(value, event);
22187 // if the browser does support "input" event, we are fine - except on IE9 which doesn't fire the
22188 // input event on backspace, delete or cut
22189 if ($sniffer.hasEvent('input')) {
22190 element.on('input', listener);
22192 var deferListener = function(ev, input, origValue) {
22194 timeout = $browser.defer(function() {
22196 if (!input || input.value !== origValue) {
22203 element.on('keydown', function(event) {
22204 var key = event.keyCode;
22207 // command modifiers arrows
22208 if (key === 91 || (15 < key && key < 19) || (37 <= key && key <= 40)) return;
22210 deferListener(event, this, this.value);
22213 // if user modifies input value using context menu in IE, we need "paste" and "cut" events to catch it
22214 if ($sniffer.hasEvent('paste')) {
22215 element.on('paste cut', deferListener);
22219 // if user paste into input using mouse on older browser
22220 // or form autocomplete on newer browser, we need "change" event to catch it
22221 element.on('change', listener);
22223 // Some native input types (date-family) have the ability to change validity without
22224 // firing any input/change events.
22225 // For these event types, when native validators are present and the browser supports the type,
22226 // check for validity changes on various DOM events.
22227 if (PARTIAL_VALIDATION_TYPES[type] && ctrl.$$hasNativeValidators && type === attr.type) {
22228 element.on(PARTIAL_VALIDATION_EVENTS, function(ev) {
22230 var validity = this[VALIDITY_STATE_PROPERTY];
22231 var origBadInput = validity.badInput;
22232 var origTypeMismatch = validity.typeMismatch;
22233 timeout = $browser.defer(function() {
22235 if (validity.badInput !== origBadInput || validity.typeMismatch !== origTypeMismatch) {
22243 ctrl.$render = function() {
22244 // Workaround for Firefox validation #12102.
22245 var value = ctrl.$isEmpty(ctrl.$viewValue) ? '' : ctrl.$viewValue;
22246 if (element.val() !== value) {
22247 element.val(value);
22252 function weekParser(isoWeek, existingDate) {
22253 if (isDate(isoWeek)) {
22257 if (isString(isoWeek)) {
22258 WEEK_REGEXP.lastIndex = 0;
22259 var parts = WEEK_REGEXP.exec(isoWeek);
22261 var year = +parts[1],
22267 firstThurs = getFirstThursdayOfYear(year),
22268 addDays = (week - 1) * 7;
22270 if (existingDate) {
22271 hours = existingDate.getHours();
22272 minutes = existingDate.getMinutes();
22273 seconds = existingDate.getSeconds();
22274 milliseconds = existingDate.getMilliseconds();
22277 return new Date(year, 0, firstThurs.getDate() + addDays, hours, minutes, seconds, milliseconds);
22284 function createDateParser(regexp, mapping) {
22285 return function(iso, date) {
22292 if (isString(iso)) {
22293 // When a date is JSON'ified to wraps itself inside of an extra
22294 // set of double quotes. This makes the date parsing code unable
22295 // to match the date string and parse it as a date.
22296 if (iso.charAt(0) == '"' && iso.charAt(iso.length - 1) == '"') {
22297 iso = iso.substring(1, iso.length - 1);
22299 if (ISO_DATE_REGEXP.test(iso)) {
22300 return new Date(iso);
22302 regexp.lastIndex = 0;
22303 parts = regexp.exec(iso);
22309 yyyy: date.getFullYear(),
22310 MM: date.getMonth() + 1,
22311 dd: date.getDate(),
22312 HH: date.getHours(),
22313 mm: date.getMinutes(),
22314 ss: date.getSeconds(),
22315 sss: date.getMilliseconds() / 1000
22318 map = { yyyy: 1970, MM: 1, dd: 1, HH: 0, mm: 0, ss: 0, sss: 0 };
22321 forEach(parts, function(part, index) {
22322 if (index < mapping.length) {
22323 map[mapping[index]] = +part;
22326 return new Date(map.yyyy, map.MM - 1, map.dd, map.HH, map.mm, map.ss || 0, map.sss * 1000 || 0);
22334 function createDateInputType(type, regexp, parseDate, format) {
22335 return function dynamicDateInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter) {
22336 badInputChecker(scope, element, attr, ctrl);
22337 baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
22338 var timezone = ctrl && ctrl.$options && ctrl.$options.timezone;
22341 ctrl.$$parserName = type;
22342 ctrl.$parsers.push(function(value) {
22343 if (ctrl.$isEmpty(value)) return null;
22344 if (regexp.test(value)) {
22345 // Note: We cannot read ctrl.$modelValue, as there might be a different
22346 // parser/formatter in the processing chain so that the model
22347 // contains some different data format!
22348 var parsedDate = parseDate(value, previousDate);
22350 parsedDate = convertTimezoneToLocal(parsedDate, timezone);
22357 ctrl.$formatters.push(function(value) {
22358 if (value && !isDate(value)) {
22359 throw ngModelMinErr('datefmt', 'Expected `{0}` to be a date', value);
22361 if (isValidDate(value)) {
22362 previousDate = value;
22363 if (previousDate && timezone) {
22364 previousDate = convertTimezoneToLocal(previousDate, timezone, true);
22366 return $filter('date')(value, format, timezone);
22368 previousDate = null;
22373 if (isDefined(attr.min) || attr.ngMin) {
22375 ctrl.$validators.min = function(value) {
22376 return !isValidDate(value) || isUndefined(minVal) || parseDate(value) >= minVal;
22378 attr.$observe('min', function(val) {
22379 minVal = parseObservedDateValue(val);
22384 if (isDefined(attr.max) || attr.ngMax) {
22386 ctrl.$validators.max = function(value) {
22387 return !isValidDate(value) || isUndefined(maxVal) || parseDate(value) <= maxVal;
22389 attr.$observe('max', function(val) {
22390 maxVal = parseObservedDateValue(val);
22395 function isValidDate(value) {
22396 // Invalid Date: getTime() returns NaN
22397 return value && !(value.getTime && value.getTime() !== value.getTime());
22400 function parseObservedDateValue(val) {
22401 return isDefined(val) && !isDate(val) ? parseDate(val) || undefined : val;
22406 function badInputChecker(scope, element, attr, ctrl) {
22407 var node = element[0];
22408 var nativeValidation = ctrl.$$hasNativeValidators = isObject(node.validity);
22409 if (nativeValidation) {
22410 ctrl.$parsers.push(function(value) {
22411 var validity = element.prop(VALIDITY_STATE_PROPERTY) || {};
22412 // Detect bug in FF35 for input[email] (https://bugzilla.mozilla.org/show_bug.cgi?id=1064430):
22413 // - also sets validity.badInput (should only be validity.typeMismatch).
22414 // - see http://www.whatwg.org/specs/web-apps/current-work/multipage/forms.html#e-mail-state-(type=email)
22415 // - can ignore this case as we can still read out the erroneous email...
22416 return validity.badInput && !validity.typeMismatch ? undefined : value;
22421 function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) {
22422 badInputChecker(scope, element, attr, ctrl);
22423 baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
22425 ctrl.$$parserName = 'number';
22426 ctrl.$parsers.push(function(value) {
22427 if (ctrl.$isEmpty(value)) return null;
22428 if (NUMBER_REGEXP.test(value)) return parseFloat(value);
22432 ctrl.$formatters.push(function(value) {
22433 if (!ctrl.$isEmpty(value)) {
22434 if (!isNumber(value)) {
22435 throw ngModelMinErr('numfmt', 'Expected `{0}` to be a number', value);
22437 value = value.toString();
22442 if (isDefined(attr.min) || attr.ngMin) {
22444 ctrl.$validators.min = function(value) {
22445 return ctrl.$isEmpty(value) || isUndefined(minVal) || value >= minVal;
22448 attr.$observe('min', function(val) {
22449 if (isDefined(val) && !isNumber(val)) {
22450 val = parseFloat(val, 10);
22452 minVal = isNumber(val) && !isNaN(val) ? val : undefined;
22453 // TODO(matsko): implement validateLater to reduce number of validations
22458 if (isDefined(attr.max) || attr.ngMax) {
22460 ctrl.$validators.max = function(value) {
22461 return ctrl.$isEmpty(value) || isUndefined(maxVal) || value <= maxVal;
22464 attr.$observe('max', function(val) {
22465 if (isDefined(val) && !isNumber(val)) {
22466 val = parseFloat(val, 10);
22468 maxVal = isNumber(val) && !isNaN(val) ? val : undefined;
22469 // TODO(matsko): implement validateLater to reduce number of validations
22475 function urlInputType(scope, element, attr, ctrl, $sniffer, $browser) {
22476 // Note: no badInputChecker here by purpose as `url` is only a validation
22477 // in browsers, i.e. we can always read out input.value even if it is not valid!
22478 baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
22479 stringBasedInputType(ctrl);
22481 ctrl.$$parserName = 'url';
22482 ctrl.$validators.url = function(modelValue, viewValue) {
22483 var value = modelValue || viewValue;
22484 return ctrl.$isEmpty(value) || URL_REGEXP.test(value);
22488 function emailInputType(scope, element, attr, ctrl, $sniffer, $browser) {
22489 // Note: no badInputChecker here by purpose as `url` is only a validation
22490 // in browsers, i.e. we can always read out input.value even if it is not valid!
22491 baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
22492 stringBasedInputType(ctrl);
22494 ctrl.$$parserName = 'email';
22495 ctrl.$validators.email = function(modelValue, viewValue) {
22496 var value = modelValue || viewValue;
22497 return ctrl.$isEmpty(value) || EMAIL_REGEXP.test(value);
22501 function radioInputType(scope, element, attr, ctrl) {
22502 // make the name unique, if not defined
22503 if (isUndefined(attr.name)) {
22504 element.attr('name', nextUid());
22507 var listener = function(ev) {
22508 if (element[0].checked) {
22509 ctrl.$setViewValue(attr.value, ev && ev.type);
22513 element.on('click', listener);
22515 ctrl.$render = function() {
22516 var value = attr.value;
22517 element[0].checked = (value == ctrl.$viewValue);
22520 attr.$observe('value', ctrl.$render);
22523 function parseConstantExpr($parse, context, name, expression, fallback) {
22525 if (isDefined(expression)) {
22526 parseFn = $parse(expression);
22527 if (!parseFn.constant) {
22528 throw ngModelMinErr('constexpr', 'Expected constant expression for `{0}`, but saw ' +
22529 '`{1}`.', name, expression);
22531 return parseFn(context);
22536 function checkboxInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter, $parse) {
22537 var trueValue = parseConstantExpr($parse, scope, 'ngTrueValue', attr.ngTrueValue, true);
22538 var falseValue = parseConstantExpr($parse, scope, 'ngFalseValue', attr.ngFalseValue, false);
22540 var listener = function(ev) {
22541 ctrl.$setViewValue(element[0].checked, ev && ev.type);
22544 element.on('click', listener);
22546 ctrl.$render = function() {
22547 element[0].checked = ctrl.$viewValue;
22550 // Override the standard `$isEmpty` because the $viewValue of an empty checkbox is always set to `false`
22551 // This is because of the parser below, which compares the `$modelValue` with `trueValue` to convert
22552 // it to a boolean.
22553 ctrl.$isEmpty = function(value) {
22554 return value === false;
22557 ctrl.$formatters.push(function(value) {
22558 return equals(value, trueValue);
22561 ctrl.$parsers.push(function(value) {
22562 return value ? trueValue : falseValue;
22573 * HTML textarea element control with angular data-binding. The data-binding and validation
22574 * properties of this element are exactly the same as those of the
22575 * {@link ng.directive:input input element}.
22577 * @param {string} ngModel Assignable angular expression to data-bind to.
22578 * @param {string=} name Property name of the form under which the control is published.
22579 * @param {string=} required Sets `required` validation error key if the value is not entered.
22580 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
22581 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
22582 * `required` when you want to data-bind to the `required` attribute.
22583 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
22585 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
22586 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any
22588 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
22589 * a RegExp found by evaluating the Angular expression given in the attribute value.
22590 * If the expression evaluates to a RegExp object, then this is used directly.
22591 * If the expression evaluates to a string, then it will be converted to a RegExp
22592 * after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
22593 * `new RegExp('^abc$')`.<br />
22594 * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
22595 * start at the index of the last search's match, thus not taking the whole input value into
22597 * @param {string=} ngChange Angular expression to be executed when input changes due to user
22598 * interaction with the input element.
22599 * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
22609 * HTML input element control. When used together with {@link ngModel `ngModel`}, it provides data-binding,
22610 * input state control, and validation.
22611 * Input control follows HTML5 input types and polyfills the HTML5 validation behavior for older browsers.
22613 * <div class="alert alert-warning">
22614 * **Note:** Not every feature offered is available for all input types.
22615 * Specifically, data binding and event handling via `ng-model` is unsupported for `input[file]`.
22618 * @param {string} ngModel Assignable angular expression to data-bind to.
22619 * @param {string=} name Property name of the form under which the control is published.
22620 * @param {string=} required Sets `required` validation error key if the value is not entered.
22621 * @param {boolean=} ngRequired Sets `required` attribute if set to true
22622 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
22624 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
22625 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any
22627 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
22628 * a RegExp found by evaluating the Angular expression given in the attribute value.
22629 * If the expression evaluates to a RegExp object, then this is used directly.
22630 * If the expression evaluates to a string, then it will be converted to a RegExp
22631 * after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
22632 * `new RegExp('^abc$')`.<br />
22633 * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
22634 * start at the index of the last search's match, thus not taking the whole input value into
22636 * @param {string=} ngChange Angular expression to be executed when input changes due to user
22637 * interaction with the input element.
22638 * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
22639 * This parameter is ignored for input[type=password] controls, which will never trim the
22643 <example name="input-directive" module="inputExample">
22644 <file name="index.html">
22646 angular.module('inputExample', [])
22647 .controller('ExampleController', ['$scope', function($scope) {
22648 $scope.user = {name: 'guest', last: 'visitor'};
22651 <div ng-controller="ExampleController">
22652 <form name="myForm">
22655 <input type="text" name="userName" ng-model="user.name" required>
22658 <span class="error" ng-show="myForm.userName.$error.required">
22663 <input type="text" name="lastName" ng-model="user.last"
22664 ng-minlength="3" ng-maxlength="10">
22667 <span class="error" ng-show="myForm.lastName.$error.minlength">
22669 <span class="error" ng-show="myForm.lastName.$error.maxlength">
22674 <tt>user = {{user}}</tt><br/>
22675 <tt>myForm.userName.$valid = {{myForm.userName.$valid}}</tt><br/>
22676 <tt>myForm.userName.$error = {{myForm.userName.$error}}</tt><br/>
22677 <tt>myForm.lastName.$valid = {{myForm.lastName.$valid}}</tt><br/>
22678 <tt>myForm.lastName.$error = {{myForm.lastName.$error}}</tt><br/>
22679 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
22680 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
22681 <tt>myForm.$error.minlength = {{!!myForm.$error.minlength}}</tt><br/>
22682 <tt>myForm.$error.maxlength = {{!!myForm.$error.maxlength}}</tt><br/>
22685 <file name="protractor.js" type="protractor">
22686 var user = element(by.exactBinding('user'));
22687 var userNameValid = element(by.binding('myForm.userName.$valid'));
22688 var lastNameValid = element(by.binding('myForm.lastName.$valid'));
22689 var lastNameError = element(by.binding('myForm.lastName.$error'));
22690 var formValid = element(by.binding('myForm.$valid'));
22691 var userNameInput = element(by.model('user.name'));
22692 var userLastInput = element(by.model('user.last'));
22694 it('should initialize to model', function() {
22695 expect(user.getText()).toContain('{"name":"guest","last":"visitor"}');
22696 expect(userNameValid.getText()).toContain('true');
22697 expect(formValid.getText()).toContain('true');
22700 it('should be invalid if empty when required', function() {
22701 userNameInput.clear();
22702 userNameInput.sendKeys('');
22704 expect(user.getText()).toContain('{"last":"visitor"}');
22705 expect(userNameValid.getText()).toContain('false');
22706 expect(formValid.getText()).toContain('false');
22709 it('should be valid if empty when min length is set', function() {
22710 userLastInput.clear();
22711 userLastInput.sendKeys('');
22713 expect(user.getText()).toContain('{"name":"guest","last":""}');
22714 expect(lastNameValid.getText()).toContain('true');
22715 expect(formValid.getText()).toContain('true');
22718 it('should be invalid if less than required min length', function() {
22719 userLastInput.clear();
22720 userLastInput.sendKeys('xx');
22722 expect(user.getText()).toContain('{"name":"guest"}');
22723 expect(lastNameValid.getText()).toContain('false');
22724 expect(lastNameError.getText()).toContain('minlength');
22725 expect(formValid.getText()).toContain('false');
22728 it('should be invalid if longer than max length', function() {
22729 userLastInput.clear();
22730 userLastInput.sendKeys('some ridiculously long name');
22732 expect(user.getText()).toContain('{"name":"guest"}');
22733 expect(lastNameValid.getText()).toContain('false');
22734 expect(lastNameError.getText()).toContain('maxlength');
22735 expect(formValid.getText()).toContain('false');
22740 var inputDirective = ['$browser', '$sniffer', '$filter', '$parse',
22741 function($browser, $sniffer, $filter, $parse) {
22744 require: ['?ngModel'],
22746 pre: function(scope, element, attr, ctrls) {
22748 (inputType[lowercase(attr.type)] || inputType.text)(scope, element, attr, ctrls[0], $sniffer,
22749 $browser, $filter, $parse);
22758 var CONSTANT_VALUE_REGEXP = /^(true|false|\d+)$/;
22764 * Binds the given expression to the value of `<option>` or {@link input[radio] `input[radio]`},
22765 * so that when the element is selected, the {@link ngModel `ngModel`} of that element is set to
22768 * `ngValue` is useful when dynamically generating lists of radio buttons using
22769 * {@link ngRepeat `ngRepeat`}, as shown below.
22771 * Likewise, `ngValue` can be used to generate `<option>` elements for
22772 * the {@link select `select`} element. In that case however, only strings are supported
22773 * for the `value `attribute, so the resulting `ngModel` will always be a string.
22774 * Support for `select` models with non-string values is available via `ngOptions`.
22777 * @param {string=} ngValue angular expression, whose value will be bound to the `value` attribute
22778 * of the `input` element
22781 <example name="ngValue-directive" module="valueExample">
22782 <file name="index.html">
22784 angular.module('valueExample', [])
22785 .controller('ExampleController', ['$scope', function($scope) {
22786 $scope.names = ['pizza', 'unicorns', 'robots'];
22787 $scope.my = { favorite: 'unicorns' };
22790 <form ng-controller="ExampleController">
22791 <h2>Which is your favorite?</h2>
22792 <label ng-repeat="name in names" for="{{name}}">
22794 <input type="radio"
22795 ng-model="my.favorite"
22800 <div>You chose {{my.favorite}}</div>
22803 <file name="protractor.js" type="protractor">
22804 var favorite = element(by.binding('my.favorite'));
22806 it('should initialize to model', function() {
22807 expect(favorite.getText()).toContain('unicorns');
22809 it('should bind the values to the inputs', function() {
22810 element.all(by.model('my.favorite')).get(0).click();
22811 expect(favorite.getText()).toContain('pizza');
22816 var ngValueDirective = function() {
22820 compile: function(tpl, tplAttr) {
22821 if (CONSTANT_VALUE_REGEXP.test(tplAttr.ngValue)) {
22822 return function ngValueConstantLink(scope, elm, attr) {
22823 attr.$set('value', scope.$eval(attr.ngValue));
22826 return function ngValueLink(scope, elm, attr) {
22827 scope.$watch(attr.ngValue, function valueWatchAction(value) {
22828 attr.$set('value', value);
22842 * The `ngBind` attribute tells Angular to replace the text content of the specified HTML element
22843 * with the value of a given expression, and to update the text content when the value of that
22844 * expression changes.
22846 * Typically, you don't use `ngBind` directly, but instead you use the double curly markup like
22847 * `{{ expression }}` which is similar but less verbose.
22849 * It is preferable to use `ngBind` instead of `{{ expression }}` if a template is momentarily
22850 * displayed by the browser in its raw state before Angular compiles it. Since `ngBind` is an
22851 * element attribute, it makes the bindings invisible to the user while the page is loading.
22853 * An alternative solution to this problem would be using the
22854 * {@link ng.directive:ngCloak ngCloak} directive.
22858 * @param {expression} ngBind {@link guide/expression Expression} to evaluate.
22861 * Enter a name in the Live Preview text box; the greeting below the text box changes instantly.
22862 <example module="bindExample">
22863 <file name="index.html">
22865 angular.module('bindExample', [])
22866 .controller('ExampleController', ['$scope', function($scope) {
22867 $scope.name = 'Whirled';
22870 <div ng-controller="ExampleController">
22871 <label>Enter name: <input type="text" ng-model="name"></label><br>
22872 Hello <span ng-bind="name"></span>!
22875 <file name="protractor.js" type="protractor">
22876 it('should check ng-bind', function() {
22877 var nameInput = element(by.model('name'));
22879 expect(element(by.binding('name')).getText()).toBe('Whirled');
22881 nameInput.sendKeys('world');
22882 expect(element(by.binding('name')).getText()).toBe('world');
22887 var ngBindDirective = ['$compile', function($compile) {
22890 compile: function ngBindCompile(templateElement) {
22891 $compile.$$addBindingClass(templateElement);
22892 return function ngBindLink(scope, element, attr) {
22893 $compile.$$addBindingInfo(element, attr.ngBind);
22894 element = element[0];
22895 scope.$watch(attr.ngBind, function ngBindWatchAction(value) {
22896 element.textContent = isUndefined(value) ? '' : value;
22906 * @name ngBindTemplate
22909 * The `ngBindTemplate` directive specifies that the element
22910 * text content should be replaced with the interpolation of the template
22911 * in the `ngBindTemplate` attribute.
22912 * Unlike `ngBind`, the `ngBindTemplate` can contain multiple `{{` `}}`
22913 * expressions. This directive is needed since some HTML elements
22914 * (such as TITLE and OPTION) cannot contain SPAN elements.
22917 * @param {string} ngBindTemplate template of form
22918 * <tt>{{</tt> <tt>expression</tt> <tt>}}</tt> to eval.
22921 * Try it here: enter text in text box and watch the greeting change.
22922 <example module="bindExample">
22923 <file name="index.html">
22925 angular.module('bindExample', [])
22926 .controller('ExampleController', ['$scope', function($scope) {
22927 $scope.salutation = 'Hello';
22928 $scope.name = 'World';
22931 <div ng-controller="ExampleController">
22932 <label>Salutation: <input type="text" ng-model="salutation"></label><br>
22933 <label>Name: <input type="text" ng-model="name"></label><br>
22934 <pre ng-bind-template="{{salutation}} {{name}}!"></pre>
22937 <file name="protractor.js" type="protractor">
22938 it('should check ng-bind', function() {
22939 var salutationElem = element(by.binding('salutation'));
22940 var salutationInput = element(by.model('salutation'));
22941 var nameInput = element(by.model('name'));
22943 expect(salutationElem.getText()).toBe('Hello World!');
22945 salutationInput.clear();
22946 salutationInput.sendKeys('Greetings');
22948 nameInput.sendKeys('user');
22950 expect(salutationElem.getText()).toBe('Greetings user!');
22955 var ngBindTemplateDirective = ['$interpolate', '$compile', function($interpolate, $compile) {
22957 compile: function ngBindTemplateCompile(templateElement) {
22958 $compile.$$addBindingClass(templateElement);
22959 return function ngBindTemplateLink(scope, element, attr) {
22960 var interpolateFn = $interpolate(element.attr(attr.$attr.ngBindTemplate));
22961 $compile.$$addBindingInfo(element, interpolateFn.expressions);
22962 element = element[0];
22963 attr.$observe('ngBindTemplate', function(value) {
22964 element.textContent = isUndefined(value) ? '' : value;
22977 * Evaluates the expression and inserts the resulting HTML into the element in a secure way. By default,
22978 * the resulting HTML content will be sanitized using the {@link ngSanitize.$sanitize $sanitize} service.
22979 * To utilize this functionality, ensure that `$sanitize` is available, for example, by including {@link
22980 * ngSanitize} in your module's dependencies (not in core Angular). In order to use {@link ngSanitize}
22981 * in your module's dependencies, you need to include "angular-sanitize.js" in your application.
22983 * You may also bypass sanitization for values you know are safe. To do so, bind to
22984 * an explicitly trusted value via {@link ng.$sce#trustAsHtml $sce.trustAsHtml}. See the example
22985 * under {@link ng.$sce#show-me-an-example-using-sce- Strict Contextual Escaping (SCE)}.
22987 * Note: If a `$sanitize` service is unavailable and the bound value isn't explicitly trusted, you
22988 * will have an exception (instead of an exploit.)
22991 * @param {expression} ngBindHtml {@link guide/expression Expression} to evaluate.
22995 <example module="bindHtmlExample" deps="angular-sanitize.js">
22996 <file name="index.html">
22997 <div ng-controller="ExampleController">
22998 <p ng-bind-html="myHTML"></p>
23002 <file name="script.js">
23003 angular.module('bindHtmlExample', ['ngSanitize'])
23004 .controller('ExampleController', ['$scope', function($scope) {
23006 'I am an <code>HTML</code>string with ' +
23007 '<a href="#">links!</a> and other <em>stuff</em>';
23011 <file name="protractor.js" type="protractor">
23012 it('should check ng-bind-html', function() {
23013 expect(element(by.binding('myHTML')).getText()).toBe(
23014 'I am an HTMLstring with links! and other stuff');
23019 var ngBindHtmlDirective = ['$sce', '$parse', '$compile', function($sce, $parse, $compile) {
23022 compile: function ngBindHtmlCompile(tElement, tAttrs) {
23023 var ngBindHtmlGetter = $parse(tAttrs.ngBindHtml);
23024 var ngBindHtmlWatch = $parse(tAttrs.ngBindHtml, function getStringValue(value) {
23025 return (value || '').toString();
23027 $compile.$$addBindingClass(tElement);
23029 return function ngBindHtmlLink(scope, element, attr) {
23030 $compile.$$addBindingInfo(element, attr.ngBindHtml);
23032 scope.$watch(ngBindHtmlWatch, function ngBindHtmlWatchAction() {
23033 // we re-evaluate the expr because we want a TrustedValueHolderType
23034 // for $sce, not a string
23035 element.html($sce.getTrustedHtml(ngBindHtmlGetter(scope)) || '');
23047 * Evaluate the given expression when the user changes the input.
23048 * The expression is evaluated immediately, unlike the JavaScript onchange event
23049 * which only triggers at the end of a change (usually, when the user leaves the
23050 * form element or presses the return key).
23052 * The `ngChange` expression is only evaluated when a change in the input value causes
23053 * a new value to be committed to the model.
23055 * It will not be evaluated:
23056 * * if the value returned from the `$parsers` transformation pipeline has not changed
23057 * * if the input has continued to be invalid since the model will stay `null`
23058 * * if the model is changed programmatically and not by a change to the input value
23061 * Note, this directive requires `ngModel` to be present.
23064 * @param {expression} ngChange {@link guide/expression Expression} to evaluate upon change
23068 * <example name="ngChange-directive" module="changeExample">
23069 * <file name="index.html">
23071 * angular.module('changeExample', [])
23072 * .controller('ExampleController', ['$scope', function($scope) {
23073 * $scope.counter = 0;
23074 * $scope.change = function() {
23075 * $scope.counter++;
23079 * <div ng-controller="ExampleController">
23080 * <input type="checkbox" ng-model="confirmed" ng-change="change()" id="ng-change-example1" />
23081 * <input type="checkbox" ng-model="confirmed" id="ng-change-example2" />
23082 * <label for="ng-change-example2">Confirmed</label><br />
23083 * <tt>debug = {{confirmed}}</tt><br/>
23084 * <tt>counter = {{counter}}</tt><br/>
23087 * <file name="protractor.js" type="protractor">
23088 * var counter = element(by.binding('counter'));
23089 * var debug = element(by.binding('confirmed'));
23091 * it('should evaluate the expression if changing from view', function() {
23092 * expect(counter.getText()).toContain('0');
23094 * element(by.id('ng-change-example1')).click();
23096 * expect(counter.getText()).toContain('1');
23097 * expect(debug.getText()).toContain('true');
23100 * it('should not evaluate the expression if changing from model', function() {
23101 * element(by.id('ng-change-example2')).click();
23103 * expect(counter.getText()).toContain('0');
23104 * expect(debug.getText()).toContain('true');
23109 var ngChangeDirective = valueFn({
23111 require: 'ngModel',
23112 link: function(scope, element, attr, ctrl) {
23113 ctrl.$viewChangeListeners.push(function() {
23114 scope.$eval(attr.ngChange);
23119 function classDirective(name, selector) {
23120 name = 'ngClass' + name;
23121 return ['$animate', function($animate) {
23124 link: function(scope, element, attr) {
23127 scope.$watch(attr[name], ngClassWatchAction, true);
23129 attr.$observe('class', function(value) {
23130 ngClassWatchAction(scope.$eval(attr[name]));
23134 if (name !== 'ngClass') {
23135 scope.$watch('$index', function($index, old$index) {
23136 // jshint bitwise: false
23137 var mod = $index & 1;
23138 if (mod !== (old$index & 1)) {
23139 var classes = arrayClasses(scope.$eval(attr[name]));
23141 addClasses(classes) :
23142 removeClasses(classes);
23147 function addClasses(classes) {
23148 var newClasses = digestClassCounts(classes, 1);
23149 attr.$addClass(newClasses);
23152 function removeClasses(classes) {
23153 var newClasses = digestClassCounts(classes, -1);
23154 attr.$removeClass(newClasses);
23157 function digestClassCounts(classes, count) {
23158 // Use createMap() to prevent class assumptions involving property
23159 // names in Object.prototype
23160 var classCounts = element.data('$classCounts') || createMap();
23161 var classesToUpdate = [];
23162 forEach(classes, function(className) {
23163 if (count > 0 || classCounts[className]) {
23164 classCounts[className] = (classCounts[className] || 0) + count;
23165 if (classCounts[className] === +(count > 0)) {
23166 classesToUpdate.push(className);
23170 element.data('$classCounts', classCounts);
23171 return classesToUpdate.join(' ');
23174 function updateClasses(oldClasses, newClasses) {
23175 var toAdd = arrayDifference(newClasses, oldClasses);
23176 var toRemove = arrayDifference(oldClasses, newClasses);
23177 toAdd = digestClassCounts(toAdd, 1);
23178 toRemove = digestClassCounts(toRemove, -1);
23179 if (toAdd && toAdd.length) {
23180 $animate.addClass(element, toAdd);
23182 if (toRemove && toRemove.length) {
23183 $animate.removeClass(element, toRemove);
23187 function ngClassWatchAction(newVal) {
23188 if (selector === true || scope.$index % 2 === selector) {
23189 var newClasses = arrayClasses(newVal || []);
23191 addClasses(newClasses);
23192 } else if (!equals(newVal,oldVal)) {
23193 var oldClasses = arrayClasses(oldVal);
23194 updateClasses(oldClasses, newClasses);
23197 oldVal = shallowCopy(newVal);
23202 function arrayDifference(tokens1, tokens2) {
23206 for (var i = 0; i < tokens1.length; i++) {
23207 var token = tokens1[i];
23208 for (var j = 0; j < tokens2.length; j++) {
23209 if (token == tokens2[j]) continue outer;
23211 values.push(token);
23216 function arrayClasses(classVal) {
23218 if (isArray(classVal)) {
23219 forEach(classVal, function(v) {
23220 classes = classes.concat(arrayClasses(v));
23223 } else if (isString(classVal)) {
23224 return classVal.split(' ');
23225 } else if (isObject(classVal)) {
23226 forEach(classVal, function(v, k) {
23228 classes = classes.concat(k.split(' '));
23244 * The `ngClass` directive allows you to dynamically set CSS classes on an HTML element by databinding
23245 * an expression that represents all classes to be added.
23247 * The directive operates in three different ways, depending on which of three types the expression
23250 * 1. If the expression evaluates to a string, the string should be one or more space-delimited class
23253 * 2. If the expression evaluates to an object, then for each key-value pair of the
23254 * object with a truthy value the corresponding key is used as a class name.
23256 * 3. If the expression evaluates to an array, each element of the array should either be a string as in
23257 * type 1 or an object as in type 2. This means that you can mix strings and objects together in an array
23258 * to give you more control over what CSS classes appear. See the code below for an example of this.
23261 * The directive won't add duplicate classes if a particular class was already set.
23263 * When the expression changes, the previously added classes are removed and only then are the
23264 * new classes added.
23267 * **add** - happens just before the class is applied to the elements
23269 * **remove** - happens just before the class is removed from the element
23272 * @param {expression} ngClass {@link guide/expression Expression} to eval. The result
23273 * of the evaluation can be a string representing space delimited class
23274 * names, an array, or a map of class names to boolean values. In the case of a map, the
23275 * names of the properties whose values are truthy will be added as css classes to the
23278 * @example Example that demonstrates basic bindings via ngClass directive.
23280 <file name="index.html">
23281 <p ng-class="{strike: deleted, bold: important, 'has-error': error}">Map Syntax Example</p>
23283 <input type="checkbox" ng-model="deleted">
23284 deleted (apply "strike" class)
23287 <input type="checkbox" ng-model="important">
23288 important (apply "bold" class)
23291 <input type="checkbox" ng-model="error">
23292 error (apply "has-error" class)
23295 <p ng-class="style">Using String Syntax</p>
23296 <input type="text" ng-model="style"
23297 placeholder="Type: bold strike red" aria-label="Type: bold strike red">
23299 <p ng-class="[style1, style2, style3]">Using Array Syntax</p>
23300 <input ng-model="style1"
23301 placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red"><br>
23302 <input ng-model="style2"
23303 placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red 2"><br>
23304 <input ng-model="style3"
23305 placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red 3"><br>
23307 <p ng-class="[style4, {orange: warning}]">Using Array and Map Syntax</p>
23308 <input ng-model="style4" placeholder="Type: bold, strike" aria-label="Type: bold, strike"><br>
23309 <label><input type="checkbox" ng-model="warning"> warning (apply "orange" class)</label>
23311 <file name="style.css">
23313 text-decoration: line-through;
23323 background-color: yellow;
23329 <file name="protractor.js" type="protractor">
23330 var ps = element.all(by.css('p'));
23332 it('should let you toggle the class', function() {
23334 expect(ps.first().getAttribute('class')).not.toMatch(/bold/);
23335 expect(ps.first().getAttribute('class')).not.toMatch(/has-error/);
23337 element(by.model('important')).click();
23338 expect(ps.first().getAttribute('class')).toMatch(/bold/);
23340 element(by.model('error')).click();
23341 expect(ps.first().getAttribute('class')).toMatch(/has-error/);
23344 it('should let you toggle string example', function() {
23345 expect(ps.get(1).getAttribute('class')).toBe('');
23346 element(by.model('style')).clear();
23347 element(by.model('style')).sendKeys('red');
23348 expect(ps.get(1).getAttribute('class')).toBe('red');
23351 it('array example should have 3 classes', function() {
23352 expect(ps.get(2).getAttribute('class')).toBe('');
23353 element(by.model('style1')).sendKeys('bold');
23354 element(by.model('style2')).sendKeys('strike');
23355 element(by.model('style3')).sendKeys('red');
23356 expect(ps.get(2).getAttribute('class')).toBe('bold strike red');
23359 it('array with map example should have 2 classes', function() {
23360 expect(ps.last().getAttribute('class')).toBe('');
23361 element(by.model('style4')).sendKeys('bold');
23362 element(by.model('warning')).click();
23363 expect(ps.last().getAttribute('class')).toBe('bold orange');
23370 The example below demonstrates how to perform animations using ngClass.
23372 <example module="ngAnimate" deps="angular-animate.js" animations="true">
23373 <file name="index.html">
23374 <input id="setbtn" type="button" value="set" ng-click="myVar='my-class'">
23375 <input id="clearbtn" type="button" value="clear" ng-click="myVar=''">
23377 <span class="base-class" ng-class="myVar">Sample Text</span>
23379 <file name="style.css">
23381 transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
23384 .base-class.my-class {
23389 <file name="protractor.js" type="protractor">
23390 it('should check ng-class', function() {
23391 expect(element(by.css('.base-class')).getAttribute('class')).not.
23392 toMatch(/my-class/);
23394 element(by.id('setbtn')).click();
23396 expect(element(by.css('.base-class')).getAttribute('class')).
23397 toMatch(/my-class/);
23399 element(by.id('clearbtn')).click();
23401 expect(element(by.css('.base-class')).getAttribute('class')).not.
23402 toMatch(/my-class/);
23408 ## ngClass and pre-existing CSS3 Transitions/Animations
23409 The ngClass directive still supports CSS3 Transitions/Animations even if they do not follow the ngAnimate CSS naming structure.
23410 Upon animation ngAnimate will apply supplementary CSS classes to track the start and end of an animation, but this will not hinder
23411 any pre-existing CSS transitions already on the element. To get an idea of what happens during a class-based animation, be sure
23412 to view the step by step details of {@link $animate#addClass $animate.addClass} and
23413 {@link $animate#removeClass $animate.removeClass}.
23415 var ngClassDirective = classDirective('', true);
23423 * The `ngClassOdd` and `ngClassEven` directives work exactly as
23424 * {@link ng.directive:ngClass ngClass}, except they work in
23425 * conjunction with `ngRepeat` and take effect only on odd (even) rows.
23427 * This directive can be applied only within the scope of an
23428 * {@link ng.directive:ngRepeat ngRepeat}.
23431 * @param {expression} ngClassOdd {@link guide/expression Expression} to eval. The result
23432 * of the evaluation can be a string representing space delimited class names or an array.
23436 <file name="index.html">
23437 <ol ng-init="names=['John', 'Mary', 'Cate', 'Suz']">
23438 <li ng-repeat="name in names">
23439 <span ng-class-odd="'odd'" ng-class-even="'even'">
23445 <file name="style.css">
23453 <file name="protractor.js" type="protractor">
23454 it('should check ng-class-odd and ng-class-even', function() {
23455 expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')).
23457 expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')).
23463 var ngClassOddDirective = classDirective('Odd', 0);
23467 * @name ngClassEven
23471 * The `ngClassOdd` and `ngClassEven` directives work exactly as
23472 * {@link ng.directive:ngClass ngClass}, except they work in
23473 * conjunction with `ngRepeat` and take effect only on odd (even) rows.
23475 * This directive can be applied only within the scope of an
23476 * {@link ng.directive:ngRepeat ngRepeat}.
23479 * @param {expression} ngClassEven {@link guide/expression Expression} to eval. The
23480 * result of the evaluation can be a string representing space delimited class names or an array.
23484 <file name="index.html">
23485 <ol ng-init="names=['John', 'Mary', 'Cate', 'Suz']">
23486 <li ng-repeat="name in names">
23487 <span ng-class-odd="'odd'" ng-class-even="'even'">
23488 {{name}}
23493 <file name="style.css">
23501 <file name="protractor.js" type="protractor">
23502 it('should check ng-class-odd and ng-class-even', function() {
23503 expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')).
23505 expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')).
23511 var ngClassEvenDirective = classDirective('Even', 1);
23519 * The `ngCloak` directive is used to prevent the Angular html template from being briefly
23520 * displayed by the browser in its raw (uncompiled) form while your application is loading. Use this
23521 * directive to avoid the undesirable flicker effect caused by the html template display.
23523 * The directive can be applied to the `<body>` element, but the preferred usage is to apply
23524 * multiple `ngCloak` directives to small portions of the page to permit progressive rendering
23525 * of the browser view.
23527 * `ngCloak` works in cooperation with the following css rule embedded within `angular.js` and
23528 * `angular.min.js`.
23529 * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
23532 * [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
23533 * display: none !important;
23537 * When this css rule is loaded by the browser, all html elements (including their children) that
23538 * are tagged with the `ngCloak` directive are hidden. When Angular encounters this directive
23539 * during the compilation of the template it deletes the `ngCloak` element attribute, making
23540 * the compiled element visible.
23542 * For the best result, the `angular.js` script must be loaded in the head section of the html
23543 * document; alternatively, the css rule above must be included in the external stylesheet of the
23550 <file name="index.html">
23551 <div id="template1" ng-cloak>{{ 'hello' }}</div>
23552 <div id="template2" class="ng-cloak">{{ 'world' }}</div>
23554 <file name="protractor.js" type="protractor">
23555 it('should remove the template directive and css class', function() {
23556 expect($('#template1').getAttribute('ng-cloak')).
23558 expect($('#template2').getAttribute('ng-cloak')).
23565 var ngCloakDirective = ngDirective({
23566 compile: function(element, attr) {
23567 attr.$set('ngCloak', undefined);
23568 element.removeClass('ng-cloak');
23574 * @name ngController
23577 * The `ngController` directive attaches a controller class to the view. This is a key aspect of how angular
23578 * supports the principles behind the Model-View-Controller design pattern.
23580 * MVC components in angular:
23582 * * Model — Models are the properties of a scope; scopes are attached to the DOM where scope properties
23583 * are accessed through bindings.
23584 * * View — The template (HTML with data bindings) that is rendered into the View.
23585 * * Controller — The `ngController` directive specifies a Controller class; the class contains business
23586 * logic behind the application to decorate the scope with functions and values
23588 * Note that you can also attach controllers to the DOM by declaring it in a route definition
23589 * via the {@link ngRoute.$route $route} service. A common mistake is to declare the controller
23590 * again using `ng-controller` in the template itself. This will cause the controller to be attached
23591 * and executed twice.
23596 * @param {expression} ngController Name of a constructor function registered with the current
23597 * {@link ng.$controllerProvider $controllerProvider} or an {@link guide/expression expression}
23598 * that on the current scope evaluates to a constructor function.
23600 * The controller instance can be published into a scope property by specifying
23601 * `ng-controller="as propertyName"`.
23603 * If the current `$controllerProvider` is configured to use globals (via
23604 * {@link ng.$controllerProvider#allowGlobals `$controllerProvider.allowGlobals()` }), this may
23605 * also be the name of a globally accessible constructor function (not recommended).
23608 * Here is a simple form for editing user contact information. Adding, removing, clearing, and
23609 * greeting are methods declared on the controller (see source tab). These methods can
23610 * easily be called from the angular markup. Any changes to the data are automatically reflected
23611 * in the View without the need for a manual update.
23613 * Two different declaration styles are included below:
23615 * * one binds methods and properties directly onto the controller using `this`:
23616 * `ng-controller="SettingsController1 as settings"`
23617 * * one injects `$scope` into the controller:
23618 * `ng-controller="SettingsController2"`
23620 * The second option is more common in the Angular community, and is generally used in boilerplates
23621 * and in this guide. However, there are advantages to binding properties directly to the controller
23622 * and avoiding scope.
23624 * * Using `controller as` makes it obvious which controller you are accessing in the template when
23625 * multiple controllers apply to an element.
23626 * * If you are writing your controllers as classes you have easier access to the properties and
23627 * methods, which will appear on the scope, from inside the controller code.
23628 * * Since there is always a `.` in the bindings, you don't have to worry about prototypal
23629 * inheritance masking primitives.
23631 * This example demonstrates the `controller as` syntax.
23633 * <example name="ngControllerAs" module="controllerAsExample">
23634 * <file name="index.html">
23635 * <div id="ctrl-as-exmpl" ng-controller="SettingsController1 as settings">
23636 * <label>Name: <input type="text" ng-model="settings.name"/></label>
23637 * <button ng-click="settings.greet()">greet</button><br/>
23640 * <li ng-repeat="contact in settings.contacts">
23641 * <select ng-model="contact.type" aria-label="Contact method" id="select_{{$index}}">
23642 * <option>phone</option>
23643 * <option>email</option>
23645 * <input type="text" ng-model="contact.value" aria-labelledby="select_{{$index}}" />
23646 * <button ng-click="settings.clearContact(contact)">clear</button>
23647 * <button ng-click="settings.removeContact(contact)" aria-label="Remove">X</button>
23649 * <li><button ng-click="settings.addContact()">add</button></li>
23653 * <file name="app.js">
23654 * angular.module('controllerAsExample', [])
23655 * .controller('SettingsController1', SettingsController1);
23657 * function SettingsController1() {
23658 * this.name = "John Smith";
23659 * this.contacts = [
23660 * {type: 'phone', value: '408 555 1212'},
23661 * {type: 'email', value: 'john.smith@example.org'} ];
23664 * SettingsController1.prototype.greet = function() {
23665 * alert(this.name);
23668 * SettingsController1.prototype.addContact = function() {
23669 * this.contacts.push({type: 'email', value: 'yourname@example.org'});
23672 * SettingsController1.prototype.removeContact = function(contactToRemove) {
23673 * var index = this.contacts.indexOf(contactToRemove);
23674 * this.contacts.splice(index, 1);
23677 * SettingsController1.prototype.clearContact = function(contact) {
23678 * contact.type = 'phone';
23679 * contact.value = '';
23682 * <file name="protractor.js" type="protractor">
23683 * it('should check controller as', function() {
23684 * var container = element(by.id('ctrl-as-exmpl'));
23685 * expect(container.element(by.model('settings.name'))
23686 * .getAttribute('value')).toBe('John Smith');
23688 * var firstRepeat =
23689 * container.element(by.repeater('contact in settings.contacts').row(0));
23690 * var secondRepeat =
23691 * container.element(by.repeater('contact in settings.contacts').row(1));
23693 * expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
23694 * .toBe('408 555 1212');
23696 * expect(secondRepeat.element(by.model('contact.value')).getAttribute('value'))
23697 * .toBe('john.smith@example.org');
23699 * firstRepeat.element(by.buttonText('clear')).click();
23701 * expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
23704 * container.element(by.buttonText('add')).click();
23706 * expect(container.element(by.repeater('contact in settings.contacts').row(2))
23707 * .element(by.model('contact.value'))
23708 * .getAttribute('value'))
23709 * .toBe('yourname@example.org');
23714 * This example demonstrates the "attach to `$scope`" style of controller.
23716 * <example name="ngController" module="controllerExample">
23717 * <file name="index.html">
23718 * <div id="ctrl-exmpl" ng-controller="SettingsController2">
23719 * <label>Name: <input type="text" ng-model="name"/></label>
23720 * <button ng-click="greet()">greet</button><br/>
23723 * <li ng-repeat="contact in contacts">
23724 * <select ng-model="contact.type" id="select_{{$index}}">
23725 * <option>phone</option>
23726 * <option>email</option>
23728 * <input type="text" ng-model="contact.value" aria-labelledby="select_{{$index}}" />
23729 * <button ng-click="clearContact(contact)">clear</button>
23730 * <button ng-click="removeContact(contact)">X</button>
23732 * <li>[ <button ng-click="addContact()">add</button> ]</li>
23736 * <file name="app.js">
23737 * angular.module('controllerExample', [])
23738 * .controller('SettingsController2', ['$scope', SettingsController2]);
23740 * function SettingsController2($scope) {
23741 * $scope.name = "John Smith";
23742 * $scope.contacts = [
23743 * {type:'phone', value:'408 555 1212'},
23744 * {type:'email', value:'john.smith@example.org'} ];
23746 * $scope.greet = function() {
23747 * alert($scope.name);
23750 * $scope.addContact = function() {
23751 * $scope.contacts.push({type:'email', value:'yourname@example.org'});
23754 * $scope.removeContact = function(contactToRemove) {
23755 * var index = $scope.contacts.indexOf(contactToRemove);
23756 * $scope.contacts.splice(index, 1);
23759 * $scope.clearContact = function(contact) {
23760 * contact.type = 'phone';
23761 * contact.value = '';
23765 * <file name="protractor.js" type="protractor">
23766 * it('should check controller', function() {
23767 * var container = element(by.id('ctrl-exmpl'));
23769 * expect(container.element(by.model('name'))
23770 * .getAttribute('value')).toBe('John Smith');
23772 * var firstRepeat =
23773 * container.element(by.repeater('contact in contacts').row(0));
23774 * var secondRepeat =
23775 * container.element(by.repeater('contact in contacts').row(1));
23777 * expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
23778 * .toBe('408 555 1212');
23779 * expect(secondRepeat.element(by.model('contact.value')).getAttribute('value'))
23780 * .toBe('john.smith@example.org');
23782 * firstRepeat.element(by.buttonText('clear')).click();
23784 * expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
23787 * container.element(by.buttonText('add')).click();
23789 * expect(container.element(by.repeater('contact in contacts').row(2))
23790 * .element(by.model('contact.value'))
23791 * .getAttribute('value'))
23792 * .toBe('yourname@example.org');
23798 var ngControllerDirective = [function() {
23814 * Angular has some features that can break certain
23815 * [CSP (Content Security Policy)](https://developer.mozilla.org/en/Security/CSP) rules.
23817 * If you intend to implement these rules then you must tell Angular not to use these features.
23819 * This is necessary when developing things like Google Chrome Extensions or Universal Windows Apps.
23822 * The following rules affect Angular:
23824 * * `unsafe-eval`: this rule forbids apps to use `eval` or `Function(string)` generated functions
23825 * (among other things). Angular makes use of this in the {@link $parse} service to provide a 30%
23826 * increase in the speed of evaluating Angular expressions.
23828 * * `unsafe-inline`: this rule forbids apps from inject custom styles into the document. Angular
23829 * makes use of this to include some CSS rules (e.g. {@link ngCloak} and {@link ngHide}).
23830 * To make these directives work when a CSP rule is blocking inline styles, you must link to the
23831 * `angular-csp.css` in your HTML manually.
23833 * If you do not provide `ngCsp` then Angular tries to autodetect if CSP is blocking unsafe-eval
23834 * and automatically deactivates this feature in the {@link $parse} service. This autodetection,
23835 * however, triggers a CSP error to be logged in the console:
23838 * Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of
23839 * script in the following Content Security Policy directive: "default-src 'self'". Note that
23840 * 'script-src' was not explicitly set, so 'default-src' is used as a fallback.
23843 * This error is harmless but annoying. To prevent the error from showing up, put the `ngCsp`
23844 * directive on an element of the HTML document that appears before the `<script>` tag that loads
23845 * the `angular.js` file.
23847 * *Note: This directive is only available in the `ng-csp` and `data-ng-csp` attribute form.*
23849 * You can specify which of the CSP related Angular features should be deactivated by providing
23850 * a value for the `ng-csp` attribute. The options are as follows:
23852 * * no-inline-style: this stops Angular from injecting CSS styles into the DOM
23854 * * no-unsafe-eval: this stops Angular from optimising $parse with unsafe eval of strings
23856 * You can use these values in the following combinations:
23859 * * No declaration means that Angular will assume that you can do inline styles, but it will do
23860 * a runtime check for unsafe-eval. E.g. `<body>`. This is backwardly compatible with previous versions
23863 * * A simple `ng-csp` (or `data-ng-csp`) attribute will tell Angular to deactivate both inline
23864 * styles and unsafe eval. E.g. `<body ng-csp>`. This is backwardly compatible with previous versions
23867 * * Specifying only `no-unsafe-eval` tells Angular that we must not use eval, but that we can inject
23868 * inline styles. E.g. `<body ng-csp="no-unsafe-eval">`.
23870 * * Specifying only `no-inline-style` tells Angular that we must not inject styles, but that we can
23871 * run eval - no automcatic check for unsafe eval will occur. E.g. `<body ng-csp="no-inline-style">`
23873 * * Specifying both `no-unsafe-eval` and `no-inline-style` tells Angular that we must not inject
23874 * styles nor use eval, which is the same as an empty: ng-csp.
23875 * E.g.`<body ng-csp="no-inline-style;no-unsafe-eval">`
23878 * This example shows how to apply the `ngCsp` directive to the `html` tag.
23881 <html ng-app ng-csp>
23887 // Note: the suffix `.csp` in the example name triggers
23888 // csp mode in our http server!
23889 <example name="example.csp" module="cspExample" ng-csp="true">
23890 <file name="index.html">
23891 <div ng-controller="MainController as ctrl">
23893 <button ng-click="ctrl.inc()" id="inc">Increment</button>
23894 <span id="counter">
23900 <button ng-click="ctrl.evil()" id="evil">Evil</button>
23901 <span id="evilError">
23907 <file name="script.js">
23908 angular.module('cspExample', [])
23909 .controller('MainController', function() {
23911 this.inc = function() {
23914 this.evil = function() {
23915 // jshint evil:true
23919 this.evilError = e.message;
23924 <file name="protractor.js" type="protractor">
23925 var util, webdriver;
23927 var incBtn = element(by.id('inc'));
23928 var counter = element(by.id('counter'));
23929 var evilBtn = element(by.id('evil'));
23930 var evilError = element(by.id('evilError'));
23932 function getAndClearSevereErrors() {
23933 return browser.manage().logs().get('browser').then(function(browserLog) {
23934 return browserLog.filter(function(logEntry) {
23935 return logEntry.level.value > webdriver.logging.Level.WARNING.value;
23940 function clearErrors() {
23941 getAndClearSevereErrors();
23944 function expectNoErrors() {
23945 getAndClearSevereErrors().then(function(filteredLog) {
23946 expect(filteredLog.length).toEqual(0);
23947 if (filteredLog.length) {
23948 console.log('browser console errors: ' + util.inspect(filteredLog));
23953 function expectError(regex) {
23954 getAndClearSevereErrors().then(function(filteredLog) {
23956 filteredLog.forEach(function(log) {
23957 if (log.message.match(regex)) {
23962 throw new Error('expected an error that matches ' + regex);
23967 beforeEach(function() {
23968 util = require('util');
23969 webdriver = require('protractor/node_modules/selenium-webdriver');
23972 // For now, we only test on Chrome,
23973 // as Safari does not load the page with Protractor's injected scripts,
23974 // and Firefox webdriver always disables content security policy (#6358)
23975 if (browser.params.browser !== 'chrome') {
23979 it('should not report errors when the page is loaded', function() {
23980 // clear errors so we are not dependent on previous tests
23982 // Need to reload the page as the page is already loaded when
23984 browser.driver.getCurrentUrl().then(function(url) {
23990 it('should evaluate expressions', function() {
23991 expect(counter.getText()).toEqual('0');
23993 expect(counter.getText()).toEqual('1');
23997 it('should throw and report an error when using "eval"', function() {
23999 expect(evilError.getText()).toMatch(/Content Security Policy/);
24000 expectError(/Content Security Policy/);
24006 // ngCsp is not implemented as a proper directive any more, because we need it be processed while we
24007 // bootstrap the system (before $parse is instantiated), for this reason we just have
24008 // the csp() fn that looks for the `ng-csp` attribute anywhere in the current doc
24015 * The ngClick directive allows you to specify custom behavior when
24016 * an element is clicked.
24020 * @param {expression} ngClick {@link guide/expression Expression} to evaluate upon
24021 * click. ({@link guide/expression#-event- Event object is available as `$event`})
24025 <file name="index.html">
24026 <button ng-click="count = count + 1" ng-init="count=0">
24033 <file name="protractor.js" type="protractor">
24034 it('should check ng-click', function() {
24035 expect(element(by.binding('count')).getText()).toMatch('0');
24036 element(by.css('button')).click();
24037 expect(element(by.binding('count')).getText()).toMatch('1');
24043 * A collection of directives that allows creation of custom event handlers that are defined as
24044 * angular expressions and are compiled and executed within the current scope.
24046 var ngEventDirectives = {};
24048 // For events that might fire synchronously during DOM manipulation
24049 // we need to execute their event handlers asynchronously using $evalAsync,
24050 // so that they are not executed in an inconsistent state.
24051 var forceAsyncEvents = {
24056 'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste'.split(' '),
24057 function(eventName) {
24058 var directiveName = directiveNormalize('ng-' + eventName);
24059 ngEventDirectives[directiveName] = ['$parse', '$rootScope', function($parse, $rootScope) {
24062 compile: function($element, attr) {
24063 // We expose the powerful $event object on the scope that provides access to the Window,
24064 // etc. that isn't protected by the fast paths in $parse. We explicitly request better
24065 // checks at the cost of speed since event handler expressions are not executed as
24066 // frequently as regular change detection.
24067 var fn = $parse(attr[directiveName], /* interceptorFn */ null, /* expensiveChecks */ true);
24068 return function ngEventHandler(scope, element) {
24069 element.on(eventName, function(event) {
24070 var callback = function() {
24071 fn(scope, {$event:event});
24073 if (forceAsyncEvents[eventName] && $rootScope.$$phase) {
24074 scope.$evalAsync(callback);
24076 scope.$apply(callback);
24091 * The `ngDblclick` directive allows you to specify custom behavior on a dblclick event.
24095 * @param {expression} ngDblclick {@link guide/expression Expression} to evaluate upon
24096 * a dblclick. (The Event object is available as `$event`)
24100 <file name="index.html">
24101 <button ng-dblclick="count = count + 1" ng-init="count=0">
24102 Increment (on double click)
24112 * @name ngMousedown
24115 * The ngMousedown directive allows you to specify custom behavior on mousedown event.
24119 * @param {expression} ngMousedown {@link guide/expression Expression} to evaluate upon
24120 * mousedown. ({@link guide/expression#-event- Event object is available as `$event`})
24124 <file name="index.html">
24125 <button ng-mousedown="count = count + 1" ng-init="count=0">
24126 Increment (on mouse down)
24139 * Specify custom behavior on mouseup event.
24143 * @param {expression} ngMouseup {@link guide/expression Expression} to evaluate upon
24144 * mouseup. ({@link guide/expression#-event- Event object is available as `$event`})
24148 <file name="index.html">
24149 <button ng-mouseup="count = count + 1" ng-init="count=0">
24150 Increment (on mouse up)
24159 * @name ngMouseover
24162 * Specify custom behavior on mouseover event.
24166 * @param {expression} ngMouseover {@link guide/expression Expression} to evaluate upon
24167 * mouseover. ({@link guide/expression#-event- Event object is available as `$event`})
24171 <file name="index.html">
24172 <button ng-mouseover="count = count + 1" ng-init="count=0">
24173 Increment (when mouse is over)
24183 * @name ngMouseenter
24186 * Specify custom behavior on mouseenter event.
24190 * @param {expression} ngMouseenter {@link guide/expression Expression} to evaluate upon
24191 * mouseenter. ({@link guide/expression#-event- Event object is available as `$event`})
24195 <file name="index.html">
24196 <button ng-mouseenter="count = count + 1" ng-init="count=0">
24197 Increment (when mouse enters)
24207 * @name ngMouseleave
24210 * Specify custom behavior on mouseleave event.
24214 * @param {expression} ngMouseleave {@link guide/expression Expression} to evaluate upon
24215 * mouseleave. ({@link guide/expression#-event- Event object is available as `$event`})
24219 <file name="index.html">
24220 <button ng-mouseleave="count = count + 1" ng-init="count=0">
24221 Increment (when mouse leaves)
24231 * @name ngMousemove
24234 * Specify custom behavior on mousemove event.
24238 * @param {expression} ngMousemove {@link guide/expression Expression} to evaluate upon
24239 * mousemove. ({@link guide/expression#-event- Event object is available as `$event`})
24243 <file name="index.html">
24244 <button ng-mousemove="count = count + 1" ng-init="count=0">
24245 Increment (when mouse moves)
24258 * Specify custom behavior on keydown event.
24262 * @param {expression} ngKeydown {@link guide/expression Expression} to evaluate upon
24263 * keydown. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
24267 <file name="index.html">
24268 <input ng-keydown="count = count + 1" ng-init="count=0">
24269 key down count: {{count}}
24280 * Specify custom behavior on keyup event.
24284 * @param {expression} ngKeyup {@link guide/expression Expression} to evaluate upon
24285 * keyup. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
24289 <file name="index.html">
24290 <p>Typing in the input box below updates the key count</p>
24291 <input ng-keyup="count = count + 1" ng-init="count=0"> key up count: {{count}}
24293 <p>Typing in the input box below updates the keycode</p>
24294 <input ng-keyup="event=$event">
24295 <p>event keyCode: {{ event.keyCode }}</p>
24296 <p>event altKey: {{ event.altKey }}</p>
24307 * Specify custom behavior on keypress event.
24310 * @param {expression} ngKeypress {@link guide/expression Expression} to evaluate upon
24311 * keypress. ({@link guide/expression#-event- Event object is available as `$event`}
24312 * and can be interrogated for keyCode, altKey, etc.)
24316 <file name="index.html">
24317 <input ng-keypress="count = count + 1" ng-init="count=0">
24318 key press count: {{count}}
24329 * Enables binding angular expressions to onsubmit events.
24331 * Additionally it prevents the default action (which for form means sending the request to the
24332 * server and reloading the current page), but only if the form does not contain `action`,
24333 * `data-action`, or `x-action` attributes.
24335 * <div class="alert alert-warning">
24336 * **Warning:** Be careful not to cause "double-submission" by using both the `ngClick` and
24337 * `ngSubmit` handlers together. See the
24338 * {@link form#submitting-a-form-and-preventing-the-default-action `form` directive documentation}
24339 * for a detailed discussion of when `ngSubmit` may be triggered.
24344 * @param {expression} ngSubmit {@link guide/expression Expression} to eval.
24345 * ({@link guide/expression#-event- Event object is available as `$event`})
24348 <example module="submitExample">
24349 <file name="index.html">
24351 angular.module('submitExample', [])
24352 .controller('ExampleController', ['$scope', function($scope) {
24354 $scope.text = 'hello';
24355 $scope.submit = function() {
24357 $scope.list.push(this.text);
24363 <form ng-submit="submit()" ng-controller="ExampleController">
24364 Enter text and hit enter:
24365 <input type="text" ng-model="text" name="text" />
24366 <input type="submit" id="submit" value="Submit" />
24367 <pre>list={{list}}</pre>
24370 <file name="protractor.js" type="protractor">
24371 it('should check ng-submit', function() {
24372 expect(element(by.binding('list')).getText()).toBe('list=[]');
24373 element(by.css('#submit')).click();
24374 expect(element(by.binding('list')).getText()).toContain('hello');
24375 expect(element(by.model('text')).getAttribute('value')).toBe('');
24377 it('should ignore empty strings', function() {
24378 expect(element(by.binding('list')).getText()).toBe('list=[]');
24379 element(by.css('#submit')).click();
24380 element(by.css('#submit')).click();
24381 expect(element(by.binding('list')).getText()).toContain('hello');
24392 * Specify custom behavior on focus event.
24394 * Note: As the `focus` event is executed synchronously when calling `input.focus()`
24395 * AngularJS executes the expression using `scope.$evalAsync` if the event is fired
24396 * during an `$apply` to ensure a consistent state.
24398 * @element window, input, select, textarea, a
24400 * @param {expression} ngFocus {@link guide/expression Expression} to evaluate upon
24401 * focus. ({@link guide/expression#-event- Event object is available as `$event`})
24404 * See {@link ng.directive:ngClick ngClick}
24412 * Specify custom behavior on blur event.
24414 * A [blur event](https://developer.mozilla.org/en-US/docs/Web/Events/blur) fires when
24415 * an element has lost focus.
24417 * Note: As the `blur` event is executed synchronously also during DOM manipulations
24418 * (e.g. removing a focussed input),
24419 * AngularJS executes the expression using `scope.$evalAsync` if the event is fired
24420 * during an `$apply` to ensure a consistent state.
24422 * @element window, input, select, textarea, a
24424 * @param {expression} ngBlur {@link guide/expression Expression} to evaluate upon
24425 * blur. ({@link guide/expression#-event- Event object is available as `$event`})
24428 * See {@link ng.directive:ngClick ngClick}
24436 * Specify custom behavior on copy event.
24438 * @element window, input, select, textarea, a
24440 * @param {expression} ngCopy {@link guide/expression Expression} to evaluate upon
24441 * copy. ({@link guide/expression#-event- Event object is available as `$event`})
24445 <file name="index.html">
24446 <input ng-copy="copied=true" ng-init="copied=false; value='copy me'" ng-model="value">
24457 * Specify custom behavior on cut event.
24459 * @element window, input, select, textarea, a
24461 * @param {expression} ngCut {@link guide/expression Expression} to evaluate upon
24462 * cut. ({@link guide/expression#-event- Event object is available as `$event`})
24466 <file name="index.html">
24467 <input ng-cut="cut=true" ng-init="cut=false; value='cut me'" ng-model="value">
24478 * Specify custom behavior on paste event.
24480 * @element window, input, select, textarea, a
24482 * @param {expression} ngPaste {@link guide/expression Expression} to evaluate upon
24483 * paste. ({@link guide/expression#-event- Event object is available as `$event`})
24487 <file name="index.html">
24488 <input ng-paste="paste=true" ng-init="paste=false" placeholder='paste here'>
24501 * The `ngIf` directive removes or recreates a portion of the DOM tree based on an
24502 * {expression}. If the expression assigned to `ngIf` evaluates to a false
24503 * value then the element is removed from the DOM, otherwise a clone of the
24504 * element is reinserted into the DOM.
24506 * `ngIf` differs from `ngShow` and `ngHide` in that `ngIf` completely removes and recreates the
24507 * element in the DOM rather than changing its visibility via the `display` css property. A common
24508 * case when this difference is significant is when using css selectors that rely on an element's
24509 * position within the DOM, such as the `:first-child` or `:last-child` pseudo-classes.
24511 * Note that when an element is removed using `ngIf` its scope is destroyed and a new scope
24512 * is created when the element is restored. The scope created within `ngIf` inherits from
24513 * its parent scope using
24514 * [prototypal inheritance](https://github.com/angular/angular.js/wiki/Understanding-Scopes#javascript-prototypal-inheritance).
24515 * An important implication of this is if `ngModel` is used within `ngIf` to bind to
24516 * a javascript primitive defined in the parent scope. In this case any modifications made to the
24517 * variable within the child scope will override (hide) the value in the parent scope.
24519 * Also, `ngIf` recreates elements using their compiled state. An example of this behavior
24520 * is if an element's class attribute is directly modified after it's compiled, using something like
24521 * jQuery's `.addClass()` method, and the element is later removed. When `ngIf` recreates the element
24522 * the added class will be lost because the original compiled state is used to regenerate the element.
24524 * Additionally, you can provide animations via the `ngAnimate` module to animate the `enter`
24525 * and `leave` effects.
24528 * enter - happens just after the `ngIf` contents change and a new DOM element is created and injected into the `ngIf` container
24529 * leave - happens just before the `ngIf` contents are removed from the DOM
24534 * @param {expression} ngIf If the {@link guide/expression expression} is falsy then
24535 * the element is removed from the DOM tree. If it is truthy a copy of the compiled
24536 * element is added to the DOM tree.
24539 <example module="ngAnimate" deps="angular-animate.js" animations="true">
24540 <file name="index.html">
24541 <label>Click me: <input type="checkbox" ng-model="checked" ng-init="checked=true" /></label><br/>
24543 <span ng-if="checked" class="animate-if">
24544 This is removed when the checkbox is unchecked.
24547 <file name="animations.css">
24550 border:1px solid black;
24554 .animate-if.ng-enter, .animate-if.ng-leave {
24555 transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
24558 .animate-if.ng-enter,
24559 .animate-if.ng-leave.ng-leave-active {
24563 .animate-if.ng-leave,
24564 .animate-if.ng-enter.ng-enter-active {
24570 var ngIfDirective = ['$animate', function($animate) {
24572 multiElement: true,
24573 transclude: 'element',
24578 link: function($scope, $element, $attr, ctrl, $transclude) {
24579 var block, childScope, previousElements;
24580 $scope.$watch($attr.ngIf, function ngIfWatchAction(value) {
24584 $transclude(function(clone, newScope) {
24585 childScope = newScope;
24586 clone[clone.length++] = document.createComment(' end ngIf: ' + $attr.ngIf + ' ');
24587 // Note: We only need the first/last node of the cloned nodes.
24588 // However, we need to keep the reference to the jqlite wrapper as it might be changed later
24589 // by a directive with templateUrl when its template arrives.
24593 $animate.enter(clone, $element.parent(), $element);
24597 if (previousElements) {
24598 previousElements.remove();
24599 previousElements = null;
24602 childScope.$destroy();
24606 previousElements = getBlockNodes(block.clone);
24607 $animate.leave(previousElements).then(function() {
24608 previousElements = null;
24624 * Fetches, compiles and includes an external HTML fragment.
24626 * By default, the template URL is restricted to the same domain and protocol as the
24627 * application document. This is done by calling {@link $sce#getTrustedResourceUrl
24628 * $sce.getTrustedResourceUrl} on it. To load templates from other domains or protocols
24629 * you may either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist them} or
24630 * {@link $sce#trustAsResourceUrl wrap them} as trusted values. Refer to Angular's {@link
24631 * ng.$sce Strict Contextual Escaping}.
24633 * In addition, the browser's
24634 * [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest)
24635 * and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/)
24636 * policy may further restrict whether the template is successfully loaded.
24637 * For example, `ngInclude` won't work for cross-domain requests on all browsers and for `file://`
24638 * access on some browsers.
24641 * enter - animation is used to bring new content into the browser.
24642 * leave - animation is used to animate existing content away.
24644 * The enter and leave animation occur concurrently.
24649 * @param {string} ngInclude|src angular expression evaluating to URL. If the source is a string constant,
24650 * make sure you wrap it in **single** quotes, e.g. `src="'myPartialTemplate.html'"`.
24651 * @param {string=} onload Expression to evaluate when a new partial is loaded.
24652 * <div class="alert alert-warning">
24653 * **Note:** When using onload on SVG elements in IE11, the browser will try to call
24654 * a function with the name on the window element, which will usually throw a
24655 * "function is undefined" error. To fix this, you can instead use `data-onload` or a
24656 * different form that {@link guide/directive#normalization matches} `onload`.
24659 * @param {string=} autoscroll Whether `ngInclude` should call {@link ng.$anchorScroll
24660 * $anchorScroll} to scroll the viewport after the content is loaded.
24662 * - If the attribute is not set, disable scrolling.
24663 * - If the attribute is set without value, enable scrolling.
24664 * - Otherwise enable scrolling only if the expression evaluates to truthy value.
24667 <example module="includeExample" deps="angular-animate.js" animations="true">
24668 <file name="index.html">
24669 <div ng-controller="ExampleController">
24670 <select ng-model="template" ng-options="t.name for t in templates">
24671 <option value="">(blank)</option>
24673 url of the template: <code>{{template.url}}</code>
24675 <div class="slide-animate-container">
24676 <div class="slide-animate" ng-include="template.url"></div>
24680 <file name="script.js">
24681 angular.module('includeExample', ['ngAnimate'])
24682 .controller('ExampleController', ['$scope', function($scope) {
24684 [ { name: 'template1.html', url: 'template1.html'},
24685 { name: 'template2.html', url: 'template2.html'} ];
24686 $scope.template = $scope.templates[0];
24689 <file name="template1.html">
24690 Content of template1.html
24692 <file name="template2.html">
24693 Content of template2.html
24695 <file name="animations.css">
24696 .slide-animate-container {
24699 border:1px solid black;
24708 .slide-animate.ng-enter, .slide-animate.ng-leave {
24709 transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
24720 .slide-animate.ng-enter {
24723 .slide-animate.ng-enter.ng-enter-active {
24727 .slide-animate.ng-leave {
24730 .slide-animate.ng-leave.ng-leave-active {
24734 <file name="protractor.js" type="protractor">
24735 var templateSelect = element(by.model('template'));
24736 var includeElem = element(by.css('[ng-include]'));
24738 it('should load template1.html', function() {
24739 expect(includeElem.getText()).toMatch(/Content of template1.html/);
24742 it('should load template2.html', function() {
24743 if (browser.params.browser == 'firefox') {
24744 // Firefox can't handle using selects
24745 // See https://github.com/angular/protractor/issues/480
24748 templateSelect.click();
24749 templateSelect.all(by.css('option')).get(2).click();
24750 expect(includeElem.getText()).toMatch(/Content of template2.html/);
24753 it('should change to blank', function() {
24754 if (browser.params.browser == 'firefox') {
24755 // Firefox can't handle using selects
24758 templateSelect.click();
24759 templateSelect.all(by.css('option')).get(0).click();
24760 expect(includeElem.isPresent()).toBe(false);
24769 * @name ngInclude#$includeContentRequested
24770 * @eventType emit on the scope ngInclude was declared in
24772 * Emitted every time the ngInclude content is requested.
24774 * @param {Object} angularEvent Synthetic event object.
24775 * @param {String} src URL of content to load.
24781 * @name ngInclude#$includeContentLoaded
24782 * @eventType emit on the current ngInclude scope
24784 * Emitted every time the ngInclude content is reloaded.
24786 * @param {Object} angularEvent Synthetic event object.
24787 * @param {String} src URL of content to load.
24793 * @name ngInclude#$includeContentError
24794 * @eventType emit on the scope ngInclude was declared in
24796 * Emitted when a template HTTP request yields an erroneous response (status < 200 || status > 299)
24798 * @param {Object} angularEvent Synthetic event object.
24799 * @param {String} src URL of content to load.
24801 var ngIncludeDirective = ['$templateRequest', '$anchorScroll', '$animate',
24802 function($templateRequest, $anchorScroll, $animate) {
24807 transclude: 'element',
24808 controller: angular.noop,
24809 compile: function(element, attr) {
24810 var srcExp = attr.ngInclude || attr.src,
24811 onloadExp = attr.onload || '',
24812 autoScrollExp = attr.autoscroll;
24814 return function(scope, $element, $attr, ctrl, $transclude) {
24815 var changeCounter = 0,
24820 var cleanupLastIncludeContent = function() {
24821 if (previousElement) {
24822 previousElement.remove();
24823 previousElement = null;
24825 if (currentScope) {
24826 currentScope.$destroy();
24827 currentScope = null;
24829 if (currentElement) {
24830 $animate.leave(currentElement).then(function() {
24831 previousElement = null;
24833 previousElement = currentElement;
24834 currentElement = null;
24838 scope.$watch(srcExp, function ngIncludeWatchAction(src) {
24839 var afterAnimation = function() {
24840 if (isDefined(autoScrollExp) && (!autoScrollExp || scope.$eval(autoScrollExp))) {
24844 var thisChangeId = ++changeCounter;
24847 //set the 2nd param to true to ignore the template request error so that the inner
24848 //contents and scope can be cleaned up.
24849 $templateRequest(src, true).then(function(response) {
24850 if (scope.$$destroyed) return;
24852 if (thisChangeId !== changeCounter) return;
24853 var newScope = scope.$new();
24854 ctrl.template = response;
24856 // Note: This will also link all children of ng-include that were contained in the original
24857 // html. If that content contains controllers, ... they could pollute/change the scope.
24858 // However, using ng-include on an element with additional content does not make sense...
24859 // Note: We can't remove them in the cloneAttchFn of $transclude as that
24860 // function is called before linking the content, which would apply child
24861 // directives to non existing elements.
24862 var clone = $transclude(newScope, function(clone) {
24863 cleanupLastIncludeContent();
24864 $animate.enter(clone, null, $element).then(afterAnimation);
24867 currentScope = newScope;
24868 currentElement = clone;
24870 currentScope.$emit('$includeContentLoaded', src);
24871 scope.$eval(onloadExp);
24873 if (scope.$$destroyed) return;
24875 if (thisChangeId === changeCounter) {
24876 cleanupLastIncludeContent();
24877 scope.$emit('$includeContentError', src);
24880 scope.$emit('$includeContentRequested', src);
24882 cleanupLastIncludeContent();
24883 ctrl.template = null;
24891 // This directive is called during the $transclude call of the first `ngInclude` directive.
24892 // It will replace and compile the content of the element with the loaded template.
24893 // We need this directive so that the element content is already filled when
24894 // the link function of another directive on the same element as ngInclude
24896 var ngIncludeFillContentDirective = ['$compile',
24897 function($compile) {
24901 require: 'ngInclude',
24902 link: function(scope, $element, $attr, ctrl) {
24903 if (/SVG/.test($element[0].toString())) {
24904 // WebKit: https://bugs.webkit.org/show_bug.cgi?id=135698 --- SVG elements do not
24905 // support innerHTML, so detect this here and try to generate the contents
24908 $compile(jqLiteBuildFragment(ctrl.template, document).childNodes)(scope,
24909 function namespaceAdaptedClone(clone) {
24910 $element.append(clone);
24911 }, {futureParentElement: $element});
24915 $element.html(ctrl.template);
24916 $compile($element.contents())(scope);
24927 * The `ngInit` directive allows you to evaluate an expression in the
24930 * <div class="alert alert-danger">
24931 * This directive can be abused to add unnecessary amounts of logic into your templates.
24932 * There are only a few appropriate uses of `ngInit`, such as for aliasing special properties of
24933 * {@link ng.directive:ngRepeat `ngRepeat`}, as seen in the demo below; and for injecting data via
24934 * server side scripting. Besides these few cases, you should use {@link guide/controller controllers}
24935 * rather than `ngInit` to initialize values on a scope.
24938 * <div class="alert alert-warning">
24939 * **Note**: If you have assignment in `ngInit` along with a {@link ng.$filter `filter`}, make
24940 * sure you have parentheses to ensure correct operator precedence:
24941 * <pre class="prettyprint">
24942 * `<div ng-init="test1 = ($index | toString)"></div>`
24949 * @param {expression} ngInit {@link guide/expression Expression} to eval.
24952 <example module="initExample">
24953 <file name="index.html">
24955 angular.module('initExample', [])
24956 .controller('ExampleController', ['$scope', function($scope) {
24957 $scope.list = [['a', 'b'], ['c', 'd']];
24960 <div ng-controller="ExampleController">
24961 <div ng-repeat="innerList in list" ng-init="outerIndex = $index">
24962 <div ng-repeat="value in innerList" ng-init="innerIndex = $index">
24963 <span class="example-init">list[ {{outerIndex}} ][ {{innerIndex}} ] = {{value}};</span>
24968 <file name="protractor.js" type="protractor">
24969 it('should alias index positions', function() {
24970 var elements = element.all(by.css('.example-init'));
24971 expect(elements.get(0).getText()).toBe('list[ 0 ][ 0 ] = a;');
24972 expect(elements.get(1).getText()).toBe('list[ 0 ][ 1 ] = b;');
24973 expect(elements.get(2).getText()).toBe('list[ 1 ][ 0 ] = c;');
24974 expect(elements.get(3).getText()).toBe('list[ 1 ][ 1 ] = d;');
24979 var ngInitDirective = ngDirective({
24981 compile: function() {
24983 pre: function(scope, element, attrs) {
24984 scope.$eval(attrs.ngInit);
24995 * Text input that converts between a delimited string and an array of strings. The default
24996 * delimiter is a comma followed by a space - equivalent to `ng-list=", "`. You can specify a custom
24997 * delimiter as the value of the `ngList` attribute - for example, `ng-list=" | "`.
24999 * The behaviour of the directive is affected by the use of the `ngTrim` attribute.
25000 * * If `ngTrim` is set to `"false"` then whitespace around both the separator and each
25001 * list item is respected. This implies that the user of the directive is responsible for
25002 * dealing with whitespace but also allows you to use whitespace as a delimiter, such as a
25003 * tab or newline character.
25004 * * Otherwise whitespace around the delimiter is ignored when splitting (although it is respected
25005 * when joining the list items back together) and whitespace around each list item is stripped
25006 * before it is added to the model.
25008 * ### Example with Validation
25010 * <example name="ngList-directive" module="listExample">
25011 * <file name="app.js">
25012 * angular.module('listExample', [])
25013 * .controller('ExampleController', ['$scope', function($scope) {
25014 * $scope.names = ['morpheus', 'neo', 'trinity'];
25017 * <file name="index.html">
25018 * <form name="myForm" ng-controller="ExampleController">
25019 * <label>List: <input name="namesInput" ng-model="names" ng-list required></label>
25020 * <span role="alert">
25021 * <span class="error" ng-show="myForm.namesInput.$error.required">
25025 * <tt>names = {{names}}</tt><br/>
25026 * <tt>myForm.namesInput.$valid = {{myForm.namesInput.$valid}}</tt><br/>
25027 * <tt>myForm.namesInput.$error = {{myForm.namesInput.$error}}</tt><br/>
25028 * <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
25029 * <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
25032 * <file name="protractor.js" type="protractor">
25033 * var listInput = element(by.model('names'));
25034 * var names = element(by.exactBinding('names'));
25035 * var valid = element(by.binding('myForm.namesInput.$valid'));
25036 * var error = element(by.css('span.error'));
25038 * it('should initialize to model', function() {
25039 * expect(names.getText()).toContain('["morpheus","neo","trinity"]');
25040 * expect(valid.getText()).toContain('true');
25041 * expect(error.getCssValue('display')).toBe('none');
25044 * it('should be invalid if empty', function() {
25045 * listInput.clear();
25046 * listInput.sendKeys('');
25048 * expect(names.getText()).toContain('');
25049 * expect(valid.getText()).toContain('false');
25050 * expect(error.getCssValue('display')).not.toBe('none');
25055 * ### Example - splitting on newline
25056 * <example name="ngList-directive-newlines">
25057 * <file name="index.html">
25058 * <textarea ng-model="list" ng-list=" " ng-trim="false"></textarea>
25059 * <pre>{{ list | json }}</pre>
25061 * <file name="protractor.js" type="protractor">
25062 * it("should split the text by newlines", function() {
25063 * var listInput = element(by.model('list'));
25064 * var output = element(by.binding('list | json'));
25065 * listInput.sendKeys('abc\ndef\nghi');
25066 * expect(output.getText()).toContain('[\n "abc",\n "def",\n "ghi"\n]');
25072 * @param {string=} ngList optional delimiter that should be used to split the value.
25074 var ngListDirective = function() {
25078 require: 'ngModel',
25079 link: function(scope, element, attr, ctrl) {
25080 // We want to control whitespace trimming so we use this convoluted approach
25081 // to access the ngList attribute, which doesn't pre-trim the attribute
25082 var ngList = element.attr(attr.$attr.ngList) || ', ';
25083 var trimValues = attr.ngTrim !== 'false';
25084 var separator = trimValues ? trim(ngList) : ngList;
25086 var parse = function(viewValue) {
25087 // If the viewValue is invalid (say required but empty) it will be `undefined`
25088 if (isUndefined(viewValue)) return;
25093 forEach(viewValue.split(separator), function(value) {
25094 if (value) list.push(trimValues ? trim(value) : value);
25101 ctrl.$parsers.push(parse);
25102 ctrl.$formatters.push(function(value) {
25103 if (isArray(value)) {
25104 return value.join(ngList);
25110 // Override the standard $isEmpty because an empty array means the input is empty.
25111 ctrl.$isEmpty = function(value) {
25112 return !value || !value.length;
25118 /* global VALID_CLASS: true,
25119 INVALID_CLASS: true,
25120 PRISTINE_CLASS: true,
25122 UNTOUCHED_CLASS: true,
25123 TOUCHED_CLASS: true,
25126 var VALID_CLASS = 'ng-valid',
25127 INVALID_CLASS = 'ng-invalid',
25128 PRISTINE_CLASS = 'ng-pristine',
25129 DIRTY_CLASS = 'ng-dirty',
25130 UNTOUCHED_CLASS = 'ng-untouched',
25131 TOUCHED_CLASS = 'ng-touched',
25132 PENDING_CLASS = 'ng-pending';
25134 var ngModelMinErr = minErr('ngModel');
25138 * @name ngModel.NgModelController
25140 * @property {*} $viewValue The actual value from the control's view. For `input` elements, this is a
25141 * String. See {@link ngModel.NgModelController#$setViewValue} for information about when the $viewValue
25143 * @property {*} $modelValue The value in the model that the control is bound to.
25144 * @property {Array.<Function>} $parsers Array of functions to execute, as a pipeline, whenever
25145 the control reads value from the DOM. The functions are called in array order, each passing
25146 its return value through to the next. The last return value is forwarded to the
25147 {@link ngModel.NgModelController#$validators `$validators`} collection.
25149 Parsers are used to sanitize / convert the {@link ngModel.NgModelController#$viewValue
25152 Returning `undefined` from a parser means a parse error occurred. In that case,
25153 no {@link ngModel.NgModelController#$validators `$validators`} will run and the `ngModel`
25154 will be set to `undefined` unless {@link ngModelOptions `ngModelOptions.allowInvalid`}
25155 is set to `true`. The parse error is stored in `ngModel.$error.parse`.
25158 * @property {Array.<Function>} $formatters Array of functions to execute, as a pipeline, whenever
25159 the model value changes. The functions are called in reverse array order, each passing the value through to the
25160 next. The last return value is used as the actual DOM value.
25161 Used to format / convert values for display in the control.
25163 * function formatter(value) {
25165 * return value.toUpperCase();
25168 * ngModel.$formatters.push(formatter);
25171 * @property {Object.<string, function>} $validators A collection of validators that are applied
25172 * whenever the model value changes. The key value within the object refers to the name of the
25173 * validator while the function refers to the validation operation. The validation operation is
25174 * provided with the model value as an argument and must return a true or false value depending
25175 * on the response of that validation.
25178 * ngModel.$validators.validCharacters = function(modelValue, viewValue) {
25179 * var value = modelValue || viewValue;
25180 * return /[0-9]+/.test(value) &&
25181 * /[a-z]+/.test(value) &&
25182 * /[A-Z]+/.test(value) &&
25183 * /\W+/.test(value);
25187 * @property {Object.<string, function>} $asyncValidators A collection of validations that are expected to
25188 * perform an asynchronous validation (e.g. a HTTP request). The validation function that is provided
25189 * is expected to return a promise when it is run during the model validation process. Once the promise
25190 * is delivered then the validation status will be set to true when fulfilled and false when rejected.
25191 * When the asynchronous validators are triggered, each of the validators will run in parallel and the model
25192 * value will only be updated once all validators have been fulfilled. As long as an asynchronous validator
25193 * is unfulfilled, its key will be added to the controllers `$pending` property. Also, all asynchronous validators
25194 * will only run once all synchronous validators have passed.
25196 * Please note that if $http is used then it is important that the server returns a success HTTP response code
25197 * in order to fulfill the validation and a status level of `4xx` in order to reject the validation.
25200 * ngModel.$asyncValidators.uniqueUsername = function(modelValue, viewValue) {
25201 * var value = modelValue || viewValue;
25203 * // Lookup user by username
25204 * return $http.get('/api/users/' + value).
25205 * then(function resolved() {
25206 * //username exists, this means validation fails
25207 * return $q.reject('exists');
25208 * }, function rejected() {
25209 * //username does not exist, therefore this validation passes
25215 * @property {Array.<Function>} $viewChangeListeners Array of functions to execute whenever the
25216 * view value has changed. It is called with no arguments, and its return value is ignored.
25217 * This can be used in place of additional $watches against the model value.
25219 * @property {Object} $error An object hash with all failing validator ids as keys.
25220 * @property {Object} $pending An object hash with all pending validator ids as keys.
25222 * @property {boolean} $untouched True if control has not lost focus yet.
25223 * @property {boolean} $touched True if control has lost focus.
25224 * @property {boolean} $pristine True if user has not interacted with the control yet.
25225 * @property {boolean} $dirty True if user has already interacted with the control.
25226 * @property {boolean} $valid True if there is no error.
25227 * @property {boolean} $invalid True if at least one error on the control.
25228 * @property {string} $name The name attribute of the control.
25232 * `NgModelController` provides API for the {@link ngModel `ngModel`} directive.
25233 * The controller contains services for data-binding, validation, CSS updates, and value formatting
25234 * and parsing. It purposefully does not contain any logic which deals with DOM rendering or
25235 * listening to DOM events.
25236 * Such DOM related logic should be provided by other directives which make use of
25237 * `NgModelController` for data-binding to control elements.
25238 * Angular provides this DOM logic for most {@link input `input`} elements.
25239 * At the end of this page you can find a {@link ngModel.NgModelController#custom-control-example
25240 * custom control example} that uses `ngModelController` to bind to `contenteditable` elements.
25243 * ### Custom Control Example
25244 * This example shows how to use `NgModelController` with a custom control to achieve
25245 * data-binding. Notice how different directives (`contenteditable`, `ng-model`, and `required`)
25246 * collaborate together to achieve the desired result.
25248 * `contenteditable` is an HTML5 attribute, which tells the browser to let the element
25249 * contents be edited in place by the user.
25251 * We are using the {@link ng.service:$sce $sce} service here and include the {@link ngSanitize $sanitize}
25252 * module to automatically remove "bad" content like inline event listener (e.g. `<span onclick="...">`).
25253 * However, as we are using `$sce` the model can still decide to provide unsafe content if it marks
25254 * that content using the `$sce` service.
25256 * <example name="NgModelController" module="customControl" deps="angular-sanitize.js">
25257 <file name="style.css">
25258 [contenteditable] {
25259 border: 1px solid black;
25260 background-color: white;
25265 border: 1px solid red;
25269 <file name="script.js">
25270 angular.module('customControl', ['ngSanitize']).
25271 directive('contenteditable', ['$sce', function($sce) {
25273 restrict: 'A', // only activate on element attribute
25274 require: '?ngModel', // get a hold of NgModelController
25275 link: function(scope, element, attrs, ngModel) {
25276 if (!ngModel) return; // do nothing if no ng-model
25278 // Specify how UI should be updated
25279 ngModel.$render = function() {
25280 element.html($sce.getTrustedHtml(ngModel.$viewValue || ''));
25283 // Listen for change events to enable binding
25284 element.on('blur keyup change', function() {
25285 scope.$evalAsync(read);
25287 read(); // initialize
25289 // Write data to the model
25291 var html = element.html();
25292 // When we clear the content editable the browser leaves a <br> behind
25293 // If strip-br attribute is provided then we strip this out
25294 if ( attrs.stripBr && html == '<br>' ) {
25297 ngModel.$setViewValue(html);
25303 <file name="index.html">
25304 <form name="myForm">
25305 <div contenteditable
25306 name="myWidget" ng-model="userContent"
25308 required>Change me!</div>
25309 <span ng-show="myForm.myWidget.$error.required">Required!</span>
25311 <textarea ng-model="userContent" aria-label="Dynamic textarea"></textarea>
25314 <file name="protractor.js" type="protractor">
25315 it('should data-bind and become invalid', function() {
25316 if (browser.params.browser == 'safari' || browser.params.browser == 'firefox') {
25317 // SafariDriver can't handle contenteditable
25318 // and Firefox driver can't clear contenteditables very well
25321 var contentEditable = element(by.css('[contenteditable]'));
25322 var content = 'Change me!';
25324 expect(contentEditable.getText()).toEqual(content);
25326 contentEditable.clear();
25327 contentEditable.sendKeys(protractor.Key.BACK_SPACE);
25328 expect(contentEditable.getText()).toEqual('');
25329 expect(contentEditable.getAttribute('class')).toMatch(/ng-invalid-required/);
25336 var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$parse', '$animate', '$timeout', '$rootScope', '$q', '$interpolate',
25337 function($scope, $exceptionHandler, $attr, $element, $parse, $animate, $timeout, $rootScope, $q, $interpolate) {
25338 this.$viewValue = Number.NaN;
25339 this.$modelValue = Number.NaN;
25340 this.$$rawModelValue = undefined; // stores the parsed modelValue / model set from scope regardless of validity.
25341 this.$validators = {};
25342 this.$asyncValidators = {};
25343 this.$parsers = [];
25344 this.$formatters = [];
25345 this.$viewChangeListeners = [];
25346 this.$untouched = true;
25347 this.$touched = false;
25348 this.$pristine = true;
25349 this.$dirty = false;
25350 this.$valid = true;
25351 this.$invalid = false;
25352 this.$error = {}; // keep invalid keys here
25353 this.$$success = {}; // keep valid keys here
25354 this.$pending = undefined; // keep pending keys here
25355 this.$name = $interpolate($attr.name || '', false)($scope);
25356 this.$$parentForm = nullFormCtrl;
25358 var parsedNgModel = $parse($attr.ngModel),
25359 parsedNgModelAssign = parsedNgModel.assign,
25360 ngModelGet = parsedNgModel,
25361 ngModelSet = parsedNgModelAssign,
25362 pendingDebounce = null,
25366 this.$$setOptions = function(options) {
25367 ctrl.$options = options;
25368 if (options && options.getterSetter) {
25369 var invokeModelGetter = $parse($attr.ngModel + '()'),
25370 invokeModelSetter = $parse($attr.ngModel + '($$$p)');
25372 ngModelGet = function($scope) {
25373 var modelValue = parsedNgModel($scope);
25374 if (isFunction(modelValue)) {
25375 modelValue = invokeModelGetter($scope);
25379 ngModelSet = function($scope, newValue) {
25380 if (isFunction(parsedNgModel($scope))) {
25381 invokeModelSetter($scope, {$$$p: ctrl.$modelValue});
25383 parsedNgModelAssign($scope, ctrl.$modelValue);
25386 } else if (!parsedNgModel.assign) {
25387 throw ngModelMinErr('nonassign', "Expression '{0}' is non-assignable. Element: {1}",
25388 $attr.ngModel, startingTag($element));
25394 * @name ngModel.NgModelController#$render
25397 * Called when the view needs to be updated. It is expected that the user of the ng-model
25398 * directive will implement this method.
25400 * The `$render()` method is invoked in the following situations:
25402 * * `$rollbackViewValue()` is called. If we are rolling back the view value to the last
25403 * committed value then `$render()` is called to update the input control.
25404 * * The value referenced by `ng-model` is changed programmatically and both the `$modelValue` and
25405 * the `$viewValue` are different from last time.
25407 * Since `ng-model` does not do a deep watch, `$render()` is only invoked if the values of
25408 * `$modelValue` and `$viewValue` are actually different from their previous value. If `$modelValue`
25409 * or `$viewValue` are objects (rather than a string or number) then `$render()` will not be
25410 * invoked if you only change a property on the objects.
25412 this.$render = noop;
25416 * @name ngModel.NgModelController#$isEmpty
25419 * This is called when we need to determine if the value of an input is empty.
25421 * For instance, the required directive does this to work out if the input has data or not.
25423 * The default `$isEmpty` function checks whether the value is `undefined`, `''`, `null` or `NaN`.
25425 * You can override this for input directives whose concept of being empty is different from the
25426 * default. The `checkboxInputType` directive does this because in its case a value of `false`
25429 * @param {*} value The value of the input to check for emptiness.
25430 * @returns {boolean} True if `value` is "empty".
25432 this.$isEmpty = function(value) {
25433 return isUndefined(value) || value === '' || value === null || value !== value;
25436 var currentValidationRunId = 0;
25440 * @name ngModel.NgModelController#$setValidity
25443 * Change the validity state, and notify the form.
25445 * This method can be called within $parsers/$formatters or a custom validation implementation.
25446 * However, in most cases it should be sufficient to use the `ngModel.$validators` and
25447 * `ngModel.$asyncValidators` collections which will call `$setValidity` automatically.
25449 * @param {string} validationErrorKey Name of the validator. The `validationErrorKey` will be assigned
25450 * to either `$error[validationErrorKey]` or `$pending[validationErrorKey]`
25451 * (for unfulfilled `$asyncValidators`), so that it is available for data-binding.
25452 * The `validationErrorKey` should be in camelCase and will get converted into dash-case
25453 * for class name. Example: `myError` will result in `ng-valid-my-error` and `ng-invalid-my-error`
25454 * class and can be bound to as `{{someForm.someControl.$error.myError}}` .
25455 * @param {boolean} isValid Whether the current state is valid (true), invalid (false), pending (undefined),
25456 * or skipped (null). Pending is used for unfulfilled `$asyncValidators`.
25457 * Skipped is used by Angular when validators do not run because of parse errors and
25458 * when `$asyncValidators` do not run because any of the `$validators` failed.
25460 addSetValidityMethod({
25462 $element: $element,
25463 set: function(object, property) {
25464 object[property] = true;
25466 unset: function(object, property) {
25467 delete object[property];
25474 * @name ngModel.NgModelController#$setPristine
25477 * Sets the control to its pristine state.
25479 * This method can be called to remove the `ng-dirty` class and set the control to its pristine
25480 * state (`ng-pristine` class). A model is considered to be pristine when the control
25481 * has not been changed from when first compiled.
25483 this.$setPristine = function() {
25484 ctrl.$dirty = false;
25485 ctrl.$pristine = true;
25486 $animate.removeClass($element, DIRTY_CLASS);
25487 $animate.addClass($element, PRISTINE_CLASS);
25492 * @name ngModel.NgModelController#$setDirty
25495 * Sets the control to its dirty state.
25497 * This method can be called to remove the `ng-pristine` class and set the control to its dirty
25498 * state (`ng-dirty` class). A model is considered to be dirty when the control has been changed
25499 * from when first compiled.
25501 this.$setDirty = function() {
25502 ctrl.$dirty = true;
25503 ctrl.$pristine = false;
25504 $animate.removeClass($element, PRISTINE_CLASS);
25505 $animate.addClass($element, DIRTY_CLASS);
25506 ctrl.$$parentForm.$setDirty();
25511 * @name ngModel.NgModelController#$setUntouched
25514 * Sets the control to its untouched state.
25516 * This method can be called to remove the `ng-touched` class and set the control to its
25517 * untouched state (`ng-untouched` class). Upon compilation, a model is set as untouched
25518 * by default, however this function can be used to restore that state if the model has
25519 * already been touched by the user.
25521 this.$setUntouched = function() {
25522 ctrl.$touched = false;
25523 ctrl.$untouched = true;
25524 $animate.setClass($element, UNTOUCHED_CLASS, TOUCHED_CLASS);
25529 * @name ngModel.NgModelController#$setTouched
25532 * Sets the control to its touched state.
25534 * This method can be called to remove the `ng-untouched` class and set the control to its
25535 * touched state (`ng-touched` class). A model is considered to be touched when the user has
25536 * first focused the control element and then shifted focus away from the control (blur event).
25538 this.$setTouched = function() {
25539 ctrl.$touched = true;
25540 ctrl.$untouched = false;
25541 $animate.setClass($element, TOUCHED_CLASS, UNTOUCHED_CLASS);
25546 * @name ngModel.NgModelController#$rollbackViewValue
25549 * Cancel an update and reset the input element's value to prevent an update to the `$modelValue`,
25550 * which may be caused by a pending debounced event or because the input is waiting for a some
25553 * If you have an input that uses `ng-model-options` to set up debounced updates or updates that
25554 * depend on special events such as blur, you can have a situation where there is a period when
25555 * the `$viewValue` is out of sync with the ngModel's `$modelValue`.
25557 * In this case, you can use `$rollbackViewValue()` to manually cancel the debounced / future update
25558 * and reset the input to the last committed view value.
25560 * It is also possible that you run into difficulties if you try to update the ngModel's `$modelValue`
25561 * programmatically before these debounced/future events have resolved/occurred, because Angular's
25562 * dirty checking mechanism is not able to tell whether the model has actually changed or not.
25564 * The `$rollbackViewValue()` method should be called before programmatically changing the model of an
25565 * input which may have such events pending. This is important in order to make sure that the
25566 * input field will be updated with the new model value and any pending operations are cancelled.
25568 * <example name="ng-model-cancel-update" module="cancel-update-example">
25569 * <file name="app.js">
25570 * angular.module('cancel-update-example', [])
25572 * .controller('CancelUpdateController', ['$scope', function($scope) {
25573 * $scope.model = {};
25575 * $scope.setEmpty = function(e, value, rollback) {
25576 * if (e.keyCode == 27) {
25577 * e.preventDefault();
25579 * $scope.myForm[value].$rollbackViewValue();
25581 * $scope.model[value] = '';
25586 * <file name="index.html">
25587 * <div ng-controller="CancelUpdateController">
25588 * <p>Both of these inputs are only updated if they are blurred. Hitting escape should
25589 * empty them. Follow these steps and observe the difference:</p>
25591 * <li>Type something in the input. You will see that the model is not yet updated</li>
25592 * <li>Press the Escape key.
25594 * <li> In the first example, nothing happens, because the model is already '', and no
25595 * update is detected. If you blur the input, the model will be set to the current view.
25597 * <li> In the second example, the pending update is cancelled, and the input is set back
25598 * to the last committed view value (''). Blurring the input does nothing.
25604 * <form name="myForm" ng-model-options="{ updateOn: 'blur' }">
25606 * <p id="inputDescription1">Without $rollbackViewValue():</p>
25607 * <input name="value1" aria-describedby="inputDescription1" ng-model="model.value1"
25608 * ng-keydown="setEmpty($event, 'value1')">
25609 * value1: "{{ model.value1 }}"
25613 * <p id="inputDescription2">With $rollbackViewValue():</p>
25614 * <input name="value2" aria-describedby="inputDescription2" ng-model="model.value2"
25615 * ng-keydown="setEmpty($event, 'value2', true)">
25616 * value2: "{{ model.value2 }}"
25621 <file name="style.css">
25623 display: table-cell;
25626 padding-right: 30px;
25632 this.$rollbackViewValue = function() {
25633 $timeout.cancel(pendingDebounce);
25634 ctrl.$viewValue = ctrl.$$lastCommittedViewValue;
25640 * @name ngModel.NgModelController#$validate
25643 * Runs each of the registered validators (first synchronous validators and then
25644 * asynchronous validators).
25645 * If the validity changes to invalid, the model will be set to `undefined`,
25646 * unless {@link ngModelOptions `ngModelOptions.allowInvalid`} is `true`.
25647 * If the validity changes to valid, it will set the model to the last available valid
25648 * `$modelValue`, i.e. either the last parsed value or the last value set from the scope.
25650 this.$validate = function() {
25651 // ignore $validate before model is initialized
25652 if (isNumber(ctrl.$modelValue) && isNaN(ctrl.$modelValue)) {
25656 var viewValue = ctrl.$$lastCommittedViewValue;
25657 // Note: we use the $$rawModelValue as $modelValue might have been
25658 // set to undefined during a view -> model update that found validation
25659 // errors. We can't parse the view here, since that could change
25660 // the model although neither viewValue nor the model on the scope changed
25661 var modelValue = ctrl.$$rawModelValue;
25663 var prevValid = ctrl.$valid;
25664 var prevModelValue = ctrl.$modelValue;
25666 var allowInvalid = ctrl.$options && ctrl.$options.allowInvalid;
25668 ctrl.$$runValidators(modelValue, viewValue, function(allValid) {
25669 // If there was no change in validity, don't update the model
25670 // This prevents changing an invalid modelValue to undefined
25671 if (!allowInvalid && prevValid !== allValid) {
25672 // Note: Don't check ctrl.$valid here, as we could have
25673 // external validators (e.g. calculated on the server),
25674 // that just call $setValidity and need the model value
25675 // to calculate their validity.
25676 ctrl.$modelValue = allValid ? modelValue : undefined;
25678 if (ctrl.$modelValue !== prevModelValue) {
25679 ctrl.$$writeModelToScope();
25686 this.$$runValidators = function(modelValue, viewValue, doneCallback) {
25687 currentValidationRunId++;
25688 var localValidationRunId = currentValidationRunId;
25690 // check parser error
25691 if (!processParseErrors()) {
25692 validationDone(false);
25695 if (!processSyncValidators()) {
25696 validationDone(false);
25699 processAsyncValidators();
25701 function processParseErrors() {
25702 var errorKey = ctrl.$$parserName || 'parse';
25703 if (isUndefined(parserValid)) {
25704 setValidity(errorKey, null);
25706 if (!parserValid) {
25707 forEach(ctrl.$validators, function(v, name) {
25708 setValidity(name, null);
25710 forEach(ctrl.$asyncValidators, function(v, name) {
25711 setValidity(name, null);
25714 // Set the parse error last, to prevent unsetting it, should a $validators key == parserName
25715 setValidity(errorKey, parserValid);
25716 return parserValid;
25721 function processSyncValidators() {
25722 var syncValidatorsValid = true;
25723 forEach(ctrl.$validators, function(validator, name) {
25724 var result = validator(modelValue, viewValue);
25725 syncValidatorsValid = syncValidatorsValid && result;
25726 setValidity(name, result);
25728 if (!syncValidatorsValid) {
25729 forEach(ctrl.$asyncValidators, function(v, name) {
25730 setValidity(name, null);
25737 function processAsyncValidators() {
25738 var validatorPromises = [];
25739 var allValid = true;
25740 forEach(ctrl.$asyncValidators, function(validator, name) {
25741 var promise = validator(modelValue, viewValue);
25742 if (!isPromiseLike(promise)) {
25743 throw ngModelMinErr('nopromise',
25744 "Expected asynchronous validator to return a promise but got '{0}' instead.", promise);
25746 setValidity(name, undefined);
25747 validatorPromises.push(promise.then(function() {
25748 setValidity(name, true);
25749 }, function(error) {
25751 setValidity(name, false);
25754 if (!validatorPromises.length) {
25755 validationDone(true);
25757 $q.all(validatorPromises).then(function() {
25758 validationDone(allValid);
25763 function setValidity(name, isValid) {
25764 if (localValidationRunId === currentValidationRunId) {
25765 ctrl.$setValidity(name, isValid);
25769 function validationDone(allValid) {
25770 if (localValidationRunId === currentValidationRunId) {
25772 doneCallback(allValid);
25779 * @name ngModel.NgModelController#$commitViewValue
25782 * Commit a pending update to the `$modelValue`.
25784 * Updates may be pending by a debounced event or because the input is waiting for a some future
25785 * event defined in `ng-model-options`. this method is rarely needed as `NgModelController`
25786 * usually handles calling this in response to input events.
25788 this.$commitViewValue = function() {
25789 var viewValue = ctrl.$viewValue;
25791 $timeout.cancel(pendingDebounce);
25793 // If the view value has not changed then we should just exit, except in the case where there is
25794 // a native validator on the element. In this case the validation state may have changed even though
25795 // the viewValue has stayed empty.
25796 if (ctrl.$$lastCommittedViewValue === viewValue && (viewValue !== '' || !ctrl.$$hasNativeValidators)) {
25799 ctrl.$$lastCommittedViewValue = viewValue;
25802 if (ctrl.$pristine) {
25805 this.$$parseAndValidate();
25808 this.$$parseAndValidate = function() {
25809 var viewValue = ctrl.$$lastCommittedViewValue;
25810 var modelValue = viewValue;
25811 parserValid = isUndefined(modelValue) ? undefined : true;
25814 for (var i = 0; i < ctrl.$parsers.length; i++) {
25815 modelValue = ctrl.$parsers[i](modelValue);
25816 if (isUndefined(modelValue)) {
25817 parserValid = false;
25822 if (isNumber(ctrl.$modelValue) && isNaN(ctrl.$modelValue)) {
25823 // ctrl.$modelValue has not been touched yet...
25824 ctrl.$modelValue = ngModelGet($scope);
25826 var prevModelValue = ctrl.$modelValue;
25827 var allowInvalid = ctrl.$options && ctrl.$options.allowInvalid;
25828 ctrl.$$rawModelValue = modelValue;
25830 if (allowInvalid) {
25831 ctrl.$modelValue = modelValue;
25832 writeToModelIfNeeded();
25835 // Pass the $$lastCommittedViewValue here, because the cached viewValue might be out of date.
25836 // This can happen if e.g. $setViewValue is called from inside a parser
25837 ctrl.$$runValidators(modelValue, ctrl.$$lastCommittedViewValue, function(allValid) {
25838 if (!allowInvalid) {
25839 // Note: Don't check ctrl.$valid here, as we could have
25840 // external validators (e.g. calculated on the server),
25841 // that just call $setValidity and need the model value
25842 // to calculate their validity.
25843 ctrl.$modelValue = allValid ? modelValue : undefined;
25844 writeToModelIfNeeded();
25848 function writeToModelIfNeeded() {
25849 if (ctrl.$modelValue !== prevModelValue) {
25850 ctrl.$$writeModelToScope();
25855 this.$$writeModelToScope = function() {
25856 ngModelSet($scope, ctrl.$modelValue);
25857 forEach(ctrl.$viewChangeListeners, function(listener) {
25861 $exceptionHandler(e);
25868 * @name ngModel.NgModelController#$setViewValue
25871 * Update the view value.
25873 * This method should be called when a control wants to change the view value; typically,
25874 * this is done from within a DOM event handler. For example, the {@link ng.directive:input input}
25875 * directive calls it when the value of the input changes and {@link ng.directive:select select}
25876 * calls it when an option is selected.
25878 * When `$setViewValue` is called, the new `value` will be staged for committing through the `$parsers`
25879 * and `$validators` pipelines. If there are no special {@link ngModelOptions} specified then the staged
25880 * value sent directly for processing, finally to be applied to `$modelValue` and then the
25881 * **expression** specified in the `ng-model` attribute. Lastly, all the registered change listeners,
25882 * in the `$viewChangeListeners` list, are called.
25884 * In case the {@link ng.directive:ngModelOptions ngModelOptions} directive is used with `updateOn`
25885 * and the `default` trigger is not listed, all those actions will remain pending until one of the
25886 * `updateOn` events is triggered on the DOM element.
25887 * All these actions will be debounced if the {@link ng.directive:ngModelOptions ngModelOptions}
25888 * directive is used with a custom debounce for this particular event.
25889 * Note that a `$digest` is only triggered once the `updateOn` events are fired, or if `debounce`
25890 * is specified, once the timer runs out.
25892 * When used with standard inputs, the view value will always be a string (which is in some cases
25893 * parsed into another type, such as a `Date` object for `input[date]`.)
25894 * However, custom controls might also pass objects to this method. In this case, we should make
25895 * a copy of the object before passing it to `$setViewValue`. This is because `ngModel` does not
25896 * perform a deep watch of objects, it only looks for a change of identity. If you only change
25897 * the property of the object then ngModel will not realise that the object has changed and
25898 * will not invoke the `$parsers` and `$validators` pipelines. For this reason, you should
25899 * not change properties of the copy once it has been passed to `$setViewValue`.
25900 * Otherwise you may cause the model value on the scope to change incorrectly.
25902 * <div class="alert alert-info">
25903 * In any case, the value passed to the method should always reflect the current value
25904 * of the control. For example, if you are calling `$setViewValue` for an input element,
25905 * you should pass the input DOM value. Otherwise, the control and the scope model become
25906 * out of sync. It's also important to note that `$setViewValue` does not call `$render` or change
25907 * the control's DOM value in any way. If we want to change the control's DOM value
25908 * programmatically, we should update the `ngModel` scope expression. Its new value will be
25909 * picked up by the model controller, which will run it through the `$formatters`, `$render` it
25910 * to update the DOM, and finally call `$validate` on it.
25913 * @param {*} value value from the view.
25914 * @param {string} trigger Event that triggered the update.
25916 this.$setViewValue = function(value, trigger) {
25917 ctrl.$viewValue = value;
25918 if (!ctrl.$options || ctrl.$options.updateOnDefault) {
25919 ctrl.$$debounceViewValueCommit(trigger);
25923 this.$$debounceViewValueCommit = function(trigger) {
25924 var debounceDelay = 0,
25925 options = ctrl.$options,
25928 if (options && isDefined(options.debounce)) {
25929 debounce = options.debounce;
25930 if (isNumber(debounce)) {
25931 debounceDelay = debounce;
25932 } else if (isNumber(debounce[trigger])) {
25933 debounceDelay = debounce[trigger];
25934 } else if (isNumber(debounce['default'])) {
25935 debounceDelay = debounce['default'];
25939 $timeout.cancel(pendingDebounce);
25940 if (debounceDelay) {
25941 pendingDebounce = $timeout(function() {
25942 ctrl.$commitViewValue();
25944 } else if ($rootScope.$$phase) {
25945 ctrl.$commitViewValue();
25947 $scope.$apply(function() {
25948 ctrl.$commitViewValue();
25954 // Note: we cannot use a normal scope.$watch as we want to detect the following:
25955 // 1. scope value is 'a'
25956 // 2. user enters 'b'
25957 // 3. ng-change kicks in and reverts scope value to 'a'
25958 // -> scope value did not change since the last digest as
25959 // ng-change executes in apply phase
25960 // 4. view should be changed back to 'a'
25961 $scope.$watch(function ngModelWatch() {
25962 var modelValue = ngModelGet($scope);
25964 // if scope model value and ngModel value are out of sync
25965 // TODO(perf): why not move this to the action fn?
25966 if (modelValue !== ctrl.$modelValue &&
25967 // checks for NaN is needed to allow setting the model to NaN when there's an asyncValidator
25968 (ctrl.$modelValue === ctrl.$modelValue || modelValue === modelValue)
25970 ctrl.$modelValue = ctrl.$$rawModelValue = modelValue;
25971 parserValid = undefined;
25973 var formatters = ctrl.$formatters,
25974 idx = formatters.length;
25976 var viewValue = modelValue;
25978 viewValue = formatters[idx](viewValue);
25980 if (ctrl.$viewValue !== viewValue) {
25981 ctrl.$viewValue = ctrl.$$lastCommittedViewValue = viewValue;
25984 ctrl.$$runValidators(modelValue, viewValue, noop);
26001 * The `ngModel` directive binds an `input`,`select`, `textarea` (or custom form control) to a
26002 * property on the scope using {@link ngModel.NgModelController NgModelController},
26003 * which is created and exposed by this directive.
26005 * `ngModel` is responsible for:
26007 * - Binding the view into the model, which other directives such as `input`, `textarea` or `select`
26009 * - Providing validation behavior (i.e. required, number, email, url).
26010 * - Keeping the state of the control (valid/invalid, dirty/pristine, touched/untouched, validation errors).
26011 * - Setting related css classes on the element (`ng-valid`, `ng-invalid`, `ng-dirty`, `ng-pristine`, `ng-touched`, `ng-untouched`) including animations.
26012 * - Registering the control with its parent {@link ng.directive:form form}.
26014 * Note: `ngModel` will try to bind to the property given by evaluating the expression on the
26015 * current scope. If the property doesn't already exist on this scope, it will be created
26016 * implicitly and added to the scope.
26018 * For best practices on using `ngModel`, see:
26020 * - [Understanding Scopes](https://github.com/angular/angular.js/wiki/Understanding-Scopes)
26022 * For basic examples, how to use `ngModel`, see:
26024 * - {@link ng.directive:input input}
26025 * - {@link input[text] text}
26026 * - {@link input[checkbox] checkbox}
26027 * - {@link input[radio] radio}
26028 * - {@link input[number] number}
26029 * - {@link input[email] email}
26030 * - {@link input[url] url}
26031 * - {@link input[date] date}
26032 * - {@link input[datetime-local] datetime-local}
26033 * - {@link input[time] time}
26034 * - {@link input[month] month}
26035 * - {@link input[week] week}
26036 * - {@link ng.directive:select select}
26037 * - {@link ng.directive:textarea textarea}
26039 * # Complex Models (objects or collections)
26041 * By default, `ngModel` watches the model by reference, not value. This is important to know when
26042 * binding inputs to models that are objects (e.g. `Date`) or collections (e.g. arrays). If only properties of the
26043 * object or collection change, `ngModel` will not be notified and so the input will not be re-rendered.
26045 * The model must be assigned an entirely new object or collection before a re-rendering will occur.
26047 * Some directives have options that will cause them to use a custom `$watchCollection` on the model expression
26048 * - for example, `ngOptions` will do so when a `track by` clause is included in the comprehension expression or
26049 * if the select is given the `multiple` attribute.
26051 * The `$watchCollection()` method only does a shallow comparison, meaning that changing properties deeper than the
26052 * first level of the object (or only changing the properties of an item in the collection if it's an array) will still
26053 * not trigger a re-rendering of the model.
26056 * The following CSS classes are added and removed on the associated input/select/textarea element
26057 * depending on the validity of the model.
26059 * - `ng-valid`: the model is valid
26060 * - `ng-invalid`: the model is invalid
26061 * - `ng-valid-[key]`: for each valid key added by `$setValidity`
26062 * - `ng-invalid-[key]`: for each invalid key added by `$setValidity`
26063 * - `ng-pristine`: the control hasn't been interacted with yet
26064 * - `ng-dirty`: the control has been interacted with
26065 * - `ng-touched`: the control has been blurred
26066 * - `ng-untouched`: the control hasn't been blurred
26067 * - `ng-pending`: any `$asyncValidators` are unfulfilled
26069 * Keep in mind that ngAnimate can detect each of these classes when added and removed.
26071 * ## Animation Hooks
26073 * Animations within models are triggered when any of the associated CSS classes are added and removed
26074 * on the input element which is attached to the model. These classes are: `.ng-pristine`, `.ng-dirty`,
26075 * `.ng-invalid` and `.ng-valid` as well as any other validations that are performed on the model itself.
26076 * The animations that are triggered within ngModel are similar to how they work in ngClass and
26077 * animations can be hooked into using CSS transitions, keyframes as well as JS animations.
26079 * The following example shows a simple way to utilize CSS transitions to style an input element
26080 * that has been rendered as invalid after it has been validated:
26083 * //be sure to include ngAnimate as a module to hook into more
26084 * //advanced animations
26086 * transition:0.5s linear all;
26087 * background: white;
26089 * .my-input.ng-invalid {
26096 * <example deps="angular-animate.js" animations="true" fixBase="true" module="inputExample">
26097 <file name="index.html">
26099 angular.module('inputExample', [])
26100 .controller('ExampleController', ['$scope', function($scope) {
26106 transition:all linear 0.5s;
26107 background: transparent;
26109 .my-input.ng-invalid {
26114 <p id="inputDescription">
26115 Update input to see transitions when valid/invalid.
26116 Integer is a valid value.
26118 <form name="testForm" ng-controller="ExampleController">
26119 <input ng-model="val" ng-pattern="/^\d+$/" name="anim" class="my-input"
26120 aria-describedby="inputDescription" />
26125 * ## Binding to a getter/setter
26127 * Sometimes it's helpful to bind `ngModel` to a getter/setter function. A getter/setter is a
26128 * function that returns a representation of the model when called with zero arguments, and sets
26129 * the internal state of a model when called with an argument. It's sometimes useful to use this
26130 * for models that have an internal representation that's different from what the model exposes
26133 * <div class="alert alert-success">
26134 * **Best Practice:** It's best to keep getters fast because Angular is likely to call them more
26135 * frequently than other parts of your code.
26138 * You use this behavior by adding `ng-model-options="{ getterSetter: true }"` to an element that
26139 * has `ng-model` attached to it. You can also add `ng-model-options="{ getterSetter: true }"` to
26140 * a `<form>`, which will enable this behavior for all `<input>`s within it. See
26141 * {@link ng.directive:ngModelOptions `ngModelOptions`} for more.
26143 * The following example shows how to use `ngModel` with a getter/setter:
26146 * <example name="ngModel-getter-setter" module="getterSetterExample">
26147 <file name="index.html">
26148 <div ng-controller="ExampleController">
26149 <form name="userForm">
26151 <input type="text" name="userName"
26152 ng-model="user.name"
26153 ng-model-options="{ getterSetter: true }" />
26156 <pre>user.name = <span ng-bind="user.name()"></span></pre>
26159 <file name="app.js">
26160 angular.module('getterSetterExample', [])
26161 .controller('ExampleController', ['$scope', function($scope) {
26162 var _name = 'Brian';
26164 name: function(newName) {
26165 // Note that newName can be undefined for two reasons:
26166 // 1. Because it is called as a getter and thus called with no arguments
26167 // 2. Because the property should actually be set to undefined. This happens e.g. if the
26168 // input is invalid
26169 return arguments.length ? (_name = newName) : _name;
26176 var ngModelDirective = ['$rootScope', function($rootScope) {
26179 require: ['ngModel', '^?form', '^?ngModelOptions'],
26180 controller: NgModelController,
26181 // Prelink needs to run before any input directive
26182 // so that we can set the NgModelOptions in NgModelController
26183 // before anyone else uses it.
26185 compile: function ngModelCompile(element) {
26186 // Setup initial state of the control
26187 element.addClass(PRISTINE_CLASS).addClass(UNTOUCHED_CLASS).addClass(VALID_CLASS);
26190 pre: function ngModelPreLink(scope, element, attr, ctrls) {
26191 var modelCtrl = ctrls[0],
26192 formCtrl = ctrls[1] || modelCtrl.$$parentForm;
26194 modelCtrl.$$setOptions(ctrls[2] && ctrls[2].$options);
26196 // notify others, especially parent forms
26197 formCtrl.$addControl(modelCtrl);
26199 attr.$observe('name', function(newValue) {
26200 if (modelCtrl.$name !== newValue) {
26201 modelCtrl.$$parentForm.$$renameControl(modelCtrl, newValue);
26205 scope.$on('$destroy', function() {
26206 modelCtrl.$$parentForm.$removeControl(modelCtrl);
26209 post: function ngModelPostLink(scope, element, attr, ctrls) {
26210 var modelCtrl = ctrls[0];
26211 if (modelCtrl.$options && modelCtrl.$options.updateOn) {
26212 element.on(modelCtrl.$options.updateOn, function(ev) {
26213 modelCtrl.$$debounceViewValueCommit(ev && ev.type);
26217 element.on('blur', function(ev) {
26218 if (modelCtrl.$touched) return;
26220 if ($rootScope.$$phase) {
26221 scope.$evalAsync(modelCtrl.$setTouched);
26223 scope.$apply(modelCtrl.$setTouched);
26232 var DEFAULT_REGEXP = /(\s+|^)default(\s+|$)/;
26236 * @name ngModelOptions
26239 * Allows tuning how model updates are done. Using `ngModelOptions` you can specify a custom list of
26240 * events that will trigger a model update and/or a debouncing delay so that the actual update only
26241 * takes place when a timer expires; this timer will be reset after another change takes place.
26243 * Given the nature of `ngModelOptions`, the value displayed inside input fields in the view might
26244 * be different from the value in the actual model. This means that if you update the model you
26245 * should also invoke {@link ngModel.NgModelController `$rollbackViewValue`} on the relevant input field in
26246 * order to make sure it is synchronized with the model and that any debounced action is canceled.
26248 * The easiest way to reference the control's {@link ngModel.NgModelController `$rollbackViewValue`}
26249 * method is by making sure the input is placed inside a form that has a `name` attribute. This is
26250 * important because `form` controllers are published to the related scope under the name in their
26251 * `name` attribute.
26253 * Any pending changes will take place immediately when an enclosing form is submitted via the
26254 * `submit` event. Note that `ngClick` events will occur before the model is updated. Use `ngSubmit`
26255 * to have access to the updated model.
26257 * `ngModelOptions` has an effect on the element it's declared on and its descendants.
26259 * @param {Object} ngModelOptions options to apply to the current model. Valid keys are:
26260 * - `updateOn`: string specifying which event should the input be bound to. You can set several
26261 * events using an space delimited list. There is a special event called `default` that
26262 * matches the default events belonging of the control.
26263 * - `debounce`: integer value which contains the debounce model update value in milliseconds. A
26264 * value of 0 triggers an immediate update. If an object is supplied instead, you can specify a
26265 * custom value for each event. For example:
26266 * `ng-model-options="{ updateOn: 'default blur', debounce: { 'default': 500, 'blur': 0 } }"`
26267 * - `allowInvalid`: boolean value which indicates that the model can be set with values that did
26268 * not validate correctly instead of the default behavior of setting the model to undefined.
26269 * - `getterSetter`: boolean value which determines whether or not to treat functions bound to
26270 `ngModel` as getters/setters.
26271 * - `timezone`: Defines the timezone to be used to read/write the `Date` instance in the model for
26272 * `<input type="date">`, `<input type="time">`, ... . It understands UTC/GMT and the
26273 * continental US time zone abbreviations, but for general use, use a time zone offset, for
26274 * example, `'+0430'` (4 hours, 30 minutes east of the Greenwich meridian)
26275 * If not specified, the timezone of the browser will be used.
26279 The following example shows how to override immediate updates. Changes on the inputs within the
26280 form will update the model only when the control loses focus (blur event). If `escape` key is
26281 pressed while the input field is focused, the value is reset to the value in the current model.
26283 <example name="ngModelOptions-directive-blur" module="optionsExample">
26284 <file name="index.html">
26285 <div ng-controller="ExampleController">
26286 <form name="userForm">
26288 <input type="text" name="userName"
26289 ng-model="user.name"
26290 ng-model-options="{ updateOn: 'blur' }"
26291 ng-keyup="cancel($event)" />
26294 <input type="text" ng-model="user.data" />
26297 <pre>user.name = <span ng-bind="user.name"></span></pre>
26298 <pre>user.data = <span ng-bind="user.data"></span></pre>
26301 <file name="app.js">
26302 angular.module('optionsExample', [])
26303 .controller('ExampleController', ['$scope', function($scope) {
26304 $scope.user = { name: 'John', data: '' };
26306 $scope.cancel = function(e) {
26307 if (e.keyCode == 27) {
26308 $scope.userForm.userName.$rollbackViewValue();
26313 <file name="protractor.js" type="protractor">
26314 var model = element(by.binding('user.name'));
26315 var input = element(by.model('user.name'));
26316 var other = element(by.model('user.data'));
26318 it('should allow custom events', function() {
26319 input.sendKeys(' Doe');
26321 expect(model.getText()).toEqual('John');
26323 expect(model.getText()).toEqual('John Doe');
26326 it('should $rollbackViewValue when model changes', function() {
26327 input.sendKeys(' Doe');
26328 expect(input.getAttribute('value')).toEqual('John Doe');
26329 input.sendKeys(protractor.Key.ESCAPE);
26330 expect(input.getAttribute('value')).toEqual('John');
26332 expect(model.getText()).toEqual('John');
26337 This one shows how to debounce model changes. Model will be updated only 1 sec after last change.
26338 If the `Clear` button is pressed, any debounced action is canceled and the value becomes empty.
26340 <example name="ngModelOptions-directive-debounce" module="optionsExample">
26341 <file name="index.html">
26342 <div ng-controller="ExampleController">
26343 <form name="userForm">
26345 <input type="text" name="userName"
26346 ng-model="user.name"
26347 ng-model-options="{ debounce: 1000 }" />
26349 <button ng-click="userForm.userName.$rollbackViewValue(); user.name=''">Clear</button>
26352 <pre>user.name = <span ng-bind="user.name"></span></pre>
26355 <file name="app.js">
26356 angular.module('optionsExample', [])
26357 .controller('ExampleController', ['$scope', function($scope) {
26358 $scope.user = { name: 'Igor' };
26363 This one shows how to bind to getter/setters:
26365 <example name="ngModelOptions-directive-getter-setter" module="getterSetterExample">
26366 <file name="index.html">
26367 <div ng-controller="ExampleController">
26368 <form name="userForm">
26370 <input type="text" name="userName"
26371 ng-model="user.name"
26372 ng-model-options="{ getterSetter: true }" />
26375 <pre>user.name = <span ng-bind="user.name()"></span></pre>
26378 <file name="app.js">
26379 angular.module('getterSetterExample', [])
26380 .controller('ExampleController', ['$scope', function($scope) {
26381 var _name = 'Brian';
26383 name: function(newName) {
26384 // Note that newName can be undefined for two reasons:
26385 // 1. Because it is called as a getter and thus called with no arguments
26386 // 2. Because the property should actually be set to undefined. This happens e.g. if the
26387 // input is invalid
26388 return arguments.length ? (_name = newName) : _name;
26395 var ngModelOptionsDirective = function() {
26398 controller: ['$scope', '$attrs', function($scope, $attrs) {
26400 this.$options = copy($scope.$eval($attrs.ngModelOptions));
26401 // Allow adding/overriding bound events
26402 if (isDefined(this.$options.updateOn)) {
26403 this.$options.updateOnDefault = false;
26404 // extract "default" pseudo-event from list of events that can trigger a model update
26405 this.$options.updateOn = trim(this.$options.updateOn.replace(DEFAULT_REGEXP, function() {
26406 that.$options.updateOnDefault = true;
26410 this.$options.updateOnDefault = true;
26419 function addSetValidityMethod(context) {
26420 var ctrl = context.ctrl,
26421 $element = context.$element,
26424 unset = context.unset,
26425 $animate = context.$animate;
26427 classCache[INVALID_CLASS] = !(classCache[VALID_CLASS] = $element.hasClass(VALID_CLASS));
26429 ctrl.$setValidity = setValidity;
26431 function setValidity(validationErrorKey, state, controller) {
26432 if (isUndefined(state)) {
26433 createAndSet('$pending', validationErrorKey, controller);
26435 unsetAndCleanup('$pending', validationErrorKey, controller);
26437 if (!isBoolean(state)) {
26438 unset(ctrl.$error, validationErrorKey, controller);
26439 unset(ctrl.$$success, validationErrorKey, controller);
26442 unset(ctrl.$error, validationErrorKey, controller);
26443 set(ctrl.$$success, validationErrorKey, controller);
26445 set(ctrl.$error, validationErrorKey, controller);
26446 unset(ctrl.$$success, validationErrorKey, controller);
26449 if (ctrl.$pending) {
26450 cachedToggleClass(PENDING_CLASS, true);
26451 ctrl.$valid = ctrl.$invalid = undefined;
26452 toggleValidationCss('', null);
26454 cachedToggleClass(PENDING_CLASS, false);
26455 ctrl.$valid = isObjectEmpty(ctrl.$error);
26456 ctrl.$invalid = !ctrl.$valid;
26457 toggleValidationCss('', ctrl.$valid);
26460 // re-read the state as the set/unset methods could have
26461 // combined state in ctrl.$error[validationError] (used for forms),
26462 // where setting/unsetting only increments/decrements the value,
26463 // and does not replace it.
26465 if (ctrl.$pending && ctrl.$pending[validationErrorKey]) {
26466 combinedState = undefined;
26467 } else if (ctrl.$error[validationErrorKey]) {
26468 combinedState = false;
26469 } else if (ctrl.$$success[validationErrorKey]) {
26470 combinedState = true;
26472 combinedState = null;
26475 toggleValidationCss(validationErrorKey, combinedState);
26476 ctrl.$$parentForm.$setValidity(validationErrorKey, combinedState, ctrl);
26479 function createAndSet(name, value, controller) {
26483 set(ctrl[name], value, controller);
26486 function unsetAndCleanup(name, value, controller) {
26488 unset(ctrl[name], value, controller);
26490 if (isObjectEmpty(ctrl[name])) {
26491 ctrl[name] = undefined;
26495 function cachedToggleClass(className, switchValue) {
26496 if (switchValue && !classCache[className]) {
26497 $animate.addClass($element, className);
26498 classCache[className] = true;
26499 } else if (!switchValue && classCache[className]) {
26500 $animate.removeClass($element, className);
26501 classCache[className] = false;
26505 function toggleValidationCss(validationErrorKey, isValid) {
26506 validationErrorKey = validationErrorKey ? '-' + snake_case(validationErrorKey, '-') : '';
26508 cachedToggleClass(VALID_CLASS + validationErrorKey, isValid === true);
26509 cachedToggleClass(INVALID_CLASS + validationErrorKey, isValid === false);
26513 function isObjectEmpty(obj) {
26515 for (var prop in obj) {
26516 if (obj.hasOwnProperty(prop)) {
26526 * @name ngNonBindable
26531 * The `ngNonBindable` directive tells Angular not to compile or bind the contents of the current
26532 * DOM element. This is useful if the element contains what appears to be Angular directives and
26533 * bindings but which should be ignored by Angular. This could be the case if you have a site that
26534 * displays snippets of code, for instance.
26539 * In this example there are two locations where a simple interpolation binding (`{{}}`) is present,
26540 * but the one wrapped in `ngNonBindable` is left alone.
26544 <file name="index.html">
26545 <div>Normal: {{1 + 2}}</div>
26546 <div ng-non-bindable>Ignored: {{1 + 2}}</div>
26548 <file name="protractor.js" type="protractor">
26549 it('should check ng-non-bindable', function() {
26550 expect(element(by.binding('1 + 2')).getText()).toContain('3');
26551 expect(element.all(by.css('div')).last().getText()).toMatch(/1 \+ 2/);
26556 var ngNonBindableDirective = ngDirective({ terminal: true, priority: 1000 });
26558 /* global jqLiteRemove */
26560 var ngOptionsMinErr = minErr('ngOptions');
26569 * The `ngOptions` attribute can be used to dynamically generate a list of `<option>`
26570 * elements for the `<select>` element using the array or object obtained by evaluating the
26571 * `ngOptions` comprehension expression.
26573 * In many cases, `ngRepeat` can be used on `<option>` elements instead of `ngOptions` to achieve a
26574 * similar result. However, `ngOptions` provides some benefits such as reducing memory and
26575 * increasing speed by not creating a new scope for each repeated instance, as well as providing
26576 * more flexibility in how the `<select>`'s model is assigned via the `select` **`as`** part of the
26577 * comprehension expression. `ngOptions` should be used when the `<select>` model needs to be bound
26578 * to a non-string value. This is because an option element can only be bound to string values at
26581 * When an item in the `<select>` menu is selected, the array element or object property
26582 * represented by the selected option will be bound to the model identified by the `ngModel`
26585 * Optionally, a single hard-coded `<option>` element, with the value set to an empty string, can
26586 * be nested into the `<select>` element. This element will then represent the `null` or "not selected"
26587 * option. See example below for demonstration.
26589 * ## Complex Models (objects or collections)
26591 * By default, `ngModel` watches the model by reference, not value. This is important to know when
26592 * binding the select to a model that is an object or a collection.
26594 * One issue occurs if you want to preselect an option. For example, if you set
26595 * the model to an object that is equal to an object in your collection, `ngOptions` won't be able to set the selection,
26596 * because the objects are not identical. So by default, you should always reference the item in your collection
26597 * for preselections, e.g.: `$scope.selected = $scope.collection[3]`.
26599 * Another solution is to use a `track by` clause, because then `ngOptions` will track the identity
26600 * of the item not by reference, but by the result of the `track by` expression. For example, if your
26601 * collection items have an id property, you would `track by item.id`.
26603 * A different issue with objects or collections is that ngModel won't detect if an object property or
26604 * a collection item changes. For that reason, `ngOptions` additionally watches the model using
26605 * `$watchCollection`, when the expression contains a `track by` clause or the the select has the `multiple` attribute.
26606 * This allows ngOptions to trigger a re-rendering of the options even if the actual object/collection
26607 * has not changed identity, but only a property on the object or an item in the collection changes.
26609 * Note that `$watchCollection` does a shallow comparison of the properties of the object (or the items in the collection
26610 * if the model is an array). This means that changing a property deeper than the first level inside the
26611 * object/collection will not trigger a re-rendering.
26613 * ## `select` **`as`**
26615 * Using `select` **`as`** will bind the result of the `select` expression to the model, but
26616 * the value of the `<select>` and `<option>` html elements will be either the index (for array data sources)
26617 * or property name (for object data sources) of the value within the collection. If a **`track by`** expression
26618 * is used, the result of that expression will be set as the value of the `option` and `select` elements.
26621 * ### `select` **`as`** and **`track by`**
26623 * <div class="alert alert-warning">
26624 * Be careful when using `select` **`as`** and **`track by`** in the same expression.
26627 * Given this array of items on the $scope:
26630 * $scope.items = [{
26633 * subItem: { name: 'aSubItem' }
26637 * subItem: { name: 'bSubItem' }
26644 * <select ng-options="item as item.label for item in items track by item.id" ng-model="selected"></select>
26647 * $scope.selected = $scope.items[0];
26650 * but this will not work:
26653 * <select ng-options="item.subItem as item.label for item in items track by item.id" ng-model="selected"></select>
26656 * $scope.selected = $scope.items[0].subItem;
26659 * In both examples, the **`track by`** expression is applied successfully to each `item` in the
26660 * `items` array. Because the selected option has been set programmatically in the controller, the
26661 * **`track by`** expression is also applied to the `ngModel` value. In the first example, the
26662 * `ngModel` value is `items[0]` and the **`track by`** expression evaluates to `items[0].id` with
26663 * no issue. In the second example, the `ngModel` value is `items[0].subItem` and the **`track by`**
26664 * expression evaluates to `items[0].subItem.id` (which is undefined). As a result, the model value
26665 * is not matched against any `<option>` and the `<select>` appears as having no selected value.
26668 * @param {string} ngModel Assignable angular expression to data-bind to.
26669 * @param {string=} name Property name of the form under which the control is published.
26670 * @param {string=} required The control is considered valid only if value is entered.
26671 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
26672 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
26673 * `required` when you want to data-bind to the `required` attribute.
26674 * @param {comprehension_expression=} ngOptions in one of the following forms:
26676 * * for array data sources:
26677 * * `label` **`for`** `value` **`in`** `array`
26678 * * `select` **`as`** `label` **`for`** `value` **`in`** `array`
26679 * * `label` **`group by`** `group` **`for`** `value` **`in`** `array`
26680 * * `label` **`disable when`** `disable` **`for`** `value` **`in`** `array`
26681 * * `label` **`group by`** `group` **`for`** `value` **`in`** `array` **`track by`** `trackexpr`
26682 * * `label` **`disable when`** `disable` **`for`** `value` **`in`** `array` **`track by`** `trackexpr`
26683 * * `label` **`for`** `value` **`in`** `array` | orderBy:`orderexpr` **`track by`** `trackexpr`
26684 * (for including a filter with `track by`)
26685 * * for object data sources:
26686 * * `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
26687 * * `select` **`as`** `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
26688 * * `label` **`group by`** `group` **`for (`**`key`**`,`** `value`**`) in`** `object`
26689 * * `label` **`disable when`** `disable` **`for (`**`key`**`,`** `value`**`) in`** `object`
26690 * * `select` **`as`** `label` **`group by`** `group`
26691 * **`for` `(`**`key`**`,`** `value`**`) in`** `object`
26692 * * `select` **`as`** `label` **`disable when`** `disable`
26693 * **`for` `(`**`key`**`,`** `value`**`) in`** `object`
26697 * * `array` / `object`: an expression which evaluates to an array / object to iterate over.
26698 * * `value`: local variable which will refer to each item in the `array` or each property value
26699 * of `object` during iteration.
26700 * * `key`: local variable which will refer to a property name in `object` during iteration.
26701 * * `label`: The result of this expression will be the label for `<option>` element. The
26702 * `expression` will most likely refer to the `value` variable (e.g. `value.propertyName`).
26703 * * `select`: The result of this expression will be bound to the model of the parent `<select>`
26704 * element. If not specified, `select` expression will default to `value`.
26705 * * `group`: The result of this expression will be used to group options using the `<optgroup>`
26707 * * `disable`: The result of this expression will be used to disable the rendered `<option>`
26708 * element. Return `true` to disable.
26709 * * `trackexpr`: Used when working with an array of objects. The result of this expression will be
26710 * used to identify the objects in the array. The `trackexpr` will most likely refer to the
26711 * `value` variable (e.g. `value.propertyName`). With this the selection is preserved
26712 * even when the options are recreated (e.g. reloaded from the server).
26715 <example module="selectExample">
26716 <file name="index.html">
26718 angular.module('selectExample', [])
26719 .controller('ExampleController', ['$scope', function($scope) {
26721 {name:'black', shade:'dark'},
26722 {name:'white', shade:'light', notAnOption: true},
26723 {name:'red', shade:'dark'},
26724 {name:'blue', shade:'dark', notAnOption: true},
26725 {name:'yellow', shade:'light', notAnOption: false}
26727 $scope.myColor = $scope.colors[2]; // red
26730 <div ng-controller="ExampleController">
26732 <li ng-repeat="color in colors">
26733 <label>Name: <input ng-model="color.name"></label>
26734 <label><input type="checkbox" ng-model="color.notAnOption"> Disabled?</label>
26735 <button ng-click="colors.splice($index, 1)" aria-label="Remove">X</button>
26738 <button ng-click="colors.push({})">add</button>
26742 <label>Color (null not allowed):
26743 <select ng-model="myColor" ng-options="color.name for color in colors"></select>
26745 <label>Color (null allowed):
26746 <span class="nullable">
26747 <select ng-model="myColor" ng-options="color.name for color in colors">
26748 <option value="">-- choose color --</option>
26750 </span></label><br/>
26752 <label>Color grouped by shade:
26753 <select ng-model="myColor" ng-options="color.name group by color.shade for color in colors">
26757 <label>Color grouped by shade, with some disabled:
26758 <select ng-model="myColor"
26759 ng-options="color.name group by color.shade disable when color.notAnOption for color in colors">
26765 Select <button ng-click="myColor = { name:'not in list', shade: 'other' }">bogus</button>.
26768 Currently selected: {{ {selected_color:myColor} }}
26769 <div style="border:solid 1px black; height:20px"
26770 ng-style="{'background-color':myColor.name}">
26774 <file name="protractor.js" type="protractor">
26775 it('should check ng-options', function() {
26776 expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('red');
26777 element.all(by.model('myColor')).first().click();
26778 element.all(by.css('select[ng-model="myColor"] option')).first().click();
26779 expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('black');
26780 element(by.css('.nullable select[ng-model="myColor"]')).click();
26781 element.all(by.css('.nullable select[ng-model="myColor"] option')).first().click();
26782 expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('null');
26788 // jshint maxlen: false
26789 // //00001111111111000000000002222222222000000000000000000000333333333300000000000000000000000004444444444400000000000005555555555555550000000006666666666666660000000777777777777777000000000000000888888888800000000000000000009999999999
26790 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]+?))?$/;
26791 // 1: value expression (valueFn)
26792 // 2: label expression (displayFn)
26793 // 3: group by expression (groupByFn)
26794 // 4: disable when expression (disableWhenFn)
26795 // 5: array item variable name
26796 // 6: object item key variable name
26797 // 7: object item value variable name
26798 // 8: collection expression
26799 // 9: track by expression
26800 // jshint maxlen: 100
26803 var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
26805 function parseOptionsExpression(optionsExp, selectElement, scope) {
26807 var match = optionsExp.match(NG_OPTIONS_REGEXP);
26809 throw ngOptionsMinErr('iexp',
26810 "Expected expression in form of " +
26811 "'_select_ (as _label_)? for (_key_,)?_value_ in _collection_'" +
26812 " but got '{0}'. Element: {1}",
26813 optionsExp, startingTag(selectElement));
26816 // Extract the parts from the ngOptions expression
26818 // The variable name for the value of the item in the collection
26819 var valueName = match[5] || match[7];
26820 // The variable name for the key of the item in the collection
26821 var keyName = match[6];
26823 // An expression that generates the viewValue for an option if there is a label expression
26824 var selectAs = / as /.test(match[0]) && match[1];
26825 // An expression that is used to track the id of each object in the options collection
26826 var trackBy = match[9];
26827 // An expression that generates the viewValue for an option if there is no label expression
26828 var valueFn = $parse(match[2] ? match[1] : valueName);
26829 var selectAsFn = selectAs && $parse(selectAs);
26830 var viewValueFn = selectAsFn || valueFn;
26831 var trackByFn = trackBy && $parse(trackBy);
26833 // Get the value by which we are going to track the option
26834 // if we have a trackFn then use that (passing scope and locals)
26835 // otherwise just hash the given viewValue
26836 var getTrackByValueFn = trackBy ?
26837 function(value, locals) { return trackByFn(scope, locals); } :
26838 function getHashOfValue(value) { return hashKey(value); };
26839 var getTrackByValue = function(value, key) {
26840 return getTrackByValueFn(value, getLocals(value, key));
26843 var displayFn = $parse(match[2] || match[1]);
26844 var groupByFn = $parse(match[3] || '');
26845 var disableWhenFn = $parse(match[4] || '');
26846 var valuesFn = $parse(match[8]);
26849 var getLocals = keyName ? function(value, key) {
26850 locals[keyName] = key;
26851 locals[valueName] = value;
26853 } : function(value) {
26854 locals[valueName] = value;
26859 function Option(selectValue, viewValue, label, group, disabled) {
26860 this.selectValue = selectValue;
26861 this.viewValue = viewValue;
26862 this.label = label;
26863 this.group = group;
26864 this.disabled = disabled;
26867 function getOptionValuesKeys(optionValues) {
26868 var optionValuesKeys;
26870 if (!keyName && isArrayLike(optionValues)) {
26871 optionValuesKeys = optionValues;
26873 // if object, extract keys, in enumeration order, unsorted
26874 optionValuesKeys = [];
26875 for (var itemKey in optionValues) {
26876 if (optionValues.hasOwnProperty(itemKey) && itemKey.charAt(0) !== '$') {
26877 optionValuesKeys.push(itemKey);
26881 return optionValuesKeys;
26886 getTrackByValue: getTrackByValue,
26887 getWatchables: $parse(valuesFn, function(optionValues) {
26888 // Create a collection of things that we would like to watch (watchedArray)
26889 // so that they can all be watched using a single $watchCollection
26890 // that only runs the handler once if anything changes
26891 var watchedArray = [];
26892 optionValues = optionValues || [];
26894 var optionValuesKeys = getOptionValuesKeys(optionValues);
26895 var optionValuesLength = optionValuesKeys.length;
26896 for (var index = 0; index < optionValuesLength; index++) {
26897 var key = (optionValues === optionValuesKeys) ? index : optionValuesKeys[index];
26898 var value = optionValues[key];
26900 var locals = getLocals(optionValues[key], key);
26901 var selectValue = getTrackByValueFn(optionValues[key], locals);
26902 watchedArray.push(selectValue);
26904 // Only need to watch the displayFn if there is a specific label expression
26905 if (match[2] || match[1]) {
26906 var label = displayFn(scope, locals);
26907 watchedArray.push(label);
26910 // Only need to watch the disableWhenFn if there is a specific disable expression
26912 var disableWhen = disableWhenFn(scope, locals);
26913 watchedArray.push(disableWhen);
26916 return watchedArray;
26919 getOptions: function() {
26921 var optionItems = [];
26922 var selectValueMap = {};
26924 // The option values were already computed in the `getWatchables` fn,
26925 // which must have been called to trigger `getOptions`
26926 var optionValues = valuesFn(scope) || [];
26927 var optionValuesKeys = getOptionValuesKeys(optionValues);
26928 var optionValuesLength = optionValuesKeys.length;
26930 for (var index = 0; index < optionValuesLength; index++) {
26931 var key = (optionValues === optionValuesKeys) ? index : optionValuesKeys[index];
26932 var value = optionValues[key];
26933 var locals = getLocals(value, key);
26934 var viewValue = viewValueFn(scope, locals);
26935 var selectValue = getTrackByValueFn(viewValue, locals);
26936 var label = displayFn(scope, locals);
26937 var group = groupByFn(scope, locals);
26938 var disabled = disableWhenFn(scope, locals);
26939 var optionItem = new Option(selectValue, viewValue, label, group, disabled);
26941 optionItems.push(optionItem);
26942 selectValueMap[selectValue] = optionItem;
26946 items: optionItems,
26947 selectValueMap: selectValueMap,
26948 getOptionFromViewValue: function(value) {
26949 return selectValueMap[getTrackByValue(value)];
26951 getViewValueFromOption: function(option) {
26952 // If the viewValue could be an object that may be mutated by the application,
26953 // we need to make a copy and not return the reference to the value on the option.
26954 return trackBy ? angular.copy(option.viewValue) : option.viewValue;
26962 // we can't just jqLite('<option>') since jqLite is not smart enough
26963 // to create it in <select> and IE barfs otherwise.
26964 var optionTemplate = document.createElement('option'),
26965 optGroupTemplate = document.createElement('optgroup');
26968 function ngOptionsPostLink(scope, selectElement, attr, ctrls) {
26970 // if ngModel is not defined, we don't need to do anything
26971 var ngModelCtrl = ctrls[1];
26972 if (!ngModelCtrl) return;
26974 var selectCtrl = ctrls[0];
26975 var multiple = attr.multiple;
26977 // The emptyOption allows the application developer to provide their own custom "empty"
26978 // option when the viewValue does not match any of the option values.
26980 for (var i = 0, children = selectElement.children(), ii = children.length; i < ii; i++) {
26981 if (children[i].value === '') {
26982 emptyOption = children.eq(i);
26987 var providedEmptyOption = !!emptyOption;
26989 var unknownOption = jqLite(optionTemplate.cloneNode(false));
26990 unknownOption.val('?');
26993 var ngOptions = parseOptionsExpression(attr.ngOptions, selectElement, scope);
26996 var renderEmptyOption = function() {
26997 if (!providedEmptyOption) {
26998 selectElement.prepend(emptyOption);
27000 selectElement.val('');
27001 emptyOption.prop('selected', true); // needed for IE
27002 emptyOption.attr('selected', true);
27005 var removeEmptyOption = function() {
27006 if (!providedEmptyOption) {
27007 emptyOption.remove();
27012 var renderUnknownOption = function() {
27013 selectElement.prepend(unknownOption);
27014 selectElement.val('?');
27015 unknownOption.prop('selected', true); // needed for IE
27016 unknownOption.attr('selected', true);
27019 var removeUnknownOption = function() {
27020 unknownOption.remove();
27023 // Update the controller methods for multiple selectable options
27026 selectCtrl.writeValue = function writeNgOptionsValue(value) {
27027 var option = options.getOptionFromViewValue(value);
27029 if (option && !option.disabled) {
27030 // Don't update the option when it is already selected.
27031 // For example, the browser will select the first option by default. In that case,
27032 // most properties are set automatically - except the `selected` attribute, which we
27035 if (selectElement[0].value !== option.selectValue) {
27036 removeUnknownOption();
27037 removeEmptyOption();
27039 selectElement[0].value = option.selectValue;
27040 option.element.selected = true;
27043 option.element.setAttribute('selected', 'selected');
27045 if (value === null || providedEmptyOption) {
27046 removeUnknownOption();
27047 renderEmptyOption();
27049 removeEmptyOption();
27050 renderUnknownOption();
27055 selectCtrl.readValue = function readNgOptionsValue() {
27057 var selectedOption = options.selectValueMap[selectElement.val()];
27059 if (selectedOption && !selectedOption.disabled) {
27060 removeEmptyOption();
27061 removeUnknownOption();
27062 return options.getViewValueFromOption(selectedOption);
27067 // If we are using `track by` then we must watch the tracked value on the model
27068 // since ngModel only watches for object identity change
27069 if (ngOptions.trackBy) {
27071 function() { return ngOptions.getTrackByValue(ngModelCtrl.$viewValue); },
27072 function() { ngModelCtrl.$render(); }
27078 ngModelCtrl.$isEmpty = function(value) {
27079 return !value || value.length === 0;
27083 selectCtrl.writeValue = function writeNgOptionsMultiple(value) {
27084 options.items.forEach(function(option) {
27085 option.element.selected = false;
27089 value.forEach(function(item) {
27090 var option = options.getOptionFromViewValue(item);
27091 if (option && !option.disabled) option.element.selected = true;
27097 selectCtrl.readValue = function readNgOptionsMultiple() {
27098 var selectedValues = selectElement.val() || [],
27101 forEach(selectedValues, function(value) {
27102 var option = options.selectValueMap[value];
27103 if (option && !option.disabled) selections.push(options.getViewValueFromOption(option));
27109 // If we are using `track by` then we must watch these tracked values on the model
27110 // since ngModel only watches for object identity change
27111 if (ngOptions.trackBy) {
27113 scope.$watchCollection(function() {
27114 if (isArray(ngModelCtrl.$viewValue)) {
27115 return ngModelCtrl.$viewValue.map(function(value) {
27116 return ngOptions.getTrackByValue(value);
27120 ngModelCtrl.$render();
27127 if (providedEmptyOption) {
27129 // we need to remove it before calling selectElement.empty() because otherwise IE will
27130 // remove the label from the element. wtf?
27131 emptyOption.remove();
27133 // compile the element since there might be bindings in it
27134 $compile(emptyOption)(scope);
27136 // remove the class, which is added automatically because we recompile the element and it
27137 // becomes the compilation root
27138 emptyOption.removeClass('ng-scope');
27140 emptyOption = jqLite(optionTemplate.cloneNode(false));
27143 // We need to do this here to ensure that the options object is defined
27144 // when we first hit it in writeNgOptionsValue
27147 // We will re-render the option elements if the option values or labels change
27148 scope.$watchCollection(ngOptions.getWatchables, updateOptions);
27150 // ------------------------------------------------------------------ //
27153 function updateOptionElement(option, element) {
27154 option.element = element;
27155 element.disabled = option.disabled;
27156 // NOTE: The label must be set before the value, otherwise IE10/11/EDGE create unresponsive
27157 // selects in certain circumstances when multiple selects are next to each other and display
27158 // the option list in listbox style, i.e. the select is [multiple], or specifies a [size].
27159 // See https://github.com/angular/angular.js/issues/11314 for more info.
27160 // This is unfortunately untestable with unit / e2e tests
27161 if (option.label !== element.label) {
27162 element.label = option.label;
27163 element.textContent = option.label;
27165 if (option.value !== element.value) element.value = option.selectValue;
27168 function addOrReuseElement(parent, current, type, templateElement) {
27170 // Check whether we can reuse the next element
27171 if (current && lowercase(current.nodeName) === type) {
27172 // The next element is the right type so reuse it
27175 // The next element is not the right type so create a new one
27176 element = templateElement.cloneNode(false);
27178 // There are no more elements so just append it to the select
27179 parent.appendChild(element);
27181 // The next element is not a group so insert the new one
27182 parent.insertBefore(element, current);
27189 function removeExcessElements(current) {
27192 next = current.nextSibling;
27193 jqLiteRemove(current);
27199 function skipEmptyAndUnknownOptions(current) {
27200 var emptyOption_ = emptyOption && emptyOption[0];
27201 var unknownOption_ = unknownOption && unknownOption[0];
27203 // We cannot rely on the extracted empty option being the same as the compiled empty option,
27204 // because the compiled empty option might have been replaced by a comment because
27205 // it had an "element" transclusion directive on it (such as ngIf)
27206 if (emptyOption_ || unknownOption_) {
27208 (current === emptyOption_ ||
27209 current === unknownOption_ ||
27210 current.nodeType === NODE_TYPE_COMMENT ||
27211 (nodeName_(current) === 'option' && current.value === ''))) {
27212 current = current.nextSibling;
27219 function updateOptions() {
27221 var previousValue = options && selectCtrl.readValue();
27223 options = ngOptions.getOptions();
27226 var currentElement = selectElement[0].firstChild;
27228 // Ensure that the empty option is always there if it was explicitly provided
27229 if (providedEmptyOption) {
27230 selectElement.prepend(emptyOption);
27233 currentElement = skipEmptyAndUnknownOptions(currentElement);
27235 options.items.forEach(function updateOption(option) {
27240 if (option.group) {
27242 // This option is to live in a group
27243 // See if we have already created this group
27244 group = groupMap[option.group];
27248 // We have not already created this group
27249 groupElement = addOrReuseElement(selectElement[0],
27253 // Move to the next element
27254 currentElement = groupElement.nextSibling;
27256 // Update the label on the group element
27257 groupElement.label = option.group;
27259 // Store it for use later
27260 group = groupMap[option.group] = {
27261 groupElement: groupElement,
27262 currentOptionElement: groupElement.firstChild
27267 // So now we have a group for this option we add the option to the group
27268 optionElement = addOrReuseElement(group.groupElement,
27269 group.currentOptionElement,
27272 updateOptionElement(option, optionElement);
27273 // Move to the next element
27274 group.currentOptionElement = optionElement.nextSibling;
27278 // This option is not in a group
27279 optionElement = addOrReuseElement(selectElement[0],
27283 updateOptionElement(option, optionElement);
27284 // Move to the next element
27285 currentElement = optionElement.nextSibling;
27290 // Now remove all excess options and group
27291 Object.keys(groupMap).forEach(function(key) {
27292 removeExcessElements(groupMap[key].currentOptionElement);
27294 removeExcessElements(currentElement);
27296 ngModelCtrl.$render();
27298 // Check to see if the value has changed due to the update to the options
27299 if (!ngModelCtrl.$isEmpty(previousValue)) {
27300 var nextValue = selectCtrl.readValue();
27301 var isNotPrimitive = ngOptions.trackBy || multiple;
27302 if (isNotPrimitive ? !equals(previousValue, nextValue) : previousValue !== nextValue) {
27303 ngModelCtrl.$setViewValue(nextValue);
27304 ngModelCtrl.$render();
27314 require: ['select', '?ngModel'],
27316 pre: function ngOptionsPreLink(scope, selectElement, attr, ctrls) {
27317 // Deactivate the SelectController.register method to prevent
27318 // option directives from accidentally registering themselves
27319 // (and unwanted $destroy handlers etc.)
27320 ctrls[0].registerOption = noop;
27322 post: ngOptionsPostLink
27329 * @name ngPluralize
27333 * `ngPluralize` is a directive that displays messages according to en-US localization rules.
27334 * These rules are bundled with angular.js, but can be overridden
27335 * (see {@link guide/i18n Angular i18n} dev guide). You configure ngPluralize directive
27336 * by specifying the mappings between
27337 * [plural categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html)
27338 * and the strings to be displayed.
27340 * # Plural categories and explicit number rules
27342 * [plural categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html)
27343 * in Angular's default en-US locale: "one" and "other".
27345 * While a plural category may match many numbers (for example, in en-US locale, "other" can match
27346 * any number that is not 1), an explicit number rule can only match one number. For example, the
27347 * explicit number rule for "3" matches the number 3. There are examples of plural categories
27348 * and explicit number rules throughout the rest of this documentation.
27350 * # Configuring ngPluralize
27351 * You configure ngPluralize by providing 2 attributes: `count` and `when`.
27352 * You can also provide an optional attribute, `offset`.
27354 * The value of the `count` attribute can be either a string or an {@link guide/expression
27355 * Angular expression}; these are evaluated on the current scope for its bound value.
27357 * The `when` attribute specifies the mappings between plural categories and the actual
27358 * string to be displayed. The value of the attribute should be a JSON object.
27360 * The following example shows how to configure ngPluralize:
27363 * <ng-pluralize count="personCount"
27364 when="{'0': 'Nobody is viewing.',
27365 * 'one': '1 person is viewing.',
27366 * 'other': '{} people are viewing.'}">
27370 * In the example, `"0: Nobody is viewing."` is an explicit number rule. If you did not
27371 * specify this rule, 0 would be matched to the "other" category and "0 people are viewing"
27372 * would be shown instead of "Nobody is viewing". You can specify an explicit number rule for
27373 * other numbers, for example 12, so that instead of showing "12 people are viewing", you can
27374 * show "a dozen people are viewing".
27376 * You can use a set of closed braces (`{}`) as a placeholder for the number that you want substituted
27377 * into pluralized strings. In the previous example, Angular will replace `{}` with
27378 * <span ng-non-bindable>`{{personCount}}`</span>. The closed braces `{}` is a placeholder
27379 * for <span ng-non-bindable>{{numberExpression}}</span>.
27381 * If no rule is defined for a category, then an empty string is displayed and a warning is generated.
27382 * Note that some locales define more categories than `one` and `other`. For example, fr-fr defines `few` and `many`.
27384 * # Configuring ngPluralize with offset
27385 * The `offset` attribute allows further customization of pluralized text, which can result in
27386 * a better user experience. For example, instead of the message "4 people are viewing this document",
27387 * you might display "John, Kate and 2 others are viewing this document".
27388 * The offset attribute allows you to offset a number by any desired value.
27389 * Let's take a look at an example:
27392 * <ng-pluralize count="personCount" offset=2
27393 * when="{'0': 'Nobody is viewing.',
27394 * '1': '{{person1}} is viewing.',
27395 * '2': '{{person1}} and {{person2}} are viewing.',
27396 * 'one': '{{person1}}, {{person2}} and one other person are viewing.',
27397 * 'other': '{{person1}}, {{person2}} and {} other people are viewing.'}">
27401 * Notice that we are still using two plural categories(one, other), but we added
27402 * three explicit number rules 0, 1 and 2.
27403 * When one person, perhaps John, views the document, "John is viewing" will be shown.
27404 * When three people view the document, no explicit number rule is found, so
27405 * an offset of 2 is taken off 3, and Angular uses 1 to decide the plural category.
27406 * In this case, plural category 'one' is matched and "John, Mary and one other person are viewing"
27409 * Note that when you specify offsets, you must provide explicit number rules for
27410 * numbers from 0 up to and including the offset. If you use an offset of 3, for example,
27411 * you must provide explicit number rules for 0, 1, 2 and 3. You must also provide plural strings for
27412 * plural categories "one" and "other".
27414 * @param {string|expression} count The variable to be bound to.
27415 * @param {string} when The mapping between plural category to its corresponding strings.
27416 * @param {number=} offset Offset to deduct from the total number.
27419 <example module="pluralizeExample">
27420 <file name="index.html">
27422 angular.module('pluralizeExample', [])
27423 .controller('ExampleController', ['$scope', function($scope) {
27424 $scope.person1 = 'Igor';
27425 $scope.person2 = 'Misko';
27426 $scope.personCount = 1;
27429 <div ng-controller="ExampleController">
27430 <label>Person 1:<input type="text" ng-model="person1" value="Igor" /></label><br/>
27431 <label>Person 2:<input type="text" ng-model="person2" value="Misko" /></label><br/>
27432 <label>Number of People:<input type="text" ng-model="personCount" value="1" /></label><br/>
27434 <!--- Example with simple pluralization rules for en locale --->
27436 <ng-pluralize count="personCount"
27437 when="{'0': 'Nobody is viewing.',
27438 'one': '1 person is viewing.',
27439 'other': '{} people are viewing.'}">
27440 </ng-pluralize><br>
27442 <!--- Example with offset --->
27444 <ng-pluralize count="personCount" offset=2
27445 when="{'0': 'Nobody is viewing.',
27446 '1': '{{person1}} is viewing.',
27447 '2': '{{person1}} and {{person2}} are viewing.',
27448 'one': '{{person1}}, {{person2}} and one other person are viewing.',
27449 'other': '{{person1}}, {{person2}} and {} other people are viewing.'}">
27453 <file name="protractor.js" type="protractor">
27454 it('should show correct pluralized string', function() {
27455 var withoutOffset = element.all(by.css('ng-pluralize')).get(0);
27456 var withOffset = element.all(by.css('ng-pluralize')).get(1);
27457 var countInput = element(by.model('personCount'));
27459 expect(withoutOffset.getText()).toEqual('1 person is viewing.');
27460 expect(withOffset.getText()).toEqual('Igor is viewing.');
27462 countInput.clear();
27463 countInput.sendKeys('0');
27465 expect(withoutOffset.getText()).toEqual('Nobody is viewing.');
27466 expect(withOffset.getText()).toEqual('Nobody is viewing.');
27468 countInput.clear();
27469 countInput.sendKeys('2');
27471 expect(withoutOffset.getText()).toEqual('2 people are viewing.');
27472 expect(withOffset.getText()).toEqual('Igor and Misko are viewing.');
27474 countInput.clear();
27475 countInput.sendKeys('3');
27477 expect(withoutOffset.getText()).toEqual('3 people are viewing.');
27478 expect(withOffset.getText()).toEqual('Igor, Misko and one other person are viewing.');
27480 countInput.clear();
27481 countInput.sendKeys('4');
27483 expect(withoutOffset.getText()).toEqual('4 people are viewing.');
27484 expect(withOffset.getText()).toEqual('Igor, Misko and 2 other people are viewing.');
27486 it('should show data-bound names', function() {
27487 var withOffset = element.all(by.css('ng-pluralize')).get(1);
27488 var personCount = element(by.model('personCount'));
27489 var person1 = element(by.model('person1'));
27490 var person2 = element(by.model('person2'));
27491 personCount.clear();
27492 personCount.sendKeys('4');
27494 person1.sendKeys('Di');
27496 person2.sendKeys('Vojta');
27497 expect(withOffset.getText()).toEqual('Di, Vojta and 2 other people are viewing.');
27502 var ngPluralizeDirective = ['$locale', '$interpolate', '$log', function($locale, $interpolate, $log) {
27504 IS_WHEN = /^when(Minus)?(.+)$/;
27507 link: function(scope, element, attr) {
27508 var numberExp = attr.count,
27509 whenExp = attr.$attr.when && element.attr(attr.$attr.when), // we have {{}} in attrs
27510 offset = attr.offset || 0,
27511 whens = scope.$eval(whenExp) || {},
27513 startSymbol = $interpolate.startSymbol(),
27514 endSymbol = $interpolate.endSymbol(),
27515 braceReplacement = startSymbol + numberExp + '-' + offset + endSymbol,
27516 watchRemover = angular.noop,
27519 forEach(attr, function(expression, attributeName) {
27520 var tmpMatch = IS_WHEN.exec(attributeName);
27522 var whenKey = (tmpMatch[1] ? '-' : '') + lowercase(tmpMatch[2]);
27523 whens[whenKey] = element.attr(attr.$attr[attributeName]);
27526 forEach(whens, function(expression, key) {
27527 whensExpFns[key] = $interpolate(expression.replace(BRACE, braceReplacement));
27531 scope.$watch(numberExp, function ngPluralizeWatchAction(newVal) {
27532 var count = parseFloat(newVal);
27533 var countIsNaN = isNaN(count);
27535 if (!countIsNaN && !(count in whens)) {
27536 // If an explicit number rule such as 1, 2, 3... is defined, just use it.
27537 // Otherwise, check it against pluralization rules in $locale service.
27538 count = $locale.pluralCat(count - offset);
27541 // If both `count` and `lastCount` are NaN, we don't need to re-register a watch.
27542 // In JS `NaN !== NaN`, so we have to exlicitly check.
27543 if ((count !== lastCount) && !(countIsNaN && isNumber(lastCount) && isNaN(lastCount))) {
27545 var whenExpFn = whensExpFns[count];
27546 if (isUndefined(whenExpFn)) {
27547 if (newVal != null) {
27548 $log.debug("ngPluralize: no rule defined for '" + count + "' in " + whenExp);
27550 watchRemover = noop;
27551 updateElementText();
27553 watchRemover = scope.$watch(whenExpFn, updateElementText);
27559 function updateElementText(newText) {
27560 element.text(newText || '');
27572 * The `ngRepeat` directive instantiates a template once per item from a collection. Each template
27573 * instance gets its own scope, where the given loop variable is set to the current collection item,
27574 * and `$index` is set to the item index or key.
27576 * Special properties are exposed on the local scope of each template instance, including:
27578 * | Variable | Type | Details |
27579 * |-----------|-----------------|-----------------------------------------------------------------------------|
27580 * | `$index` | {@type number} | iterator offset of the repeated element (0..length-1) |
27581 * | `$first` | {@type boolean} | true if the repeated element is first in the iterator. |
27582 * | `$middle` | {@type boolean} | true if the repeated element is between the first and last in the iterator. |
27583 * | `$last` | {@type boolean} | true if the repeated element is last in the iterator. |
27584 * | `$even` | {@type boolean} | true if the iterator position `$index` is even (otherwise false). |
27585 * | `$odd` | {@type boolean} | true if the iterator position `$index` is odd (otherwise false). |
27587 * <div class="alert alert-info">
27588 * Creating aliases for these properties is possible with {@link ng.directive:ngInit `ngInit`}.
27589 * This may be useful when, for instance, nesting ngRepeats.
27593 * # Iterating over object properties
27595 * It is possible to get `ngRepeat` to iterate over the properties of an object using the following
27599 * <div ng-repeat="(key, value) in myObj"> ... </div>
27602 * You need to be aware that the JavaScript specification does not define the order of keys
27603 * returned for an object. (To mitigate this in Angular 1.3 the `ngRepeat` directive
27604 * used to sort the keys alphabetically.)
27606 * Version 1.4 removed the alphabetic sorting. We now rely on the order returned by the browser
27607 * when running `for key in myObj`. It seems that browsers generally follow the strategy of providing
27608 * keys in the order in which they were defined, although there are exceptions when keys are deleted
27609 * and reinstated. See the [MDN page on `delete` for more info](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/delete#Cross-browser_notes).
27611 * If this is not desired, the recommended workaround is to convert your object into an array
27612 * that is sorted into the order that you prefer before providing it to `ngRepeat`. You could
27613 * do this with a filter such as [toArrayFilter](http://ngmodules.org/modules/angular-toArrayFilter)
27614 * or implement a `$watch` on the object yourself.
27617 * # Tracking and Duplicates
27619 * `ngRepeat` uses {@link $rootScope.Scope#$watchCollection $watchCollection} to detect changes in
27620 * the collection. When a change happens, ngRepeat then makes the corresponding changes to the DOM:
27622 * * When an item is added, a new instance of the template is added to the DOM.
27623 * * When an item is removed, its template instance is removed from the DOM.
27624 * * When items are reordered, their respective templates are reordered in the DOM.
27626 * To minimize creation of DOM elements, `ngRepeat` uses a function
27627 * to "keep track" of all items in the collection and their corresponding DOM elements.
27628 * For example, if an item is added to the collection, ngRepeat will know that all other items
27629 * already have DOM elements, and will not re-render them.
27631 * The default tracking function (which tracks items by their identity) does not allow
27632 * duplicate items in arrays. This is because when there are duplicates, it is not possible
27633 * to maintain a one-to-one mapping between collection items and DOM elements.
27635 * If you do need to repeat duplicate items, you can substitute the default tracking behavior
27636 * with your own using the `track by` expression.
27638 * For example, you may track items by the index of each item in the collection, using the
27639 * special scope property `$index`:
27641 * <div ng-repeat="n in [42, 42, 43, 43] track by $index">
27646 * You may also use arbitrary expressions in `track by`, including references to custom functions
27649 * <div ng-repeat="n in [42, 42, 43, 43] track by myTrackingFunction(n)">
27654 * <div class="alert alert-success">
27655 * If you are working with objects that have an identifier property, you should track
27656 * by the identifier instead of the whole object. Should you reload your data later, `ngRepeat`
27657 * will not have to rebuild the DOM elements for items it has already rendered, even if the
27658 * JavaScript objects in the collection have been substituted for new ones. For large collections,
27659 * this signifincantly improves rendering performance. If you don't have a unique identifier,
27660 * `track by $index` can also provide a performance boost.
27663 * <div ng-repeat="model in collection track by model.id">
27668 * When no `track by` expression is provided, it is equivalent to tracking by the built-in
27669 * `$id` function, which tracks items by their identity:
27671 * <div ng-repeat="obj in collection track by $id(obj)">
27676 * <div class="alert alert-warning">
27677 * **Note:** `track by` must always be the last expression:
27680 * <div ng-repeat="model in collection | orderBy: 'id' as filtered_result track by model.id">
27685 * # Special repeat start and end points
27686 * To repeat a series of elements instead of just one parent element, ngRepeat (as well as other ng directives) supports extending
27687 * the range of the repeater by defining explicit start and end points by using **ng-repeat-start** and **ng-repeat-end** respectively.
27688 * 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)
27689 * up to and including the ending HTML tag where **ng-repeat-end** is placed.
27691 * The example below makes use of this feature:
27693 * <header ng-repeat-start="item in items">
27694 * Header {{ item }}
27696 * <div class="body">
27699 * <footer ng-repeat-end>
27700 * Footer {{ item }}
27704 * And with an input of {@type ['A','B']} for the items variable in the example above, the output will evaluate to:
27709 * <div class="body">
27718 * <div class="body">
27726 * The custom start and end points for ngRepeat also support all other HTML directive syntax flavors provided in AngularJS (such
27727 * as **data-ng-repeat-start**, **x-ng-repeat-start** and **ng:repeat-start**).
27730 * **.enter** - when a new item is added to the list or when an item is revealed after a filter
27732 * **.leave** - when an item is removed from the list or when an item is filtered out
27734 * **.move** - when an adjacent item is filtered out causing a reorder or when the item contents are reordered
27739 * @param {repeat_expression} ngRepeat The expression indicating how to enumerate a collection. These
27740 * formats are currently supported:
27742 * * `variable in expression` – where variable is the user defined loop variable and `expression`
27743 * is a scope expression giving the collection to enumerate.
27745 * For example: `album in artist.albums`.
27747 * * `(key, value) in expression` – where `key` and `value` can be any user defined identifiers,
27748 * and `expression` is the scope expression giving the collection to enumerate.
27750 * For example: `(name, age) in {'adam':10, 'amalie':12}`.
27752 * * `variable in expression track by tracking_expression` – You can also provide an optional tracking expression
27753 * which can be used to associate the objects in the collection with the DOM elements. If no tracking expression
27754 * is specified, ng-repeat associates elements by identity. It is an error to have
27755 * more than one tracking expression value resolve to the same key. (This would mean that two distinct objects are
27756 * mapped to the same DOM element, which is not possible.)
27758 * Note that the tracking expression must come last, after any filters, and the alias expression.
27760 * For example: `item in items` is equivalent to `item in items track by $id(item)`. This implies that the DOM elements
27761 * will be associated by item identity in the array.
27763 * For example: `item in items track by $id(item)`. A built in `$id()` function can be used to assign a unique
27764 * `$$hashKey` property to each item in the array. This property is then used as a key to associated DOM elements
27765 * with the corresponding item in the array by identity. Moving the same object in array would move the DOM
27766 * element in the same way in the DOM.
27768 * For example: `item in items track by item.id` is a typical pattern when the items come from the database. In this
27769 * case the object identity does not matter. Two objects are considered equivalent as long as their `id`
27770 * property is same.
27772 * For example: `item in items | filter:searchText track by item.id` is a pattern that might be used to apply a filter
27773 * to items in conjunction with a tracking expression.
27775 * * `variable in expression as alias_expression` – You can also provide an optional alias expression which will then store the
27776 * intermediate results of the repeater after the filters have been applied. Typically this is used to render a special message
27777 * when a filter is active on the repeater, but the filtered result set is empty.
27779 * For example: `item in items | filter:x as results` will store the fragment of the repeated items as `results`, but only after
27780 * the items have been processed through the filter.
27782 * 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
27783 * (and not as operator, inside an expression).
27785 * For example: `item in items | filter : x | orderBy : order | limitTo : limit as results` .
27788 * This example initializes the scope to a list of names and
27789 * then uses `ngRepeat` to display every person:
27790 <example module="ngAnimate" deps="angular-animate.js" animations="true">
27791 <file name="index.html">
27792 <div ng-init="friends = [
27793 {name:'John', age:25, gender:'boy'},
27794 {name:'Jessie', age:30, gender:'girl'},
27795 {name:'Johanna', age:28, gender:'girl'},
27796 {name:'Joy', age:15, gender:'girl'},
27797 {name:'Mary', age:28, gender:'girl'},
27798 {name:'Peter', age:95, gender:'boy'},
27799 {name:'Sebastian', age:50, gender:'boy'},
27800 {name:'Erika', age:27, gender:'girl'},
27801 {name:'Patrick', age:40, gender:'boy'},
27802 {name:'Samantha', age:60, gender:'girl'}
27804 I have {{friends.length}} friends. They are:
27805 <input type="search" ng-model="q" placeholder="filter friends..." aria-label="filter friends" />
27806 <ul class="example-animate-container">
27807 <li class="animate-repeat" ng-repeat="friend in friends | filter:q as results">
27808 [{{$index + 1}}] {{friend.name}} who is {{friend.age}} years old.
27810 <li class="animate-repeat" ng-if="results.length == 0">
27811 <strong>No results found...</strong>
27816 <file name="animations.css">
27817 .example-animate-container {
27819 border:1px solid black;
27828 box-sizing:border-box;
27831 .animate-repeat.ng-move,
27832 .animate-repeat.ng-enter,
27833 .animate-repeat.ng-leave {
27834 transition:all linear 0.5s;
27837 .animate-repeat.ng-leave.ng-leave-active,
27838 .animate-repeat.ng-move,
27839 .animate-repeat.ng-enter {
27844 .animate-repeat.ng-leave,
27845 .animate-repeat.ng-move.ng-move-active,
27846 .animate-repeat.ng-enter.ng-enter-active {
27851 <file name="protractor.js" type="protractor">
27852 var friends = element.all(by.repeater('friend in friends'));
27854 it('should render initial data set', function() {
27855 expect(friends.count()).toBe(10);
27856 expect(friends.get(0).getText()).toEqual('[1] John who is 25 years old.');
27857 expect(friends.get(1).getText()).toEqual('[2] Jessie who is 30 years old.');
27858 expect(friends.last().getText()).toEqual('[10] Samantha who is 60 years old.');
27859 expect(element(by.binding('friends.length')).getText())
27860 .toMatch("I have 10 friends. They are:");
27863 it('should update repeater when filter predicate changes', function() {
27864 expect(friends.count()).toBe(10);
27866 element(by.model('q')).sendKeys('ma');
27868 expect(friends.count()).toBe(2);
27869 expect(friends.get(0).getText()).toEqual('[1] Mary who is 28 years old.');
27870 expect(friends.last().getText()).toEqual('[2] Samantha who is 60 years old.');
27875 var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
27876 var NG_REMOVED = '$$NG_REMOVED';
27877 var ngRepeatMinErr = minErr('ngRepeat');
27879 var updateScope = function(scope, index, valueIdentifier, value, keyIdentifier, key, arrayLength) {
27880 // TODO(perf): generate setters to shave off ~40ms or 1-1.5%
27881 scope[valueIdentifier] = value;
27882 if (keyIdentifier) scope[keyIdentifier] = key;
27883 scope.$index = index;
27884 scope.$first = (index === 0);
27885 scope.$last = (index === (arrayLength - 1));
27886 scope.$middle = !(scope.$first || scope.$last);
27887 // jshint bitwise: false
27888 scope.$odd = !(scope.$even = (index&1) === 0);
27889 // jshint bitwise: true
27892 var getBlockStart = function(block) {
27893 return block.clone[0];
27896 var getBlockEnd = function(block) {
27897 return block.clone[block.clone.length - 1];
27903 multiElement: true,
27904 transclude: 'element',
27908 compile: function ngRepeatCompile($element, $attr) {
27909 var expression = $attr.ngRepeat;
27910 var ngRepeatEndComment = document.createComment(' end ngRepeat: ' + expression + ' ');
27912 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*$/);
27915 throw ngRepeatMinErr('iexp', "Expected expression in form of '_item_ in _collection_[ track by _id_]' but got '{0}'.",
27919 var lhs = match[1];
27920 var rhs = match[2];
27921 var aliasAs = match[3];
27922 var trackByExp = match[4];
27924 match = lhs.match(/^(?:(\s*[\$\w]+)|\(\s*([\$\w]+)\s*,\s*([\$\w]+)\s*\))$/);
27927 throw ngRepeatMinErr('iidexp', "'_item_' in '_item_ in _collection_' should be an identifier or '(_key_, _value_)' expression, but got '{0}'.",
27930 var valueIdentifier = match[3] || match[1];
27931 var keyIdentifier = match[2];
27933 if (aliasAs && (!/^[$a-zA-Z_][$a-zA-Z0-9_]*$/.test(aliasAs) ||
27934 /^(null|undefined|this|\$index|\$first|\$middle|\$last|\$even|\$odd|\$parent|\$root|\$id)$/.test(aliasAs))) {
27935 throw ngRepeatMinErr('badident', "alias '{0}' is invalid --- must be a valid JS identifier which is not a reserved name.",
27939 var trackByExpGetter, trackByIdExpFn, trackByIdArrayFn, trackByIdObjFn;
27940 var hashFnLocals = {$id: hashKey};
27943 trackByExpGetter = $parse(trackByExp);
27945 trackByIdArrayFn = function(key, value) {
27946 return hashKey(value);
27948 trackByIdObjFn = function(key) {
27953 return function ngRepeatLink($scope, $element, $attr, ctrl, $transclude) {
27955 if (trackByExpGetter) {
27956 trackByIdExpFn = function(key, value, index) {
27957 // assign key, value, and $index to the locals so that they can be used in hash functions
27958 if (keyIdentifier) hashFnLocals[keyIdentifier] = key;
27959 hashFnLocals[valueIdentifier] = value;
27960 hashFnLocals.$index = index;
27961 return trackByExpGetter($scope, hashFnLocals);
27965 // Store a list of elements from previous run. This is a hash where key is the item from the
27966 // iterator, and the value is objects with following properties.
27967 // - scope: bound scope
27968 // - element: previous element.
27969 // - index: position
27971 // We are using no-proto object so that we don't need to guard against inherited props via
27973 var lastBlockMap = createMap();
27976 $scope.$watchCollection(rhs, function ngRepeatAction(collection) {
27978 previousNode = $element[0], // node that cloned nodes should be inserted after
27979 // initialized to the comment node anchor
27981 // Same as lastBlockMap but it has the current state. It will become the
27982 // lastBlockMap on the next iteration.
27983 nextBlockMap = createMap(),
27985 key, value, // key/value of iteration
27989 block, // last object information {scope, element, id}
27994 $scope[aliasAs] = collection;
27997 if (isArrayLike(collection)) {
27998 collectionKeys = collection;
27999 trackByIdFn = trackByIdExpFn || trackByIdArrayFn;
28001 trackByIdFn = trackByIdExpFn || trackByIdObjFn;
28002 // if object, extract keys, in enumeration order, unsorted
28003 collectionKeys = [];
28004 for (var itemKey in collection) {
28005 if (hasOwnProperty.call(collection, itemKey) && itemKey.charAt(0) !== '$') {
28006 collectionKeys.push(itemKey);
28011 collectionLength = collectionKeys.length;
28012 nextBlockOrder = new Array(collectionLength);
28014 // locate existing items
28015 for (index = 0; index < collectionLength; index++) {
28016 key = (collection === collectionKeys) ? index : collectionKeys[index];
28017 value = collection[key];
28018 trackById = trackByIdFn(key, value, index);
28019 if (lastBlockMap[trackById]) {
28020 // found previously seen block
28021 block = lastBlockMap[trackById];
28022 delete lastBlockMap[trackById];
28023 nextBlockMap[trackById] = block;
28024 nextBlockOrder[index] = block;
28025 } else if (nextBlockMap[trackById]) {
28026 // if collision detected. restore lastBlockMap and throw an error
28027 forEach(nextBlockOrder, function(block) {
28028 if (block && block.scope) lastBlockMap[block.id] = block;
28030 throw ngRepeatMinErr('dupes',
28031 "Duplicates in a repeater are not allowed. Use 'track by' expression to specify unique keys. Repeater: {0}, Duplicate key: {1}, Duplicate value: {2}",
28032 expression, trackById, value);
28034 // new never before seen block
28035 nextBlockOrder[index] = {id: trackById, scope: undefined, clone: undefined};
28036 nextBlockMap[trackById] = true;
28040 // remove leftover items
28041 for (var blockKey in lastBlockMap) {
28042 block = lastBlockMap[blockKey];
28043 elementsToRemove = getBlockNodes(block.clone);
28044 $animate.leave(elementsToRemove);
28045 if (elementsToRemove[0].parentNode) {
28046 // if the element was not removed yet because of pending animation, mark it as deleted
28047 // so that we can ignore it later
28048 for (index = 0, length = elementsToRemove.length; index < length; index++) {
28049 elementsToRemove[index][NG_REMOVED] = true;
28052 block.scope.$destroy();
28055 // we are not using forEach for perf reasons (trying to avoid #call)
28056 for (index = 0; index < collectionLength; index++) {
28057 key = (collection === collectionKeys) ? index : collectionKeys[index];
28058 value = collection[key];
28059 block = nextBlockOrder[index];
28062 // if we have already seen this object, then we need to reuse the
28063 // associated scope/element
28065 nextNode = previousNode;
28067 // skip nodes that are already pending removal via leave animation
28069 nextNode = nextNode.nextSibling;
28070 } while (nextNode && nextNode[NG_REMOVED]);
28072 if (getBlockStart(block) != nextNode) {
28073 // existing item which got moved
28074 $animate.move(getBlockNodes(block.clone), null, previousNode);
28076 previousNode = getBlockEnd(block);
28077 updateScope(block.scope, index, valueIdentifier, value, keyIdentifier, key, collectionLength);
28079 // new item which we don't know about
28080 $transclude(function ngRepeatTransclude(clone, scope) {
28081 block.scope = scope;
28082 // http://jsperf.com/clone-vs-createcomment
28083 var endNode = ngRepeatEndComment.cloneNode(false);
28084 clone[clone.length++] = endNode;
28086 $animate.enter(clone, null, previousNode);
28087 previousNode = endNode;
28088 // Note: We only need the first/last node of the cloned nodes.
28089 // However, we need to keep the reference to the jqlite wrapper as it might be changed later
28090 // by a directive with templateUrl when its template arrives.
28091 block.clone = clone;
28092 nextBlockMap[block.id] = block;
28093 updateScope(block.scope, index, valueIdentifier, value, keyIdentifier, key, collectionLength);
28097 lastBlockMap = nextBlockMap;
28104 var NG_HIDE_CLASS = 'ng-hide';
28105 var NG_HIDE_IN_PROGRESS_CLASS = 'ng-hide-animate';
28112 * The `ngShow` directive shows or hides the given HTML element based on the expression
28113 * provided to the `ngShow` attribute. The element is shown or hidden by removing or adding
28114 * the `.ng-hide` CSS class onto the element. The `.ng-hide` CSS class is predefined
28115 * in AngularJS and sets the display style to none (using an !important flag).
28116 * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
28119 * <!-- when $scope.myValue is truthy (element is visible) -->
28120 * <div ng-show="myValue"></div>
28122 * <!-- when $scope.myValue is falsy (element is hidden) -->
28123 * <div ng-show="myValue" class="ng-hide"></div>
28126 * When the `ngShow` expression evaluates to a falsy value then the `.ng-hide` CSS class is added to the class
28127 * attribute on the element causing it to become hidden. When truthy, the `.ng-hide` CSS class is removed
28128 * from the element causing the element not to appear hidden.
28130 * ## Why is !important used?
28132 * You may be wondering why !important is used for the `.ng-hide` CSS class. This is because the `.ng-hide` selector
28133 * can be easily overridden by heavier selectors. For example, something as simple
28134 * as changing the display style on a HTML list item would make hidden elements appear visible.
28135 * This also becomes a bigger issue when dealing with CSS frameworks.
28137 * By using !important, the show and hide behavior will work as expected despite any clash between CSS selector
28138 * specificity (when !important isn't used with any conflicting styles). If a developer chooses to override the
28139 * styling to change how to hide an element then it is just a matter of using !important in their own CSS code.
28141 * ### Overriding `.ng-hide`
28143 * By default, the `.ng-hide` class will style the element with `display: none!important`. If you wish to change
28144 * the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide`
28145 * class CSS. Note that the selector that needs to be used is actually `.ng-hide:not(.ng-hide-animate)` to cope
28146 * with extra animation classes that can be added.
28149 * .ng-hide:not(.ng-hide-animate) {
28150 * /* this is just another form of hiding an element */
28151 * display: block!important;
28152 * position: absolute;
28158 * By default you don't need to override in CSS anything and the animations will work around the display style.
28160 * ## A note about animations with `ngShow`
28162 * Animations in ngShow/ngHide work with the show and hide events that are triggered when the directive expression
28163 * is true and false. This system works like the animation system present with ngClass except that
28164 * you must also include the !important flag to override the display property
28165 * so that you can perform an animation when the element is hidden during the time of the animation.
28169 * //a working example can be found at the bottom of this page
28171 * .my-element.ng-hide-add, .my-element.ng-hide-remove {
28172 * /* this is required as of 1.3x to properly
28173 * apply all styling in a show/hide animation */
28174 * transition: 0s linear all;
28177 * .my-element.ng-hide-add-active,
28178 * .my-element.ng-hide-remove-active {
28179 * /* the transition is defined in the active class */
28180 * transition: 1s linear all;
28183 * .my-element.ng-hide-add { ... }
28184 * .my-element.ng-hide-add.ng-hide-add-active { ... }
28185 * .my-element.ng-hide-remove { ... }
28186 * .my-element.ng-hide-remove.ng-hide-remove-active { ... }
28189 * Keep in mind that, as of AngularJS version 1.3.0-beta.11, there is no need to change the display
28190 * property to block during animation states--ngAnimate will handle the style toggling automatically for you.
28193 * addClass: `.ng-hide` - happens after the `ngShow` expression evaluates to a truthy value and the just before contents are set to visible
28194 * removeClass: `.ng-hide` - happens after the `ngShow` expression evaluates to a non truthy value and just before the contents are set to hidden
28197 * @param {expression} ngShow If the {@link guide/expression expression} is truthy
28198 * then the element is shown or hidden respectively.
28201 <example module="ngAnimate" deps="angular-animate.js" animations="true">
28202 <file name="index.html">
28203 Click me: <input type="checkbox" ng-model="checked" aria-label="Toggle ngHide"><br/>
28206 <div class="check-element animate-show" ng-show="checked">
28207 <span class="glyphicon glyphicon-thumbs-up"></span> I show up when your checkbox is checked.
28212 <div class="check-element animate-show" ng-hide="checked">
28213 <span class="glyphicon glyphicon-thumbs-down"></span> I hide when your checkbox is checked.
28217 <file name="glyphicons.css">
28218 @import url(../../components/bootstrap-3.1.1/css/bootstrap.css);
28220 <file name="animations.css">
28225 border: 1px solid black;
28229 .animate-show.ng-hide-add, .animate-show.ng-hide-remove {
28230 transition: all linear 0.5s;
28233 .animate-show.ng-hide {
28241 border: 1px solid black;
28245 <file name="protractor.js" type="protractor">
28246 var thumbsUp = element(by.css('span.glyphicon-thumbs-up'));
28247 var thumbsDown = element(by.css('span.glyphicon-thumbs-down'));
28249 it('should check ng-show / ng-hide', function() {
28250 expect(thumbsUp.isDisplayed()).toBeFalsy();
28251 expect(thumbsDown.isDisplayed()).toBeTruthy();
28253 element(by.model('checked')).click();
28255 expect(thumbsUp.isDisplayed()).toBeTruthy();
28256 expect(thumbsDown.isDisplayed()).toBeFalsy();
28261 var ngShowDirective = ['$animate', function($animate) {
28264 multiElement: true,
28265 link: function(scope, element, attr) {
28266 scope.$watch(attr.ngShow, function ngShowWatchAction(value) {
28267 // we're adding a temporary, animation-specific class for ng-hide since this way
28268 // we can control when the element is actually displayed on screen without having
28269 // to have a global/greedy CSS selector that breaks when other animations are run.
28270 // Read: https://github.com/angular/angular.js/issues/9103#issuecomment-58335845
28271 $animate[value ? 'removeClass' : 'addClass'](element, NG_HIDE_CLASS, {
28272 tempClasses: NG_HIDE_IN_PROGRESS_CLASS
28286 * The `ngHide` directive shows or hides the given HTML element based on the expression
28287 * provided to the `ngHide` attribute. The element is shown or hidden by removing or adding
28288 * the `ng-hide` CSS class onto the element. The `.ng-hide` CSS class is predefined
28289 * in AngularJS and sets the display style to none (using an !important flag).
28290 * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
28293 * <!-- when $scope.myValue is truthy (element is hidden) -->
28294 * <div ng-hide="myValue" class="ng-hide"></div>
28296 * <!-- when $scope.myValue is falsy (element is visible) -->
28297 * <div ng-hide="myValue"></div>
28300 * When the `ngHide` expression evaluates to a truthy value then the `.ng-hide` CSS class is added to the class
28301 * attribute on the element causing it to become hidden. When falsy, the `.ng-hide` CSS class is removed
28302 * from the element causing the element not to appear hidden.
28304 * ## Why is !important used?
28306 * You may be wondering why !important is used for the `.ng-hide` CSS class. This is because the `.ng-hide` selector
28307 * can be easily overridden by heavier selectors. For example, something as simple
28308 * as changing the display style on a HTML list item would make hidden elements appear visible.
28309 * This also becomes a bigger issue when dealing with CSS frameworks.
28311 * By using !important, the show and hide behavior will work as expected despite any clash between CSS selector
28312 * specificity (when !important isn't used with any conflicting styles). If a developer chooses to override the
28313 * styling to change how to hide an element then it is just a matter of using !important in their own CSS code.
28315 * ### Overriding `.ng-hide`
28317 * By default, the `.ng-hide` class will style the element with `display: none!important`. If you wish to change
28318 * the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide`
28323 * /* this is just another form of hiding an element */
28324 * display: block!important;
28325 * position: absolute;
28331 * By default you don't need to override in CSS anything and the animations will work around the display style.
28333 * ## A note about animations with `ngHide`
28335 * Animations in ngShow/ngHide work with the show and hide events that are triggered when the directive expression
28336 * is true and false. This system works like the animation system present with ngClass, except that the `.ng-hide`
28337 * CSS class is added and removed for you instead of your own CSS class.
28341 * //a working example can be found at the bottom of this page
28343 * .my-element.ng-hide-add, .my-element.ng-hide-remove {
28344 * transition: 0.5s linear all;
28347 * .my-element.ng-hide-add { ... }
28348 * .my-element.ng-hide-add.ng-hide-add-active { ... }
28349 * .my-element.ng-hide-remove { ... }
28350 * .my-element.ng-hide-remove.ng-hide-remove-active { ... }
28353 * Keep in mind that, as of AngularJS version 1.3.0-beta.11, there is no need to change the display
28354 * property to block during animation states--ngAnimate will handle the style toggling automatically for you.
28357 * removeClass: `.ng-hide` - happens after the `ngHide` expression evaluates to a truthy value and just before the contents are set to hidden
28358 * addClass: `.ng-hide` - happens after the `ngHide` expression evaluates to a non truthy value and just before the contents are set to visible
28361 * @param {expression} ngHide If the {@link guide/expression expression} is truthy then
28362 * the element is shown or hidden respectively.
28365 <example module="ngAnimate" deps="angular-animate.js" animations="true">
28366 <file name="index.html">
28367 Click me: <input type="checkbox" ng-model="checked" aria-label="Toggle ngShow"><br/>
28370 <div class="check-element animate-hide" ng-show="checked">
28371 <span class="glyphicon glyphicon-thumbs-up"></span> I show up when your checkbox is checked.
28376 <div class="check-element animate-hide" ng-hide="checked">
28377 <span class="glyphicon glyphicon-thumbs-down"></span> I hide when your checkbox is checked.
28381 <file name="glyphicons.css">
28382 @import url(../../components/bootstrap-3.1.1/css/bootstrap.css);
28384 <file name="animations.css">
28386 transition: all linear 0.5s;
28390 border: 1px solid black;
28394 .animate-hide.ng-hide {
28402 border: 1px solid black;
28406 <file name="protractor.js" type="protractor">
28407 var thumbsUp = element(by.css('span.glyphicon-thumbs-up'));
28408 var thumbsDown = element(by.css('span.glyphicon-thumbs-down'));
28410 it('should check ng-show / ng-hide', function() {
28411 expect(thumbsUp.isDisplayed()).toBeFalsy();
28412 expect(thumbsDown.isDisplayed()).toBeTruthy();
28414 element(by.model('checked')).click();
28416 expect(thumbsUp.isDisplayed()).toBeTruthy();
28417 expect(thumbsDown.isDisplayed()).toBeFalsy();
28422 var ngHideDirective = ['$animate', function($animate) {
28425 multiElement: true,
28426 link: function(scope, element, attr) {
28427 scope.$watch(attr.ngHide, function ngHideWatchAction(value) {
28428 // The comment inside of the ngShowDirective explains why we add and
28429 // remove a temporary class for the show/hide animation
28430 $animate[value ? 'addClass' : 'removeClass'](element,NG_HIDE_CLASS, {
28431 tempClasses: NG_HIDE_IN_PROGRESS_CLASS
28444 * The `ngStyle` directive allows you to set CSS style on an HTML element conditionally.
28447 * @param {expression} ngStyle
28449 * {@link guide/expression Expression} which evals to an
28450 * object whose keys are CSS style names and values are corresponding values for those CSS
28453 * Since some CSS style names are not valid keys for an object, they must be quoted.
28454 * See the 'background-color' style in the example below.
28458 <file name="index.html">
28459 <input type="button" value="set color" ng-click="myStyle={color:'red'}">
28460 <input type="button" value="set background" ng-click="myStyle={'background-color':'blue'}">
28461 <input type="button" value="clear" ng-click="myStyle={}">
28463 <span ng-style="myStyle">Sample Text</span>
28464 <pre>myStyle={{myStyle}}</pre>
28466 <file name="style.css">
28471 <file name="protractor.js" type="protractor">
28472 var colorSpan = element(by.css('span'));
28474 it('should check ng-style', function() {
28475 expect(colorSpan.getCssValue('color')).toBe('rgba(0, 0, 0, 1)');
28476 element(by.css('input[value=\'set color\']')).click();
28477 expect(colorSpan.getCssValue('color')).toBe('rgba(255, 0, 0, 1)');
28478 element(by.css('input[value=clear]')).click();
28479 expect(colorSpan.getCssValue('color')).toBe('rgba(0, 0, 0, 1)');
28484 var ngStyleDirective = ngDirective(function(scope, element, attr) {
28485 scope.$watch(attr.ngStyle, function ngStyleWatchAction(newStyles, oldStyles) {
28486 if (oldStyles && (newStyles !== oldStyles)) {
28487 forEach(oldStyles, function(val, style) { element.css(style, '');});
28489 if (newStyles) element.css(newStyles);
28499 * The `ngSwitch` directive is used to conditionally swap DOM structure on your template based on a scope expression.
28500 * Elements within `ngSwitch` but without `ngSwitchWhen` or `ngSwitchDefault` directives will be preserved at the location
28501 * as specified in the template.
28503 * The directive itself works similar to ngInclude, however, instead of downloading template code (or loading it
28504 * from the template cache), `ngSwitch` simply chooses one of the nested elements and makes it visible based on which element
28505 * matches the value obtained from the evaluated expression. In other words, you define a container element
28506 * (where you place the directive), place an expression on the **`on="..."` attribute**
28507 * (or the **`ng-switch="..."` attribute**), define any inner elements inside of the directive and place
28508 * a when attribute per element. The when attribute is used to inform ngSwitch which element to display when the on
28509 * expression is evaluated. If a matching expression is not found via a when attribute then an element with the default
28510 * attribute is displayed.
28512 * <div class="alert alert-info">
28513 * Be aware that the attribute values to match against cannot be expressions. They are interpreted
28514 * as literal string values to match against.
28515 * For example, **`ng-switch-when="someVal"`** will match against the string `"someVal"` not against the
28516 * value of the expression `$scope.someVal`.
28520 * enter - happens after the ngSwitch contents change and the matched child element is placed inside the container
28521 * leave - happens just after the ngSwitch contents change and just before the former contents are removed from the DOM
28526 * <ANY ng-switch="expression">
28527 * <ANY ng-switch-when="matchValue1">...</ANY>
28528 * <ANY ng-switch-when="matchValue2">...</ANY>
28529 * <ANY ng-switch-default>...</ANY>
28536 * @param {*} ngSwitch|on expression to match against <code>ng-switch-when</code>.
28537 * On child elements add:
28539 * * `ngSwitchWhen`: the case statement to match against. If match then this
28540 * case will be displayed. If the same match appears multiple times, all the
28541 * elements will be displayed.
28542 * * `ngSwitchDefault`: the default case when no other case match. If there
28543 * are multiple default cases, all of them will be displayed when no other
28548 <example module="switchExample" deps="angular-animate.js" animations="true">
28549 <file name="index.html">
28550 <div ng-controller="ExampleController">
28551 <select ng-model="selection" ng-options="item for item in items">
28553 <code>selection={{selection}}</code>
28555 <div class="animate-switch-container"
28556 ng-switch on="selection">
28557 <div class="animate-switch" ng-switch-when="settings">Settings Div</div>
28558 <div class="animate-switch" ng-switch-when="home">Home Span</div>
28559 <div class="animate-switch" ng-switch-default>default</div>
28563 <file name="script.js">
28564 angular.module('switchExample', ['ngAnimate'])
28565 .controller('ExampleController', ['$scope', function($scope) {
28566 $scope.items = ['settings', 'home', 'other'];
28567 $scope.selection = $scope.items[0];
28570 <file name="animations.css">
28571 .animate-switch-container {
28574 border:1px solid black;
28583 .animate-switch.ng-animate {
28584 transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
28593 .animate-switch.ng-leave.ng-leave-active,
28594 .animate-switch.ng-enter {
28597 .animate-switch.ng-leave,
28598 .animate-switch.ng-enter.ng-enter-active {
28602 <file name="protractor.js" type="protractor">
28603 var switchElem = element(by.css('[ng-switch]'));
28604 var select = element(by.model('selection'));
28606 it('should start in settings', function() {
28607 expect(switchElem.getText()).toMatch(/Settings Div/);
28609 it('should change to home', function() {
28610 select.all(by.css('option')).get(1).click();
28611 expect(switchElem.getText()).toMatch(/Home Span/);
28613 it('should select default', function() {
28614 select.all(by.css('option')).get(2).click();
28615 expect(switchElem.getText()).toMatch(/default/);
28620 var ngSwitchDirective = ['$animate', function($animate) {
28622 require: 'ngSwitch',
28624 // asks for $scope to fool the BC controller module
28625 controller: ['$scope', function ngSwitchController() {
28628 link: function(scope, element, attr, ngSwitchController) {
28629 var watchExpr = attr.ngSwitch || attr.on,
28630 selectedTranscludes = [],
28631 selectedElements = [],
28632 previousLeaveAnimations = [],
28633 selectedScopes = [];
28635 var spliceFactory = function(array, index) {
28636 return function() { array.splice(index, 1); };
28639 scope.$watch(watchExpr, function ngSwitchWatchAction(value) {
28641 for (i = 0, ii = previousLeaveAnimations.length; i < ii; ++i) {
28642 $animate.cancel(previousLeaveAnimations[i]);
28644 previousLeaveAnimations.length = 0;
28646 for (i = 0, ii = selectedScopes.length; i < ii; ++i) {
28647 var selected = getBlockNodes(selectedElements[i].clone);
28648 selectedScopes[i].$destroy();
28649 var promise = previousLeaveAnimations[i] = $animate.leave(selected);
28650 promise.then(spliceFactory(previousLeaveAnimations, i));
28653 selectedElements.length = 0;
28654 selectedScopes.length = 0;
28656 if ((selectedTranscludes = ngSwitchController.cases['!' + value] || ngSwitchController.cases['?'])) {
28657 forEach(selectedTranscludes, function(selectedTransclude) {
28658 selectedTransclude.transclude(function(caseElement, selectedScope) {
28659 selectedScopes.push(selectedScope);
28660 var anchor = selectedTransclude.element;
28661 caseElement[caseElement.length++] = document.createComment(' end ngSwitchWhen: ');
28662 var block = { clone: caseElement };
28664 selectedElements.push(block);
28665 $animate.enter(caseElement, anchor.parent(), anchor);
28674 var ngSwitchWhenDirective = ngDirective({
28675 transclude: 'element',
28677 require: '^ngSwitch',
28678 multiElement: true,
28679 link: function(scope, element, attrs, ctrl, $transclude) {
28680 ctrl.cases['!' + attrs.ngSwitchWhen] = (ctrl.cases['!' + attrs.ngSwitchWhen] || []);
28681 ctrl.cases['!' + attrs.ngSwitchWhen].push({ transclude: $transclude, element: element });
28685 var ngSwitchDefaultDirective = ngDirective({
28686 transclude: 'element',
28688 require: '^ngSwitch',
28689 multiElement: true,
28690 link: function(scope, element, attr, ctrl, $transclude) {
28691 ctrl.cases['?'] = (ctrl.cases['?'] || []);
28692 ctrl.cases['?'].push({ transclude: $transclude, element: element });
28698 * @name ngTransclude
28702 * Directive that marks the insertion point for the transcluded DOM of the nearest parent directive that uses transclusion.
28704 * Any existing content of the element that this directive is placed on will be removed before the transcluded content is inserted.
28709 <example module="transcludeExample">
28710 <file name="index.html">
28712 angular.module('transcludeExample', [])
28713 .directive('pane', function(){
28717 scope: { title:'@' },
28718 template: '<div style="border: 1px solid black;">' +
28719 '<div style="background-color: gray">{{title}}</div>' +
28720 '<ng-transclude></ng-transclude>' +
28724 .controller('ExampleController', ['$scope', function($scope) {
28725 $scope.title = 'Lorem Ipsum';
28726 $scope.text = 'Neque porro quisquam est qui dolorem ipsum quia dolor...';
28729 <div ng-controller="ExampleController">
28730 <input ng-model="title" aria-label="title"> <br/>
28731 <textarea ng-model="text" aria-label="text"></textarea> <br/>
28732 <pane title="{{title}}">{{text}}</pane>
28735 <file name="protractor.js" type="protractor">
28736 it('should have transcluded', function() {
28737 var titleElement = element(by.model('title'));
28738 titleElement.clear();
28739 titleElement.sendKeys('TITLE');
28740 var textElement = element(by.model('text'));
28741 textElement.clear();
28742 textElement.sendKeys('TEXT');
28743 expect(element(by.binding('title')).getText()).toEqual('TITLE');
28744 expect(element(by.binding('text')).getText()).toEqual('TEXT');
28750 var ngTranscludeDirective = ngDirective({
28752 link: function($scope, $element, $attrs, controller, $transclude) {
28753 if (!$transclude) {
28754 throw minErr('ngTransclude')('orphan',
28755 'Illegal use of ngTransclude directive in the template! ' +
28756 'No parent directive that requires a transclusion found. ' +
28758 startingTag($element));
28761 $transclude(function(clone) {
28763 $element.append(clone);
28774 * Load the content of a `<script>` element into {@link ng.$templateCache `$templateCache`}, so that the
28775 * template can be used by {@link ng.directive:ngInclude `ngInclude`},
28776 * {@link ngRoute.directive:ngView `ngView`}, or {@link guide/directive directives}. The type of the
28777 * `<script>` element must be specified as `text/ng-template`, and a cache name for the template must be
28778 * assigned through the element's `id`, which can then be used as a directive's `templateUrl`.
28780 * @param {string} type Must be set to `'text/ng-template'`.
28781 * @param {string} id Cache name of the template.
28785 <file name="index.html">
28786 <script type="text/ng-template" id="/tpl.html">
28787 Content of the template.
28790 <a ng-click="currentTpl='/tpl.html'" id="tpl-link">Load inlined template</a>
28791 <div id="tpl-content" ng-include src="currentTpl"></div>
28793 <file name="protractor.js" type="protractor">
28794 it('should load template defined inside script tag', function() {
28795 element(by.css('#tpl-link')).click();
28796 expect(element(by.css('#tpl-content')).getText()).toMatch(/Content of the template/);
28801 var scriptDirective = ['$templateCache', function($templateCache) {
28805 compile: function(element, attr) {
28806 if (attr.type == 'text/ng-template') {
28807 var templateUrl = attr.id,
28808 text = element[0].text;
28810 $templateCache.put(templateUrl, text);
28816 var noopNgModelController = { $setViewValue: noop, $render: noop };
28818 function chromeHack(optionElement) {
28819 // Workaround for https://code.google.com/p/chromium/issues/detail?id=381459
28820 // Adding an <option selected="selected"> element to a <select required="required"> should
28821 // automatically select the new element
28822 if (optionElement[0].hasAttribute('selected')) {
28823 optionElement[0].selected = true;
28829 * @name select.SelectController
28831 * The controller for the `<select>` directive. This provides support for reading
28832 * and writing the selected value(s) of the control and also coordinates dynamically
28833 * added `<option>` elements, perhaps by an `ngRepeat` directive.
28835 var SelectController =
28836 ['$element', '$scope', '$attrs', function($element, $scope, $attrs) {
28839 optionsMap = new HashMap();
28841 // If the ngModel doesn't get provided then provide a dummy noop version to prevent errors
28842 self.ngModelCtrl = noopNgModelController;
28844 // The "unknown" option is one that is prepended to the list if the viewValue
28845 // does not match any of the options. When it is rendered the value of the unknown
28846 // option is '? XXX ?' where XXX is the hashKey of the value that is not known.
28848 // We can't just jqLite('<option>') since jqLite is not smart enough
28849 // to create it in <select> and IE barfs otherwise.
28850 self.unknownOption = jqLite(document.createElement('option'));
28851 self.renderUnknownOption = function(val) {
28852 var unknownVal = '? ' + hashKey(val) + ' ?';
28853 self.unknownOption.val(unknownVal);
28854 $element.prepend(self.unknownOption);
28855 $element.val(unknownVal);
28858 $scope.$on('$destroy', function() {
28859 // disable unknown option so that we don't do work when the whole select is being destroyed
28860 self.renderUnknownOption = noop;
28863 self.removeUnknownOption = function() {
28864 if (self.unknownOption.parent()) self.unknownOption.remove();
28868 // Read the value of the select control, the implementation of this changes depending
28869 // upon whether the select can have multiple values and whether ngOptions is at work.
28870 self.readValue = function readSingleValue() {
28871 self.removeUnknownOption();
28872 return $element.val();
28876 // Write the value to the select control, the implementation of this changes depending
28877 // upon whether the select can have multiple values and whether ngOptions is at work.
28878 self.writeValue = function writeSingleValue(value) {
28879 if (self.hasOption(value)) {
28880 self.removeUnknownOption();
28881 $element.val(value);
28882 if (value === '') self.emptyOption.prop('selected', true); // to make IE9 happy
28884 if (value == null && self.emptyOption) {
28885 self.removeUnknownOption();
28888 self.renderUnknownOption(value);
28894 // Tell the select control that an option, with the given value, has been added
28895 self.addOption = function(value, element) {
28896 // Skip comment nodes, as they only pollute the `optionsMap`
28897 if (element[0].nodeType === NODE_TYPE_COMMENT) return;
28899 assertNotHasOwnProperty(value, '"option value"');
28900 if (value === '') {
28901 self.emptyOption = element;
28903 var count = optionsMap.get(value) || 0;
28904 optionsMap.put(value, count + 1);
28905 self.ngModelCtrl.$render();
28906 chromeHack(element);
28909 // Tell the select control that an option, with the given value, has been removed
28910 self.removeOption = function(value) {
28911 var count = optionsMap.get(value);
28914 optionsMap.remove(value);
28915 if (value === '') {
28916 self.emptyOption = undefined;
28919 optionsMap.put(value, count - 1);
28924 // Check whether the select control has an option matching the given value
28925 self.hasOption = function(value) {
28926 return !!optionsMap.get(value);
28930 self.registerOption = function(optionScope, optionElement, optionAttrs, interpolateValueFn, interpolateTextFn) {
28932 if (interpolateValueFn) {
28933 // The value attribute is interpolated
28935 optionAttrs.$observe('value', function valueAttributeObserveAction(newVal) {
28936 if (isDefined(oldVal)) {
28937 self.removeOption(oldVal);
28940 self.addOption(newVal, optionElement);
28942 } else if (interpolateTextFn) {
28943 // The text content is interpolated
28944 optionScope.$watch(interpolateTextFn, function interpolateWatchAction(newVal, oldVal) {
28945 optionAttrs.$set('value', newVal);
28946 if (oldVal !== newVal) {
28947 self.removeOption(oldVal);
28949 self.addOption(newVal, optionElement);
28952 // The value attribute is static
28953 self.addOption(optionAttrs.value, optionElement);
28956 optionElement.on('$destroy', function() {
28957 self.removeOption(optionAttrs.value);
28958 self.ngModelCtrl.$render();
28969 * HTML `SELECT` element with angular data-binding.
28971 * The `select` directive is used together with {@link ngModel `ngModel`} to provide data-binding
28972 * between the scope and the `<select>` control (including setting default values).
28973 * It also handles dynamic `<option>` elements, which can be added using the {@link ngRepeat `ngRepeat}` or
28974 * {@link ngOptions `ngOptions`} directives.
28976 * When an item in the `<select>` menu is selected, the value of the selected option will be bound
28977 * to the model identified by the `ngModel` directive. With static or repeated options, this is
28978 * the content of the `value` attribute or the textContent of the `<option>`, if the value attribute is missing.
28979 * If you want dynamic value attributes, you can use interpolation inside the value attribute.
28981 * <div class="alert alert-warning">
28982 * Note that the value of a `select` directive used without `ngOptions` is always a string.
28983 * When the model needs to be bound to a non-string value, you must either explictly convert it
28984 * using a directive (see example below) or use `ngOptions` to specify the set of options.
28985 * This is because an option element can only be bound to string values at present.
28988 * If the viewValue of `ngModel` does not match any of the options, then the control
28989 * will automatically add an "unknown" option, which it then removes when the mismatch is resolved.
28991 * Optionally, a single hard-coded `<option>` element, with the value set to an empty string, can
28992 * be nested into the `<select>` element. This element will then represent the `null` or "not selected"
28993 * option. See example below for demonstration.
28995 * <div class="alert alert-info">
28996 * In many cases, `ngRepeat` can be used on `<option>` elements instead of {@link ng.directive:ngOptions
28997 * ngOptions} to achieve a similar result. However, `ngOptions` provides some benefits, such as
28998 * more flexibility in how the `<select>`'s model is assigned via the `select` **`as`** part of the
28999 * comprehension expression, and additionally in reducing memory and increasing speed by not creating
29000 * a new scope for each repeated instance.
29004 * @param {string} ngModel Assignable angular expression to data-bind to.
29005 * @param {string=} name Property name of the form under which the control is published.
29006 * @param {string=} multiple Allows multiple options to be selected. The selected values will be
29007 * bound to the model as an array.
29008 * @param {string=} required Sets `required` validation error key if the value is not entered.
29009 * @param {string=} ngRequired Adds required attribute and required validation constraint to
29010 * the element when the ngRequired expression evaluates to true. Use ngRequired instead of required
29011 * when you want to data-bind to the required attribute.
29012 * @param {string=} ngChange Angular expression to be executed when selected option(s) changes due to user
29013 * interaction with the select element.
29014 * @param {string=} ngOptions sets the options that the select is populated with and defines what is
29015 * set on the model on selection. See {@link ngOptions `ngOptions`}.
29018 * ### Simple `select` elements with static options
29020 * <example name="static-select" module="staticSelect">
29021 * <file name="index.html">
29022 * <div ng-controller="ExampleController">
29023 * <form name="myForm">
29024 * <label for="singleSelect"> Single select: </label><br>
29025 * <select name="singleSelect" ng-model="data.singleSelect">
29026 * <option value="option-1">Option 1</option>
29027 * <option value="option-2">Option 2</option>
29030 * <label for="singleSelect"> Single select with "not selected" option and dynamic option values: </label><br>
29031 * <select name="singleSelect" id="singleSelect" ng-model="data.singleSelect">
29032 * <option value="">---Please select---</option> <!-- not selected / blank option -->
29033 * <option value="{{data.option1}}">Option 1</option> <!-- interpolation -->
29034 * <option value="option-2">Option 2</option>
29036 * <button ng-click="forceUnknownOption()">Force unknown option</button><br>
29037 * <tt>singleSelect = {{data.singleSelect}}</tt>
29040 * <label for="multipleSelect"> Multiple select: </label><br>
29041 * <select name="multipleSelect" id="multipleSelect" ng-model="data.multipleSelect" multiple>
29042 * <option value="option-1">Option 1</option>
29043 * <option value="option-2">Option 2</option>
29044 * <option value="option-3">Option 3</option>
29046 * <tt>multipleSelect = {{data.multipleSelect}}</tt><br/>
29050 * <file name="app.js">
29051 * angular.module('staticSelect', [])
29052 * .controller('ExampleController', ['$scope', function($scope) {
29054 * singleSelect: null,
29055 * multipleSelect: [],
29056 * option1: 'option-1',
29059 * $scope.forceUnknownOption = function() {
29060 * $scope.data.singleSelect = 'nonsense';
29066 * ### Using `ngRepeat` to generate `select` options
29067 * <example name="ngrepeat-select" module="ngrepeatSelect">
29068 * <file name="index.html">
29069 * <div ng-controller="ExampleController">
29070 * <form name="myForm">
29071 * <label for="repeatSelect"> Repeat select: </label>
29072 * <select name="repeatSelect" id="repeatSelect" ng-model="data.repeatSelect">
29073 * <option ng-repeat="option in data.availableOptions" value="{{option.id}}">{{option.name}}</option>
29077 * <tt>repeatSelect = {{data.repeatSelect}}</tt><br/>
29080 * <file name="app.js">
29081 * angular.module('ngrepeatSelect', [])
29082 * .controller('ExampleController', ['$scope', function($scope) {
29084 * repeatSelect: null,
29085 * availableOptions: [
29086 * {id: '1', name: 'Option A'},
29087 * {id: '2', name: 'Option B'},
29088 * {id: '3', name: 'Option C'}
29096 * ### Using `select` with `ngOptions` and setting a default value
29097 * See the {@link ngOptions ngOptions documentation} for more `ngOptions` usage examples.
29099 * <example name="select-with-default-values" module="defaultValueSelect">
29100 * <file name="index.html">
29101 * <div ng-controller="ExampleController">
29102 * <form name="myForm">
29103 * <label for="mySelect">Make a choice:</label>
29104 * <select name="mySelect" id="mySelect"
29105 * ng-options="option.name for option in data.availableOptions track by option.id"
29106 * ng-model="data.selectedOption"></select>
29109 * <tt>option = {{data.selectedOption}}</tt><br/>
29112 * <file name="app.js">
29113 * angular.module('defaultValueSelect', [])
29114 * .controller('ExampleController', ['$scope', function($scope) {
29116 * availableOptions: [
29117 * {id: '1', name: 'Option A'},
29118 * {id: '2', name: 'Option B'},
29119 * {id: '3', name: 'Option C'}
29121 * selectedOption: {id: '3', name: 'Option C'} //This sets the default value of the select in the ui
29128 * ### Binding `select` to a non-string value via `ngModel` parsing / formatting
29130 * <example name="select-with-non-string-options" module="nonStringSelect">
29131 * <file name="index.html">
29132 * <select ng-model="model.id" convert-to-number>
29133 * <option value="0">Zero</option>
29134 * <option value="1">One</option>
29135 * <option value="2">Two</option>
29139 * <file name="app.js">
29140 * angular.module('nonStringSelect', [])
29141 * .run(function($rootScope) {
29142 * $rootScope.model = { id: 2 };
29144 * .directive('convertToNumber', function() {
29146 * require: 'ngModel',
29147 * link: function(scope, element, attrs, ngModel) {
29148 * ngModel.$parsers.push(function(val) {
29149 * return parseInt(val, 10);
29151 * ngModel.$formatters.push(function(val) {
29158 * <file name="protractor.js" type="protractor">
29159 * it('should initialize to model', function() {
29160 * var select = element(by.css('select'));
29161 * expect(element(by.model('model.id')).$('option:checked').getText()).toEqual('Two');
29167 var selectDirective = function() {
29171 require: ['select', '?ngModel'],
29172 controller: SelectController,
29175 pre: selectPreLink,
29176 post: selectPostLink
29180 function selectPreLink(scope, element, attr, ctrls) {
29182 // if ngModel is not defined, we don't need to do anything
29183 var ngModelCtrl = ctrls[1];
29184 if (!ngModelCtrl) return;
29186 var selectCtrl = ctrls[0];
29188 selectCtrl.ngModelCtrl = ngModelCtrl;
29190 // When the selected item(s) changes we delegate getting the value of the select control
29191 // to the `readValue` method, which can be changed if the select can have multiple
29192 // selected values or if the options are being generated by `ngOptions`
29193 element.on('change', function() {
29194 scope.$apply(function() {
29195 ngModelCtrl.$setViewValue(selectCtrl.readValue());
29199 // If the select allows multiple values then we need to modify how we read and write
29200 // values from and to the control; also what it means for the value to be empty and
29201 // we have to add an extra watch since ngModel doesn't work well with arrays - it
29202 // doesn't trigger rendering if only an item in the array changes.
29203 if (attr.multiple) {
29205 // Read value now needs to check each option to see if it is selected
29206 selectCtrl.readValue = function readMultipleValue() {
29208 forEach(element.find('option'), function(option) {
29209 if (option.selected) {
29210 array.push(option.value);
29216 // Write value now needs to set the selected property of each matching option
29217 selectCtrl.writeValue = function writeMultipleValue(value) {
29218 var items = new HashMap(value);
29219 forEach(element.find('option'), function(option) {
29220 option.selected = isDefined(items.get(option.value));
29224 // we have to do it on each watch since ngModel watches reference, but
29225 // we need to work of an array, so we need to see if anything was inserted/removed
29226 var lastView, lastViewRef = NaN;
29227 scope.$watch(function selectMultipleWatch() {
29228 if (lastViewRef === ngModelCtrl.$viewValue && !equals(lastView, ngModelCtrl.$viewValue)) {
29229 lastView = shallowCopy(ngModelCtrl.$viewValue);
29230 ngModelCtrl.$render();
29232 lastViewRef = ngModelCtrl.$viewValue;
29235 // If we are a multiple select then value is now a collection
29236 // so the meaning of $isEmpty changes
29237 ngModelCtrl.$isEmpty = function(value) {
29238 return !value || value.length === 0;
29244 function selectPostLink(scope, element, attrs, ctrls) {
29245 // if ngModel is not defined, we don't need to do anything
29246 var ngModelCtrl = ctrls[1];
29247 if (!ngModelCtrl) return;
29249 var selectCtrl = ctrls[0];
29251 // We delegate rendering to the `writeValue` method, which can be changed
29252 // if the select can have multiple selected values or if the options are being
29253 // generated by `ngOptions`.
29254 // This must be done in the postLink fn to prevent $render to be called before
29255 // all nodes have been linked correctly.
29256 ngModelCtrl.$render = function() {
29257 selectCtrl.writeValue(ngModelCtrl.$viewValue);
29263 // The option directive is purely designed to communicate the existence (or lack of)
29264 // of dynamically created (and destroyed) option elements to their containing select
29265 // directive via its controller.
29266 var optionDirective = ['$interpolate', function($interpolate) {
29270 compile: function(element, attr) {
29271 if (isDefined(attr.value)) {
29272 // If the value attribute is defined, check if it contains an interpolation
29273 var interpolateValueFn = $interpolate(attr.value, true);
29275 // If the value attribute is not defined then we fall back to the
29276 // text content of the option element, which may be interpolated
29277 var interpolateTextFn = $interpolate(element.text(), true);
29278 if (!interpolateTextFn) {
29279 attr.$set('value', element.text());
29283 return function(scope, element, attr) {
29284 // This is an optimization over using ^^ since we don't want to have to search
29285 // all the way to the root of the DOM for every single option element
29286 var selectCtrlName = '$selectController',
29287 parent = element.parent(),
29288 selectCtrl = parent.data(selectCtrlName) ||
29289 parent.parent().data(selectCtrlName); // in case we are in optgroup
29292 selectCtrl.registerOption(scope, element, attr, interpolateValueFn, interpolateTextFn);
29299 var styleDirective = valueFn({
29310 * ngRequired adds the required {@link ngModel.NgModelController#$validators `validator`} to {@link ngModel `ngModel`}.
29311 * It is most often used for {@link input `input`} and {@link select `select`} controls, but can also be
29312 * applied to custom controls.
29314 * The directive sets the `required` attribute on the element if the Angular expression inside
29315 * `ngRequired` evaluates to true. A special directive for setting `required` is necessary because we
29316 * cannot use interpolation inside `required`. See the {@link guide/interpolation interpolation guide}
29319 * The validator will set the `required` error key to true if the `required` attribute is set and
29320 * calling {@link ngModel.NgModelController#$isEmpty `NgModelController.$isEmpty`} with the
29321 * {@link ngModel.NgModelController#$viewValue `ngModel.$viewValue`} returns `true`. For example, the
29322 * `$isEmpty()` implementation for `input[text]` checks the length of the `$viewValue`. When developing
29323 * custom controls, `$isEmpty()` can be overwritten to account for a $viewValue that is not string-based.
29326 * <example name="ngRequiredDirective" module="ngRequiredExample">
29327 * <file name="index.html">
29329 * angular.module('ngRequiredExample', [])
29330 * .controller('ExampleController', ['$scope', function($scope) {
29331 * $scope.required = true;
29334 * <div ng-controller="ExampleController">
29335 * <form name="form">
29336 * <label for="required">Toggle required: </label>
29337 * <input type="checkbox" ng-model="required" id="required" />
29339 * <label for="input">This input must be filled if `required` is true: </label>
29340 * <input type="text" ng-model="model" id="input" name="input" ng-required="required" /><br>
29342 * required error set? = <code>{{form.input.$error.required}}</code><br>
29343 * model = <code>{{model}}</code>
29347 * <file name="protractor.js" type="protractor">
29348 var required = element(by.binding('form.input.$error.required'));
29349 var model = element(by.binding('model'));
29350 var input = element(by.id('input'));
29352 it('should set the required error', function() {
29353 expect(required.getText()).toContain('true');
29355 input.sendKeys('123');
29356 expect(required.getText()).not.toContain('true');
29357 expect(model.getText()).toContain('123');
29362 var requiredDirective = function() {
29365 require: '?ngModel',
29366 link: function(scope, elm, attr, ctrl) {
29368 attr.required = true; // force truthy in case we are on non input element
29370 ctrl.$validators.required = function(modelValue, viewValue) {
29371 return !attr.required || !ctrl.$isEmpty(viewValue);
29374 attr.$observe('required', function() {
29387 * ngPattern adds the pattern {@link ngModel.NgModelController#$validators `validator`} to {@link ngModel `ngModel`}.
29388 * It is most often used for text-based {@link input `input`} controls, but can also be applied to custom text-based controls.
29390 * The validator sets the `pattern` error key if the {@link ngModel.NgModelController#$viewValue `ngModel.$viewValue`}
29391 * does not match a RegExp which is obtained by evaluating the Angular expression given in the
29392 * `ngPattern` attribute value:
29393 * * If the expression evaluates to a RegExp object, then this is used directly.
29394 * * If the expression evaluates to a string, then it will be converted to a RegExp after wrapping it
29395 * in `^` and `$` characters. For instance, `"abc"` will be converted to `new RegExp('^abc$')`.
29397 * <div class="alert alert-info">
29398 * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
29399 * start at the index of the last search's match, thus not taking the whole input value into
29403 * <div class="alert alert-info">
29404 * **Note:** This directive is also added when the plain `pattern` attribute is used, with two
29408 * `ngPattern` does not set the `pattern` attribute and therefore HTML5 constraint validation is
29412 * The `ngPattern` attribute must be an expression, while the `pattern` value must be
29419 * <example name="ngPatternDirective" module="ngPatternExample">
29420 * <file name="index.html">
29422 * angular.module('ngPatternExample', [])
29423 * .controller('ExampleController', ['$scope', function($scope) {
29424 * $scope.regex = '\\d+';
29427 * <div ng-controller="ExampleController">
29428 * <form name="form">
29429 * <label for="regex">Set a pattern (regex string): </label>
29430 * <input type="text" ng-model="regex" id="regex" />
29432 * <label for="input">This input is restricted by the current pattern: </label>
29433 * <input type="text" ng-model="model" id="input" name="input" ng-pattern="regex" /><br>
29435 * input valid? = <code>{{form.input.$valid}}</code><br>
29436 * model = <code>{{model}}</code>
29440 * <file name="protractor.js" type="protractor">
29441 var model = element(by.binding('model'));
29442 var input = element(by.id('input'));
29444 it('should validate the input with the default pattern', function() {
29445 input.sendKeys('aaa');
29446 expect(model.getText()).not.toContain('aaa');
29448 input.clear().then(function() {
29449 input.sendKeys('123');
29450 expect(model.getText()).toContain('123');
29456 var patternDirective = function() {
29459 require: '?ngModel',
29460 link: function(scope, elm, attr, ctrl) {
29463 var regexp, patternExp = attr.ngPattern || attr.pattern;
29464 attr.$observe('pattern', function(regex) {
29465 if (isString(regex) && regex.length > 0) {
29466 regex = new RegExp('^' + regex + '$');
29469 if (regex && !regex.test) {
29470 throw minErr('ngPattern')('noregexp',
29471 'Expected {0} to be a RegExp but was {1}. Element: {2}', patternExp,
29472 regex, startingTag(elm));
29475 regexp = regex || undefined;
29479 ctrl.$validators.pattern = function(modelValue, viewValue) {
29480 // HTML5 pattern constraint validates the input value, so we validate the viewValue
29481 return ctrl.$isEmpty(viewValue) || isUndefined(regexp) || regexp.test(viewValue);
29489 * @name ngMaxlength
29493 * ngMaxlength adds the maxlength {@link ngModel.NgModelController#$validators `validator`} to {@link ngModel `ngModel`}.
29494 * It is most often used for text-based {@link input `input`} controls, but can also be applied to custom text-based controls.
29496 * The validator sets the `maxlength` error key if the {@link ngModel.NgModelController#$viewValue `ngModel.$viewValue`}
29497 * is longer than the integer obtained by evaluating the Angular expression given in the
29498 * `ngMaxlength` attribute value.
29500 * <div class="alert alert-info">
29501 * **Note:** This directive is also added when the plain `maxlength` attribute is used, with two
29505 * `ngMaxlength` does not set the `maxlength` attribute and therefore HTML5 constraint
29506 * validation is not available.
29509 * The `ngMaxlength` attribute must be an expression, while the `maxlength` value must be
29516 * <example name="ngMaxlengthDirective" module="ngMaxlengthExample">
29517 * <file name="index.html">
29519 * angular.module('ngMaxlengthExample', [])
29520 * .controller('ExampleController', ['$scope', function($scope) {
29521 * $scope.maxlength = 5;
29524 * <div ng-controller="ExampleController">
29525 * <form name="form">
29526 * <label for="maxlength">Set a maxlength: </label>
29527 * <input type="number" ng-model="maxlength" id="maxlength" />
29529 * <label for="input">This input is restricted by the current maxlength: </label>
29530 * <input type="text" ng-model="model" id="input" name="input" ng-maxlength="maxlength" /><br>
29532 * input valid? = <code>{{form.input.$valid}}</code><br>
29533 * model = <code>{{model}}</code>
29537 * <file name="protractor.js" type="protractor">
29538 var model = element(by.binding('model'));
29539 var input = element(by.id('input'));
29541 it('should validate the input with the default maxlength', function() {
29542 input.sendKeys('abcdef');
29543 expect(model.getText()).not.toContain('abcdef');
29545 input.clear().then(function() {
29546 input.sendKeys('abcde');
29547 expect(model.getText()).toContain('abcde');
29553 var maxlengthDirective = function() {
29556 require: '?ngModel',
29557 link: function(scope, elm, attr, ctrl) {
29560 var maxlength = -1;
29561 attr.$observe('maxlength', function(value) {
29562 var intVal = toInt(value);
29563 maxlength = isNaN(intVal) ? -1 : intVal;
29566 ctrl.$validators.maxlength = function(modelValue, viewValue) {
29567 return (maxlength < 0) || ctrl.$isEmpty(viewValue) || (viewValue.length <= maxlength);
29575 * @name ngMinlength
29579 * ngMinlength adds the minlength {@link ngModel.NgModelController#$validators `validator`} to {@link ngModel `ngModel`}.
29580 * It is most often used for text-based {@link input `input`} controls, but can also be applied to custom text-based controls.
29582 * The validator sets the `minlength` error key if the {@link ngModel.NgModelController#$viewValue `ngModel.$viewValue`}
29583 * is shorter than the integer obtained by evaluating the Angular expression given in the
29584 * `ngMinlength` attribute value.
29586 * <div class="alert alert-info">
29587 * **Note:** This directive is also added when the plain `minlength` attribute is used, with two
29591 * `ngMinlength` does not set the `minlength` attribute and therefore HTML5 constraint
29592 * validation is not available.
29595 * The `ngMinlength` value must be an expression, while the `minlength` value must be
29602 * <example name="ngMinlengthDirective" module="ngMinlengthExample">
29603 * <file name="index.html">
29605 * angular.module('ngMinlengthExample', [])
29606 * .controller('ExampleController', ['$scope', function($scope) {
29607 * $scope.minlength = 3;
29610 * <div ng-controller="ExampleController">
29611 * <form name="form">
29612 * <label for="minlength">Set a minlength: </label>
29613 * <input type="number" ng-model="minlength" id="minlength" />
29615 * <label for="input">This input is restricted by the current minlength: </label>
29616 * <input type="text" ng-model="model" id="input" name="input" ng-minlength="minlength" /><br>
29618 * input valid? = <code>{{form.input.$valid}}</code><br>
29619 * model = <code>{{model}}</code>
29623 * <file name="protractor.js" type="protractor">
29624 var model = element(by.binding('model'));
29625 var input = element(by.id('input'));
29627 it('should validate the input with the default minlength', function() {
29628 input.sendKeys('ab');
29629 expect(model.getText()).not.toContain('ab');
29631 input.sendKeys('abc');
29632 expect(model.getText()).toContain('abc');
29637 var minlengthDirective = function() {
29640 require: '?ngModel',
29641 link: function(scope, elm, attr, ctrl) {
29645 attr.$observe('minlength', function(value) {
29646 minlength = toInt(value) || 0;
29649 ctrl.$validators.minlength = function(modelValue, viewValue) {
29650 return ctrl.$isEmpty(viewValue) || viewValue.length >= minlength;
29656 if (window.angular.bootstrap) {
29657 //AngularJS is already loaded, so we can return here...
29658 if (window.console) {
29659 console.log('WARNING: Tried to load angular more than once.');
29664 //try to bind to jquery now so that one can write jqLite(document).ready()
29665 //but we will rebind on bootstrap again.
29668 publishExternalAPI(angular);
29670 angular.module("ngLocale", [], ["$provide", function($provide) {
29671 var PLURAL_CATEGORY = {ZERO: "zero", ONE: "one", TWO: "two", FEW: "few", MANY: "many", OTHER: "other"};
29672 function getDecimals(n) {
29674 var i = n.indexOf('.');
29675 return (i == -1) ? 0 : n.length - i - 1;
29678 function getVF(n, opt_precision) {
29679 var v = opt_precision;
29681 if (undefined === v) {
29682 v = Math.min(getDecimals(n), 3);
29685 var base = Math.pow(10, v);
29686 var f = ((n * base) | 0) % base;
29687 return {v: v, f: f};
29690 $provide.value("$locale", {
29691 "DATETIME_FORMATS": {
29713 "FIRSTDAYOFWEEK": 6,
29751 "STANDALONEMONTH": [
29769 "fullDate": "EEEE, MMMM d, y",
29770 "longDate": "MMMM d, y",
29771 "medium": "MMM d, y h:mm:ss a",
29772 "mediumDate": "MMM d, y",
29773 "mediumTime": "h:mm:ss a",
29774 "short": "M/d/yy h:mm a",
29775 "shortDate": "M/d/yy",
29776 "shortTime": "h:mm a"
29778 "NUMBER_FORMATS": {
29779 "CURRENCY_SYM": "$",
29780 "DECIMAL_SEP": ".",
29800 "negPre": "-\u00a4",
29802 "posPre": "\u00a4",
29808 "localeID": "en_US",
29809 "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;}
29813 jqLite(document).ready(function() {
29814 angularInit(document, bootstrap);
29817 })(window, document);
29819 !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>');