2 * @license AngularJS v1.5.5
3 * (c) 2010-2016 Google, Inc. http://angularjs.org
6 (function(window) {'use strict';
11 * This object provides a utility for producing rich Error messages within
12 * Angular. It can be called as follows:
14 * var exampleMinErr = minErr('example');
15 * throw exampleMinErr('one', 'This {0} is {1}', foo, bar);
17 * The above creates an instance of minErr in the example namespace. The
18 * resulting error will have a namespaced error code of example.one. The
19 * resulting error will replace {0} with the value of foo, and {1} with the
20 * value of bar. The object is not restricted in the number of arguments it can
23 * If fewer arguments are specified than necessary for interpolation, the extra
24 * interpolation markers will be preserved in the final string.
26 * Since data will be parsed statically during a build step, some restrictions
27 * are applied with respect to how minErr instances are created and called.
28 * Instances should have names of the form namespaceMinErr for a minErr created
29 * using minErr('namespace') . Error codes, namespaces and template strings
30 * should all be static strings, not variables or general expressions.
32 * @param {string} module The namespace to use for the new minErr instance.
33 * @param {function} ErrorConstructor Custom error constructor to be instantiated when returning
34 * error from returned function, for cases when a particular type of error is useful.
35 * @returns {function(code:string, template:string, ...templateArgs): Error} minErr instance
38 function minErr(module, ErrorConstructor) {
39 ErrorConstructor = ErrorConstructor || Error;
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.5.5/' +
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 ////////////////////////////////////
178 * The ng module is loaded by default when an AngularJS application is started. The module itself
179 * contains the essential components for an AngularJS application to function. The table below
180 * lists a high level breakdown of each of the services/factories, filters, directives and testing
181 * components available within this core module.
183 * <div doc-module-components="ng"></div>
186 var REGEX_STRING_REGEXP = /^\/(.+)\/([a-z]*)$/;
188 // The name of a form control's ValidityState property.
189 // This is used so that it's possible for internal tests to create mock ValidityStates.
190 var VALIDITY_STATE_PROPERTY = 'validity';
192 var hasOwnProperty = Object.prototype.hasOwnProperty;
194 var lowercase = function(string) {return isString(string) ? string.toLowerCase() : string;};
195 var uppercase = function(string) {return isString(string) ? string.toUpperCase() : string;};
198 var manualLowercase = function(s) {
199 /* jshint bitwise: false */
201 ? s.replace(/[A-Z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) | 32);})
204 var manualUppercase = function(s) {
205 /* jshint bitwise: false */
207 ? s.replace(/[a-z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) & ~32);})
212 // String#toLowerCase and String#toUpperCase don't produce correct results in browsers with Turkish
213 // locale, for this reason we need to detect this case and redefine lowercase/uppercase methods
214 // with correct but slower alternatives. See https://github.com/angular/angular.js/issues/11387
215 if ('i' !== 'I'.toLowerCase()) {
216 lowercase = manualLowercase;
217 uppercase = manualUppercase;
222 msie, // holds major version number for IE, or NaN if UA is not IE.
223 jqLite, // delay binding since jQuery could be loaded after us.
224 jQuery, // delay binding
228 toString = Object.prototype.toString,
229 getPrototypeOf = Object.getPrototypeOf,
230 ngMinErr = minErr('ng'),
233 angular = window.angular || (window.angular = {}),
238 * documentMode is an IE-only property
239 * http://msdn.microsoft.com/en-us/library/ie/cc196988(v=vs.85).aspx
241 msie = window.document.documentMode;
247 * @return {boolean} Returns true if `obj` is an array or array-like object (NodeList, Arguments,
250 function isArrayLike(obj) {
252 // `null`, `undefined` and `window` are not array-like
253 if (obj == null || isWindow(obj)) return false;
255 // arrays, strings and jQuery/jqLite objects are array like
256 // * jqLite is either the jQuery or jqLite constructor function
257 // * we have to check the existence of jqLite first as this method is called
258 // via the forEach method when constructing the jqLite object in the first place
259 if (isArray(obj) || isString(obj) || (jqLite && obj instanceof jqLite)) return true;
261 // Support: iOS 8.2 (not reproducible in simulator)
262 // "length" in obj used to prevent JIT error (gh-11508)
263 var length = "length" in Object(obj) && obj.length;
265 // NodeList objects (with `item` method) and
266 // other objects with suitable length characteristics are array-like
267 return isNumber(length) &&
268 (length >= 0 && ((length - 1) in obj || obj instanceof Array) || typeof obj.item == 'function');
274 * @name angular.forEach
279 * Invokes the `iterator` function once for each item in `obj` collection, which can be either an
280 * object or an array. The `iterator` function is invoked with `iterator(value, key, obj)`, where `value`
281 * is the value of an object property or an array element, `key` is the object property key or
282 * array element index and obj is the `obj` itself. Specifying a `context` for the function is optional.
284 * It is worth noting that `.forEach` does not iterate over inherited properties because it filters
285 * using the `hasOwnProperty` method.
288 * [Array.prototype.forEach](http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.18),
289 * providing 'undefined' or 'null' values for `obj` will not throw a TypeError, but rather just
290 * return the value provided.
293 var values = {name: 'misko', gender: 'male'};
295 angular.forEach(values, function(value, key) {
296 this.push(key + ': ' + value);
298 expect(log).toEqual(['name: misko', 'gender: male']);
301 * @param {Object|Array} obj Object to iterate over.
302 * @param {Function} iterator Iterator function.
303 * @param {Object=} context Object to become context (`this`) for the iterator function.
304 * @returns {Object|Array} Reference to `obj`.
307 function forEach(obj, iterator, context) {
310 if (isFunction(obj)) {
312 // Need to check if hasOwnProperty exists,
313 // as on IE8 the result of querySelectorAll is an object without a hasOwnProperty function
314 if (key != 'prototype' && key != 'length' && key != 'name' && (!obj.hasOwnProperty || obj.hasOwnProperty(key))) {
315 iterator.call(context, obj[key], key, obj);
318 } else if (isArray(obj) || isArrayLike(obj)) {
319 var isPrimitive = typeof obj !== 'object';
320 for (key = 0, length = obj.length; key < length; key++) {
321 if (isPrimitive || key in obj) {
322 iterator.call(context, obj[key], key, obj);
325 } else if (obj.forEach && obj.forEach !== forEach) {
326 obj.forEach(iterator, context, obj);
327 } else if (isBlankObject(obj)) {
328 // createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty
330 iterator.call(context, obj[key], key, obj);
332 } else if (typeof obj.hasOwnProperty === 'function') {
333 // Slow path for objects inheriting Object.prototype, hasOwnProperty check needed
335 if (obj.hasOwnProperty(key)) {
336 iterator.call(context, obj[key], key, obj);
340 // Slow path for objects which do not have a method `hasOwnProperty`
342 if (hasOwnProperty.call(obj, key)) {
343 iterator.call(context, obj[key], key, obj);
351 function forEachSorted(obj, iterator, context) {
352 var keys = Object.keys(obj).sort();
353 for (var i = 0; i < keys.length; i++) {
354 iterator.call(context, obj[keys[i]], keys[i]);
361 * when using forEach the params are value, key, but it is often useful to have key, value.
362 * @param {function(string, *)} iteratorFn
363 * @returns {function(*, string)}
365 function reverseParams(iteratorFn) {
366 return function(value, key) {iteratorFn(key, value);};
370 * A consistent way of creating unique IDs in angular.
372 * Using simple numbers allows us to generate 28.6 million unique ids per second for 10 years before
373 * we hit number precision issues in JavaScript.
375 * Math.pow(2,53) / 60 / 60 / 24 / 365 / 10 = 28.6M
377 * @returns {number} an unique alpha-numeric string
385 * Set or clear the hashkey for an object.
387 * @param h the hashkey (!truthy to delete the hashkey)
389 function setHashKey(obj, h) {
393 delete obj.$$hashKey;
398 function baseExtend(dst, objs, deep) {
399 var h = dst.$$hashKey;
401 for (var i = 0, ii = objs.length; i < ii; ++i) {
403 if (!isObject(obj) && !isFunction(obj)) continue;
404 var keys = Object.keys(obj);
405 for (var j = 0, jj = keys.length; j < jj; j++) {
409 if (deep && isObject(src)) {
411 dst[key] = new Date(src.valueOf());
412 } else if (isRegExp(src)) {
413 dst[key] = new RegExp(src);
414 } else if (src.nodeName) {
415 dst[key] = src.cloneNode(true);
416 } else if (isElement(src)) {
417 dst[key] = src.clone();
419 if (!isObject(dst[key])) dst[key] = isArray(src) ? [] : {};
420 baseExtend(dst[key], [src], true);
434 * @name angular.extend
439 * Extends the destination object `dst` by copying own enumerable properties from the `src` object(s)
440 * to `dst`. You can specify multiple `src` objects. If you want to preserve original objects, you can do so
441 * by passing an empty object as the target: `var object = angular.extend({}, object1, object2)`.
443 * **Note:** Keep in mind that `angular.extend` does not support recursive merge (deep copy). Use
444 * {@link angular.merge} for this.
446 * @param {Object} dst Destination object.
447 * @param {...Object} src Source object(s).
448 * @returns {Object} Reference to `dst`.
450 function extend(dst) {
451 return baseExtend(dst, slice.call(arguments, 1), false);
457 * @name angular.merge
462 * Deeply extends the destination object `dst` by copying own enumerable properties from the `src` object(s)
463 * to `dst`. You can specify multiple `src` objects. If you want to preserve original objects, you can do so
464 * by passing an empty object as the target: `var object = angular.merge({}, object1, object2)`.
466 * Unlike {@link angular.extend extend()}, `merge()` recursively descends into object properties of source
467 * objects, performing a deep copy.
469 * @param {Object} dst Destination object.
470 * @param {...Object} src Source object(s).
471 * @returns {Object} Reference to `dst`.
473 function merge(dst) {
474 return baseExtend(dst, slice.call(arguments, 1), true);
479 function toInt(str) {
480 return parseInt(str, 10);
484 function inherit(parent, extra) {
485 return extend(Object.create(parent), extra);
495 * A function that performs no operations. This function can be useful when writing code in the
498 function foo(callback) {
499 var result = calculateResult();
500 (callback || angular.noop)(result);
510 * @name angular.identity
515 * A function that returns its first argument. This function is useful when writing code in the
519 function transformer(transformationFn, value) {
520 return (transformationFn || angular.identity)(value);
523 * @param {*} value to be returned.
524 * @returns {*} the value passed in.
526 function identity($) {return $;}
527 identity.$inject = [];
530 function valueFn(value) {return function valueRef() {return value;};}
532 function hasCustomToString(obj) {
533 return isFunction(obj.toString) && obj.toString !== toString;
539 * @name angular.isUndefined
544 * Determines if a reference is undefined.
546 * @param {*} value Reference to check.
547 * @returns {boolean} True if `value` is undefined.
549 function isUndefined(value) {return typeof value === 'undefined';}
554 * @name angular.isDefined
559 * Determines if a reference is defined.
561 * @param {*} value Reference to check.
562 * @returns {boolean} True if `value` is defined.
564 function isDefined(value) {return typeof value !== 'undefined';}
569 * @name angular.isObject
574 * Determines if a reference is an `Object`. Unlike `typeof` in JavaScript, `null`s are not
575 * considered to be objects. Note that JavaScript arrays are objects.
577 * @param {*} value Reference to check.
578 * @returns {boolean} True if `value` is an `Object` but not `null`.
580 function isObject(value) {
581 // http://jsperf.com/isobject4
582 return value !== null && typeof value === 'object';
587 * Determine if a value is an object with a null prototype
589 * @returns {boolean} True if `value` is an `Object` with a null prototype
591 function isBlankObject(value) {
592 return value !== null && typeof value === 'object' && !getPrototypeOf(value);
598 * @name angular.isString
603 * Determines if a reference is a `String`.
605 * @param {*} value Reference to check.
606 * @returns {boolean} True if `value` is a `String`.
608 function isString(value) {return typeof value === 'string';}
613 * @name angular.isNumber
618 * Determines if a reference is a `Number`.
620 * This includes the "special" numbers `NaN`, `+Infinity` and `-Infinity`.
622 * If you wish to exclude these then you can use the native
623 * [`isFinite'](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/isFinite)
626 * @param {*} value Reference to check.
627 * @returns {boolean} True if `value` is a `Number`.
629 function isNumber(value) {return typeof value === 'number';}
634 * @name angular.isDate
639 * Determines if a value is a date.
641 * @param {*} value Reference to check.
642 * @returns {boolean} True if `value` is a `Date`.
644 function isDate(value) {
645 return toString.call(value) === '[object Date]';
651 * @name angular.isArray
656 * Determines if a reference is an `Array`.
658 * @param {*} value Reference to check.
659 * @returns {boolean} True if `value` is an `Array`.
661 var isArray = Array.isArray;
665 * @name angular.isFunction
670 * Determines if a reference is a `Function`.
672 * @param {*} value Reference to check.
673 * @returns {boolean} True if `value` is a `Function`.
675 function isFunction(value) {return typeof value === 'function';}
679 * Determines if a value is a regular expression object.
682 * @param {*} value Reference to check.
683 * @returns {boolean} True if `value` is a `RegExp`.
685 function isRegExp(value) {
686 return toString.call(value) === '[object RegExp]';
691 * Checks if `obj` is a window object.
694 * @param {*} obj Object to check
695 * @returns {boolean} True if `obj` is a window obj.
697 function isWindow(obj) {
698 return obj && obj.window === obj;
702 function isScope(obj) {
703 return obj && obj.$evalAsync && obj.$watch;
707 function isFile(obj) {
708 return toString.call(obj) === '[object File]';
712 function isFormData(obj) {
713 return toString.call(obj) === '[object FormData]';
717 function isBlob(obj) {
718 return toString.call(obj) === '[object Blob]';
722 function isBoolean(value) {
723 return typeof value === 'boolean';
727 function isPromiseLike(obj) {
728 return obj && isFunction(obj.then);
732 var TYPED_ARRAY_REGEXP = /^\[object (?:Uint8|Uint8Clamped|Uint16|Uint32|Int8|Int16|Int32|Float32|Float64)Array\]$/;
733 function isTypedArray(value) {
734 return value && isNumber(value.length) && TYPED_ARRAY_REGEXP.test(toString.call(value));
737 function isArrayBuffer(obj) {
738 return toString.call(obj) === '[object ArrayBuffer]';
742 var trim = function(value) {
743 return isString(value) ? value.trim() : value;
747 // http://docs.closure-library.googlecode.com/git/local_closure_goog_string_string.js.source.html#line1021
748 // Prereq: s is a string.
749 var escapeForRegexp = function(s) {
750 return s.replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g, '\\$1').
751 replace(/\x08/g, '\\x08');
757 * @name angular.isElement
762 * Determines if a reference is a DOM element (or wrapped jQuery element).
764 * @param {*} value Reference to check.
765 * @returns {boolean} True if `value` is a DOM element (or wrapped jQuery element).
767 function isElement(node) {
769 (node.nodeName // we are a direct element
770 || (node.prop && node.attr && node.find))); // we have an on and find method part of jQuery API
774 * @param str 'key1,key2,...'
775 * @returns {object} in the form of {key1:true, key2:true, ...}
777 function makeMap(str) {
778 var obj = {}, items = str.split(','), i;
779 for (i = 0; i < items.length; i++) {
780 obj[items[i]] = true;
786 function nodeName_(element) {
787 return lowercase(element.nodeName || (element[0] && element[0].nodeName));
790 function includes(array, obj) {
791 return Array.prototype.indexOf.call(array, obj) != -1;
794 function arrayRemove(array, value) {
795 var index = array.indexOf(value);
797 array.splice(index, 1);
809 * Creates a deep copy of `source`, which should be an object or an array.
811 * * If no destination is supplied, a copy of the object or array is created.
812 * * If a destination is provided, all of its elements (for arrays) or properties (for objects)
813 * are deleted and then all elements/properties from the source are copied to it.
814 * * If `source` is not an object or array (inc. `null` and `undefined`), `source` is returned.
815 * * If `source` is identical to 'destination' an exception will be thrown.
817 * @param {*} source The source that will be used to make a copy.
818 * Can be any type, including primitives, `null`, and `undefined`.
819 * @param {(Object|Array)=} destination Destination into which the source is copied. If
820 * provided, must be of the same type as `source`.
821 * @returns {*} The copy or updated `destination`, if `destination` was specified.
824 <example module="copyExample">
825 <file name="index.html">
826 <div ng-controller="ExampleController">
827 <form novalidate class="simple-form">
828 Name: <input type="text" ng-model="user.name" /><br />
829 E-mail: <input type="email" ng-model="user.email" /><br />
830 Gender: <input type="radio" ng-model="user.gender" value="male" />male
831 <input type="radio" ng-model="user.gender" value="female" />female<br />
832 <button ng-click="reset()">RESET</button>
833 <button ng-click="update(user)">SAVE</button>
835 <pre>form = {{user | json}}</pre>
836 <pre>master = {{master | json}}</pre>
840 angular.module('copyExample', [])
841 .controller('ExampleController', ['$scope', function($scope) {
844 $scope.update = function(user) {
845 // Example with 1 argument
846 $scope.master= angular.copy(user);
849 $scope.reset = function() {
850 // Example with 2 arguments
851 angular.copy($scope.master, $scope.user);
860 function copy(source, destination) {
861 var stackSource = [];
865 if (isTypedArray(destination) || isArrayBuffer(destination)) {
866 throw ngMinErr('cpta', "Can't copy! TypedArray destination cannot be mutated.");
868 if (source === destination) {
869 throw ngMinErr('cpi', "Can't copy! Source and destination are identical.");
872 // Empty the destination object
873 if (isArray(destination)) {
874 destination.length = 0;
876 forEach(destination, function(value, key) {
877 if (key !== '$$hashKey') {
878 delete destination[key];
883 stackSource.push(source);
884 stackDest.push(destination);
885 return copyRecurse(source, destination);
888 return copyElement(source);
890 function copyRecurse(source, destination) {
891 var h = destination.$$hashKey;
893 if (isArray(source)) {
894 for (var i = 0, ii = source.length; i < ii; i++) {
895 destination.push(copyElement(source[i]));
897 } else if (isBlankObject(source)) {
898 // createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty
899 for (key in source) {
900 destination[key] = copyElement(source[key]);
902 } else if (source && typeof source.hasOwnProperty === 'function') {
903 // Slow path, which must rely on hasOwnProperty
904 for (key in source) {
905 if (source.hasOwnProperty(key)) {
906 destination[key] = copyElement(source[key]);
910 // Slowest path --- hasOwnProperty can't be called as a method
911 for (key in source) {
912 if (hasOwnProperty.call(source, key)) {
913 destination[key] = copyElement(source[key]);
917 setHashKey(destination, h);
921 function copyElement(source) {
923 if (!isObject(source)) {
927 // Already copied values
928 var index = stackSource.indexOf(source);
930 return stackDest[index];
933 if (isWindow(source) || isScope(source)) {
934 throw ngMinErr('cpws',
935 "Can't copy! Making copies of Window or Scope instances is not supported.");
938 var needsRecurse = false;
939 var destination = copyType(source);
941 if (destination === undefined) {
942 destination = isArray(source) ? [] : Object.create(getPrototypeOf(source));
946 stackSource.push(source);
947 stackDest.push(destination);
950 ? copyRecurse(source, destination)
954 function copyType(source) {
955 switch (toString.call(source)) {
956 case '[object Int8Array]':
957 case '[object Int16Array]':
958 case '[object Int32Array]':
959 case '[object Float32Array]':
960 case '[object Float64Array]':
961 case '[object Uint8Array]':
962 case '[object Uint8ClampedArray]':
963 case '[object Uint16Array]':
964 case '[object Uint32Array]':
965 return new source.constructor(copyElement(source.buffer));
967 case '[object ArrayBuffer]':
970 var copied = new ArrayBuffer(source.byteLength);
971 new Uint8Array(copied).set(new Uint8Array(source));
974 return source.slice(0);
976 case '[object Boolean]':
977 case '[object Number]':
978 case '[object String]':
979 case '[object Date]':
980 return new source.constructor(source.valueOf());
982 case '[object RegExp]':
983 var re = new RegExp(source.source, source.toString().match(/[^\/]*$/)[0]);
984 re.lastIndex = source.lastIndex;
987 case '[object Blob]':
988 return new source.constructor([source], {type: source.type});
991 if (isFunction(source.cloneNode)) {
992 return source.cloneNode(true);
998 * Creates a shallow copy of an object, an array or a primitive.
1000 * Assumes that there are no proto properties for objects.
1002 function shallowCopy(src, dst) {
1006 for (var i = 0, ii = src.length; i < ii; i++) {
1009 } else if (isObject(src)) {
1012 for (var key in src) {
1013 if (!(key.charAt(0) === '$' && key.charAt(1) === '$')) {
1014 dst[key] = src[key];
1025 * @name angular.equals
1030 * Determines if two objects or two values are equivalent. Supports value types, regular
1031 * expressions, arrays and objects.
1033 * Two objects or values are considered equivalent if at least one of the following is true:
1035 * * Both objects or values pass `===` comparison.
1036 * * Both objects or values are of the same type and all of their properties are equal by
1037 * comparing them with `angular.equals`.
1038 * * Both values are NaN. (In JavaScript, NaN == NaN => false. But we consider two NaN as equal)
1039 * * Both values represent the same regular expression (In JavaScript,
1040 * /abc/ == /abc/ => false. But we consider two regular expressions as equal when their textual
1041 * representation matches).
1043 * During a property comparison, properties of `function` type and properties with names
1044 * that begin with `$` are ignored.
1046 * Scope and DOMWindow objects are being compared only by identify (`===`).
1048 * @param {*} o1 Object or value to compare.
1049 * @param {*} o2 Object or value to compare.
1050 * @returns {boolean} True if arguments are equal.
1053 <example module="equalsExample" name="equalsExample">
1054 <file name="index.html">
1055 <div ng-controller="ExampleController">
1058 Name: <input type="text" ng-model="user1.name">
1059 Age: <input type="number" ng-model="user1.age">
1062 Name: <input type="text" ng-model="user2.name">
1063 Age: <input type="number" ng-model="user2.age">
1067 <input type="button" value="Compare" ng-click="compare()">
1069 User 1: <pre>{{user1 | json}}</pre>
1070 User 2: <pre>{{user2 | json}}</pre>
1071 Equal: <pre>{{result}}</pre>
1075 <file name="script.js">
1076 angular.module('equalsExample', []).controller('ExampleController', ['$scope', function($scope) {
1080 $scope.compare = function() {
1081 $scope.result = angular.equals($scope.user1, $scope.user2);
1087 function equals(o1, o2) {
1088 if (o1 === o2) return true;
1089 if (o1 === null || o2 === null) return false;
1090 if (o1 !== o1 && o2 !== o2) return true; // NaN === NaN
1091 var t1 = typeof o1, t2 = typeof o2, length, key, keySet;
1092 if (t1 == t2 && t1 == 'object') {
1094 if (!isArray(o2)) return false;
1095 if ((length = o1.length) == o2.length) {
1096 for (key = 0; key < length; key++) {
1097 if (!equals(o1[key], o2[key])) return false;
1101 } else if (isDate(o1)) {
1102 if (!isDate(o2)) return false;
1103 return equals(o1.getTime(), o2.getTime());
1104 } else if (isRegExp(o1)) {
1105 if (!isRegExp(o2)) return false;
1106 return o1.toString() == o2.toString();
1108 if (isScope(o1) || isScope(o2) || isWindow(o1) || isWindow(o2) ||
1109 isArray(o2) || isDate(o2) || isRegExp(o2)) return false;
1110 keySet = createMap();
1112 if (key.charAt(0) === '$' || isFunction(o1[key])) continue;
1113 if (!equals(o1[key], o2[key])) return false;
1117 if (!(key in keySet) &&
1118 key.charAt(0) !== '$' &&
1119 isDefined(o2[key]) &&
1120 !isFunction(o2[key])) return false;
1128 var csp = function() {
1129 if (!isDefined(csp.rules)) {
1132 var ngCspElement = (window.document.querySelector('[ng-csp]') ||
1133 window.document.querySelector('[data-ng-csp]'));
1136 var ngCspAttribute = ngCspElement.getAttribute('ng-csp') ||
1137 ngCspElement.getAttribute('data-ng-csp');
1139 noUnsafeEval: !ngCspAttribute || (ngCspAttribute.indexOf('no-unsafe-eval') !== -1),
1140 noInlineStyle: !ngCspAttribute || (ngCspAttribute.indexOf('no-inline-style') !== -1)
1144 noUnsafeEval: noUnsafeEval(),
1145 noInlineStyle: false
1152 function noUnsafeEval() {
1154 /* jshint -W031, -W054 */
1156 /* jshint +W031, +W054 */
1170 * @param {string=} ngJq the name of the library available under `window`
1171 * to be used for angular.element
1173 * Use this directive to force the angular.element library. This should be
1174 * used to force either jqLite by leaving ng-jq blank or setting the name of
1175 * the jquery variable under window (eg. jQuery).
1177 * Since angular looks for this directive when it is loaded (doesn't wait for the
1178 * DOMContentLoaded event), it must be placed on an element that comes before the script
1179 * which loads angular. Also, only the first instance of `ng-jq` will be used and all
1183 * This example shows how to force jqLite using the `ngJq` directive to the `html` tag.
1192 * This example shows how to use a jQuery based library of a different name.
1193 * The library name must be available at the top most 'window'.
1196 <html ng-app ng-jq="jQueryLib">
1202 var jq = function() {
1203 if (isDefined(jq.name_)) return jq.name_;
1205 var i, ii = ngAttrPrefixes.length, prefix, name;
1206 for (i = 0; i < ii; ++i) {
1207 prefix = ngAttrPrefixes[i];
1208 if (el = window.document.querySelector('[' + prefix.replace(':', '\\:') + 'jq]')) {
1209 name = el.getAttribute(prefix + 'jq');
1214 return (jq.name_ = name);
1217 function concat(array1, array2, index) {
1218 return array1.concat(slice.call(array2, index));
1221 function sliceArgs(args, startIndex) {
1222 return slice.call(args, startIndex || 0);
1229 * @name angular.bind
1234 * Returns a function which calls function `fn` bound to `self` (`self` becomes the `this` for
1235 * `fn`). You can supply optional `args` that are prebound to the function. This feature is also
1236 * known as [partial application](http://en.wikipedia.org/wiki/Partial_application), as
1237 * distinguished from [function currying](http://en.wikipedia.org/wiki/Currying#Contrast_with_partial_function_application).
1239 * @param {Object} self Context which `fn` should be evaluated in.
1240 * @param {function()} fn Function to be bound.
1241 * @param {...*} args Optional arguments to be prebound to the `fn` function call.
1242 * @returns {function()} Function that wraps the `fn` with all the specified bindings.
1245 function bind(self, fn) {
1246 var curryArgs = arguments.length > 2 ? sliceArgs(arguments, 2) : [];
1247 if (isFunction(fn) && !(fn instanceof RegExp)) {
1248 return curryArgs.length
1250 return arguments.length
1251 ? fn.apply(self, concat(curryArgs, arguments, 0))
1252 : fn.apply(self, curryArgs);
1255 return arguments.length
1256 ? fn.apply(self, arguments)
1260 // in IE, native methods are not functions so they cannot be bound (note: they don't need to be)
1266 function toJsonReplacer(key, value) {
1269 if (typeof key === 'string' && key.charAt(0) === '$' && key.charAt(1) === '$') {
1271 } else if (isWindow(value)) {
1273 } else if (value && window.document === value) {
1275 } else if (isScope(value)) {
1285 * @name angular.toJson
1290 * Serializes input into a JSON-formatted string. Properties with leading $$ characters will be
1291 * stripped since angular uses this notation internally.
1293 * @param {Object|Array|Date|string|number} obj Input to be serialized into JSON.
1294 * @param {boolean|number} [pretty=2] If set to true, the JSON output will contain newlines and whitespace.
1295 * If set to an integer, the JSON output will contain that many spaces per indentation.
1296 * @returns {string|undefined} JSON-ified string representing `obj`.
1298 function toJson(obj, pretty) {
1299 if (isUndefined(obj)) return undefined;
1300 if (!isNumber(pretty)) {
1301 pretty = pretty ? 2 : null;
1303 return JSON.stringify(obj, toJsonReplacer, pretty);
1309 * @name angular.fromJson
1314 * Deserializes a JSON string.
1316 * @param {string} json JSON string to deserialize.
1317 * @returns {Object|Array|string|number} Deserialized JSON string.
1319 function fromJson(json) {
1320 return isString(json)
1326 var ALL_COLONS = /:/g;
1327 function timezoneToOffset(timezone, fallback) {
1328 // IE/Edge do not "understand" colon (`:`) in timezone
1329 timezone = timezone.replace(ALL_COLONS, '');
1330 var requestedTimezoneOffset = Date.parse('Jan 01, 1970 00:00:00 ' + timezone) / 60000;
1331 return isNaN(requestedTimezoneOffset) ? fallback : requestedTimezoneOffset;
1335 function addDateMinutes(date, minutes) {
1336 date = new Date(date.getTime());
1337 date.setMinutes(date.getMinutes() + minutes);
1342 function convertTimezoneToLocal(date, timezone, reverse) {
1343 reverse = reverse ? -1 : 1;
1344 var dateTimezoneOffset = date.getTimezoneOffset();
1345 var timezoneOffset = timezoneToOffset(timezone, dateTimezoneOffset);
1346 return addDateMinutes(date, reverse * (timezoneOffset - dateTimezoneOffset));
1351 * @returns {string} Returns the string representation of the element.
1353 function startingTag(element) {
1354 element = jqLite(element).clone();
1356 // turns out IE does not let you set .html() on elements which
1357 // are not allowed to have children. So we just ignore it.
1360 var elemHtml = jqLite('<div>').append(element).html();
1362 return element[0].nodeType === NODE_TYPE_TEXT ? lowercase(elemHtml) :
1364 match(/^(<[^>]+>)/)[1].
1365 replace(/^<([\w\-]+)/, function(match, nodeName) {return '<' + lowercase(nodeName);});
1367 return lowercase(elemHtml);
1373 /////////////////////////////////////////////////
1376 * Tries to decode the URI component without throwing an exception.
1379 * @param str value potential URI component to check.
1380 * @returns {boolean} True if `value` can be decoded
1381 * with the decodeURIComponent function.
1383 function tryDecodeURIComponent(value) {
1385 return decodeURIComponent(value);
1387 // Ignore any invalid uri component
1393 * Parses an escaped url query string into key-value pairs.
1394 * @returns {Object.<string,boolean|Array>}
1396 function parseKeyValue(/**string*/keyValue) {
1398 forEach((keyValue || "").split('&'), function(keyValue) {
1399 var splitPoint, key, val;
1401 key = keyValue = keyValue.replace(/\+/g,'%20');
1402 splitPoint = keyValue.indexOf('=');
1403 if (splitPoint !== -1) {
1404 key = keyValue.substring(0, splitPoint);
1405 val = keyValue.substring(splitPoint + 1);
1407 key = tryDecodeURIComponent(key);
1408 if (isDefined(key)) {
1409 val = isDefined(val) ? tryDecodeURIComponent(val) : true;
1410 if (!hasOwnProperty.call(obj, key)) {
1412 } else if (isArray(obj[key])) {
1415 obj[key] = [obj[key],val];
1423 function toKeyValue(obj) {
1425 forEach(obj, function(value, key) {
1426 if (isArray(value)) {
1427 forEach(value, function(arrayValue) {
1428 parts.push(encodeUriQuery(key, true) +
1429 (arrayValue === true ? '' : '=' + encodeUriQuery(arrayValue, true)));
1432 parts.push(encodeUriQuery(key, true) +
1433 (value === true ? '' : '=' + encodeUriQuery(value, true)));
1436 return parts.length ? parts.join('&') : '';
1441 * We need our custom method because encodeURIComponent is too aggressive and doesn't follow
1442 * http://www.ietf.org/rfc/rfc3986.txt with regards to the character set (pchar) allowed in path
1445 * pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
1446 * pct-encoded = "%" HEXDIG HEXDIG
1447 * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
1448 * sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
1449 * / "*" / "+" / "," / ";" / "="
1451 function encodeUriSegment(val) {
1452 return encodeUriQuery(val, true).
1453 replace(/%26/gi, '&').
1454 replace(/%3D/gi, '=').
1455 replace(/%2B/gi, '+');
1460 * This method is intended for encoding *key* or *value* parts of query component. We need a custom
1461 * method because encodeURIComponent is too aggressive and encodes stuff that doesn't have to be
1462 * encoded per http://tools.ietf.org/html/rfc3986:
1463 * query = *( pchar / "/" / "?" )
1464 * pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
1465 * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
1466 * pct-encoded = "%" HEXDIG HEXDIG
1467 * sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
1468 * / "*" / "+" / "," / ";" / "="
1470 function encodeUriQuery(val, pctEncodeSpaces) {
1471 return encodeURIComponent(val).
1472 replace(/%40/gi, '@').
1473 replace(/%3A/gi, ':').
1474 replace(/%24/g, '$').
1475 replace(/%2C/gi, ',').
1476 replace(/%3B/gi, ';').
1477 replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
1480 var ngAttrPrefixes = ['ng-', 'data-ng-', 'ng:', 'x-ng-'];
1482 function getNgAttribute(element, ngAttr) {
1483 var attr, i, ii = ngAttrPrefixes.length;
1484 for (i = 0; i < ii; ++i) {
1485 attr = ngAttrPrefixes[i] + ngAttr;
1486 if (isString(attr = element.getAttribute(attr))) {
1499 * @param {angular.Module} ngApp an optional application
1500 * {@link angular.module module} name to load.
1501 * @param {boolean=} ngStrictDi if this attribute is present on the app element, the injector will be
1502 * created in "strict-di" mode. This means that the application will fail to invoke functions which
1503 * do not use explicit function annotation (and are thus unsuitable for minification), as described
1504 * in {@link guide/di the Dependency Injection guide}, and useful debugging info will assist in
1505 * tracking down the root of these bugs.
1509 * Use this directive to **auto-bootstrap** an AngularJS application. The `ngApp` directive
1510 * designates the **root element** of the application and is typically placed near the root element
1511 * of the page - e.g. on the `<body>` or `<html>` tags.
1513 * There are a few things to keep in mind when using `ngApp`:
1514 * - only one AngularJS application can be auto-bootstrapped per HTML document. The first `ngApp`
1515 * found in the document will be used to define the root element to auto-bootstrap as an
1516 * application. To run multiple applications in an HTML document you must manually bootstrap them using
1517 * {@link angular.bootstrap} instead.
1518 * - AngularJS applications cannot be nested within each other.
1519 * - Do not use a directive that uses {@link ng.$compile#transclusion transclusion} on the same element as `ngApp`.
1520 * This includes directives such as {@link ng.ngIf `ngIf`}, {@link ng.ngInclude `ngInclude`} and
1521 * {@link ngRoute.ngView `ngView`}.
1522 * Doing this misplaces the app {@link ng.$rootElement `$rootElement`} and the app's {@link auto.$injector injector},
1523 * causing animations to stop working and making the injector inaccessible from outside the app.
1525 * You can specify an **AngularJS module** to be used as the root module for the application. This
1526 * module will be loaded into the {@link auto.$injector} when the application is bootstrapped. It
1527 * should contain the application code needed or have dependencies on other modules that will
1528 * contain the code. See {@link angular.module} for more information.
1530 * In the example below if the `ngApp` directive were not placed on the `html` element then the
1531 * document would not be compiled, the `AppController` would not be instantiated and the `{{ a+b }}`
1532 * would not be resolved to `3`.
1534 * `ngApp` is the easiest, and most common way to bootstrap an application.
1536 <example module="ngAppDemo">
1537 <file name="index.html">
1538 <div ng-controller="ngAppDemoController">
1539 I can add: {{a}} + {{b}} = {{ a+b }}
1542 <file name="script.js">
1543 angular.module('ngAppDemo', []).controller('ngAppDemoController', function($scope) {
1550 * Using `ngStrictDi`, you would see something like this:
1552 <example ng-app-included="true">
1553 <file name="index.html">
1554 <div ng-app="ngAppStrictDemo" ng-strict-di>
1555 <div ng-controller="GoodController1">
1556 I can add: {{a}} + {{b}} = {{ a+b }}
1558 <p>This renders because the controller does not fail to
1559 instantiate, by using explicit annotation style (see
1560 script.js for details)
1564 <div ng-controller="GoodController2">
1565 Name: <input ng-model="name"><br />
1568 <p>This renders because the controller does not fail to
1569 instantiate, by using explicit annotation style
1570 (see script.js for details)
1574 <div ng-controller="BadController">
1575 I can add: {{a}} + {{b}} = {{ a+b }}
1577 <p>The controller could not be instantiated, due to relying
1578 on automatic function annotations (which are disabled in
1579 strict mode). As such, the content of this section is not
1580 interpolated, and there should be an error in your web console.
1585 <file name="script.js">
1586 angular.module('ngAppStrictDemo', [])
1587 // BadController will fail to instantiate, due to relying on automatic function annotation,
1588 // rather than an explicit annotation
1589 .controller('BadController', function($scope) {
1593 // Unlike BadController, GoodController1 and GoodController2 will not fail to be instantiated,
1594 // due to using explicit annotations using the array style and $inject property, respectively.
1595 .controller('GoodController1', ['$scope', function($scope) {
1599 .controller('GoodController2', GoodController2);
1600 function GoodController2($scope) {
1601 $scope.name = "World";
1603 GoodController2.$inject = ['$scope'];
1605 <file name="style.css">
1606 div[ng-controller] {
1608 -webkit-border-radius: 4px;
1613 div[ng-controller^=Good] {
1614 border-color: #d6e9c6;
1615 background-color: #dff0d8;
1618 div[ng-controller^=Bad] {
1619 border-color: #ebccd1;
1620 background-color: #f2dede;
1627 function angularInit(element, bootstrap) {
1632 // The element `element` has priority over any other element
1633 forEach(ngAttrPrefixes, function(prefix) {
1634 var name = prefix + 'app';
1636 if (!appElement && element.hasAttribute && element.hasAttribute(name)) {
1637 appElement = element;
1638 module = element.getAttribute(name);
1641 forEach(ngAttrPrefixes, function(prefix) {
1642 var name = prefix + 'app';
1645 if (!appElement && (candidate = element.querySelector('[' + name.replace(':', '\\:') + ']'))) {
1646 appElement = candidate;
1647 module = candidate.getAttribute(name);
1651 config.strictDi = getNgAttribute(appElement, "strict-di") !== null;
1652 bootstrap(appElement, module ? [module] : [], config);
1658 * @name angular.bootstrap
1661 * Use this function to manually start up angular application.
1663 * For more information, see the {@link guide/bootstrap Bootstrap guide}.
1665 * Angular will detect if it has been loaded into the browser more than once and only allow the
1666 * first loaded script to be bootstrapped and will report a warning to the browser console for
1667 * each of the subsequent scripts. This prevents strange results in applications, where otherwise
1668 * multiple instances of Angular try to work on the DOM.
1670 * <div class="alert alert-warning">
1671 * **Note:** Protractor based end-to-end tests cannot use this function to bootstrap manually.
1672 * They must use {@link ng.directive:ngApp ngApp}.
1675 * <div class="alert alert-warning">
1676 * **Note:** Do not bootstrap the app on an element with a directive that uses {@link ng.$compile#transclusion transclusion},
1677 * such as {@link ng.ngIf `ngIf`}, {@link ng.ngInclude `ngInclude`} and {@link ngRoute.ngView `ngView`}.
1678 * Doing this misplaces the app {@link ng.$rootElement `$rootElement`} and the app's {@link auto.$injector injector},
1679 * causing animations to stop working and making the injector inaccessible from outside the app.
1686 * <div ng-controller="WelcomeController">
1690 * <script src="angular.js"></script>
1692 * var app = angular.module('demo', [])
1693 * .controller('WelcomeController', function($scope) {
1694 * $scope.greeting = 'Welcome!';
1696 * angular.bootstrap(document, ['demo']);
1702 * @param {DOMElement} element DOM element which is the root of angular application.
1703 * @param {Array<String|Function|Array>=} modules an array of modules to load into the application.
1704 * Each item in the array should be the name of a predefined module or a (DI annotated)
1705 * function that will be invoked by the injector as a `config` block.
1706 * See: {@link angular.module modules}
1707 * @param {Object=} config an object for defining configuration options for the application. The
1708 * following keys are supported:
1710 * * `strictDi` - disable automatic function annotation for the application. This is meant to
1711 * assist in finding bugs which break minified code. Defaults to `false`.
1713 * @returns {auto.$injector} Returns the newly created injector for this app.
1715 function bootstrap(element, modules, config) {
1716 if (!isObject(config)) config = {};
1717 var defaultConfig = {
1720 config = extend(defaultConfig, config);
1721 var doBootstrap = function() {
1722 element = jqLite(element);
1724 if (element.injector()) {
1725 var tag = (element[0] === window.document) ? 'document' : startingTag(element);
1726 //Encode angle brackets to prevent input from being sanitized to empty string #8683
1729 "App already bootstrapped with this element '{0}'",
1730 tag.replace(/</,'<').replace(/>/,'>'));
1733 modules = modules || [];
1734 modules.unshift(['$provide', function($provide) {
1735 $provide.value('$rootElement', element);
1738 if (config.debugInfoEnabled) {
1739 // Pushing so that this overrides `debugInfoEnabled` setting defined in user's `modules`.
1740 modules.push(['$compileProvider', function($compileProvider) {
1741 $compileProvider.debugInfoEnabled(true);
1745 modules.unshift('ng');
1746 var injector = createInjector(modules, config.strictDi);
1747 injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector',
1748 function bootstrapApply(scope, element, compile, injector) {
1749 scope.$apply(function() {
1750 element.data('$injector', injector);
1751 compile(element)(scope);
1758 var NG_ENABLE_DEBUG_INFO = /^NG_ENABLE_DEBUG_INFO!/;
1759 var NG_DEFER_BOOTSTRAP = /^NG_DEFER_BOOTSTRAP!/;
1761 if (window && NG_ENABLE_DEBUG_INFO.test(window.name)) {
1762 config.debugInfoEnabled = true;
1763 window.name = window.name.replace(NG_ENABLE_DEBUG_INFO, '');
1766 if (window && !NG_DEFER_BOOTSTRAP.test(window.name)) {
1767 return doBootstrap();
1770 window.name = window.name.replace(NG_DEFER_BOOTSTRAP, '');
1771 angular.resumeBootstrap = function(extraModules) {
1772 forEach(extraModules, function(module) {
1773 modules.push(module);
1775 return doBootstrap();
1778 if (isFunction(angular.resumeDeferredBootstrap)) {
1779 angular.resumeDeferredBootstrap();
1785 * @name angular.reloadWithDebugInfo
1788 * Use this function to reload the current application with debug information turned on.
1789 * This takes precedence over a call to `$compileProvider.debugInfoEnabled(false)`.
1791 * See {@link ng.$compileProvider#debugInfoEnabled} for more.
1793 function reloadWithDebugInfo() {
1794 window.name = 'NG_ENABLE_DEBUG_INFO!' + window.name;
1795 window.location.reload();
1799 * @name angular.getTestability
1802 * Get the testability service for the instance of Angular on the given
1804 * @param {DOMElement} element DOM element which is the root of angular application.
1806 function getTestability(rootElement) {
1807 var injector = angular.element(rootElement).injector();
1809 throw ngMinErr('test',
1810 'no injector found for element argument to getTestability');
1812 return injector.get('$$testability');
1815 var SNAKE_CASE_REGEXP = /[A-Z]/g;
1816 function snake_case(name, separator) {
1817 separator = separator || '_';
1818 return name.replace(SNAKE_CASE_REGEXP, function(letter, pos) {
1819 return (pos ? separator : '') + letter.toLowerCase();
1823 var bindJQueryFired = false;
1824 function bindJQuery() {
1825 var originalCleanData;
1827 if (bindJQueryFired) {
1831 // bind to jQuery if present;
1833 jQuery = isUndefined(jqName) ? window.jQuery : // use jQuery (if present)
1834 !jqName ? undefined : // use jqLite
1835 window[jqName]; // use jQuery specified by `ngJq`
1837 // Use jQuery if it exists with proper functionality, otherwise default to us.
1838 // Angular 1.2+ requires jQuery 1.7+ for on()/off() support.
1839 // Angular 1.3+ technically requires at least jQuery 2.1+ but it may work with older
1840 // versions. It will not work for sure with jQuery <1.7, though.
1841 if (jQuery && jQuery.fn.on) {
1844 scope: JQLitePrototype.scope,
1845 isolateScope: JQLitePrototype.isolateScope,
1846 controller: JQLitePrototype.controller,
1847 injector: JQLitePrototype.injector,
1848 inheritedData: JQLitePrototype.inheritedData
1851 // All nodes removed from the DOM via various jQuery APIs like .remove()
1852 // are passed through jQuery.cleanData. Monkey-patch this method to fire
1853 // the $destroy event on all removed nodes.
1854 originalCleanData = jQuery.cleanData;
1855 jQuery.cleanData = function(elems) {
1857 for (var i = 0, elem; (elem = elems[i]) != null; i++) {
1858 events = jQuery._data(elem, "events");
1859 if (events && events.$destroy) {
1860 jQuery(elem).triggerHandler('$destroy');
1863 originalCleanData(elems);
1869 angular.element = jqLite;
1871 // Prevent double-proxying.
1872 bindJQueryFired = true;
1876 * throw error if the argument is falsy.
1878 function assertArg(arg, name, reason) {
1880 throw ngMinErr('areq', "Argument '{0}' is {1}", (name || '?'), (reason || "required"));
1885 function assertArgFn(arg, name, acceptArrayAnnotation) {
1886 if (acceptArrayAnnotation && isArray(arg)) {
1887 arg = arg[arg.length - 1];
1890 assertArg(isFunction(arg), name, 'not a function, got ' +
1891 (arg && typeof arg === 'object' ? arg.constructor.name || 'Object' : typeof arg));
1896 * throw error if the name given is hasOwnProperty
1897 * @param {String} name the name to test
1898 * @param {String} context the context in which the name is used, such as module or directive
1900 function assertNotHasOwnProperty(name, context) {
1901 if (name === 'hasOwnProperty') {
1902 throw ngMinErr('badname', "hasOwnProperty is not a valid {0} name", context);
1907 * Return the value accessible from the object by path. Any undefined traversals are ignored
1908 * @param {Object} obj starting object
1909 * @param {String} path path to traverse
1910 * @param {boolean} [bindFnToScope=true]
1911 * @returns {Object} value as accessible by path
1913 //TODO(misko): this function needs to be removed
1914 function getter(obj, path, bindFnToScope) {
1915 if (!path) return obj;
1916 var keys = path.split('.');
1918 var lastInstance = obj;
1919 var len = keys.length;
1921 for (var i = 0; i < len; i++) {
1924 obj = (lastInstance = obj)[key];
1927 if (!bindFnToScope && isFunction(obj)) {
1928 return bind(lastInstance, obj);
1934 * Return the DOM siblings between the first and last node in the given array.
1935 * @param {Array} array like object
1936 * @returns {Array} the inputted object or a jqLite collection containing the nodes
1938 function getBlockNodes(nodes) {
1939 // TODO(perf): update `nodes` instead of creating a new object?
1940 var node = nodes[0];
1941 var endNode = nodes[nodes.length - 1];
1944 for (var i = 1; node !== endNode && (node = node.nextSibling); i++) {
1945 if (blockNodes || nodes[i] !== node) {
1947 blockNodes = jqLite(slice.call(nodes, 0, i));
1949 blockNodes.push(node);
1953 return blockNodes || nodes;
1958 * Creates a new object without a prototype. This object is useful for lookup without having to
1959 * guard against prototypically inherited properties via hasOwnProperty.
1961 * Related micro-benchmarks:
1962 * - http://jsperf.com/object-create2
1963 * - http://jsperf.com/proto-map-lookup/2
1964 * - http://jsperf.com/for-in-vs-object-keys2
1968 function createMap() {
1969 return Object.create(null);
1972 var NODE_TYPE_ELEMENT = 1;
1973 var NODE_TYPE_ATTRIBUTE = 2;
1974 var NODE_TYPE_TEXT = 3;
1975 var NODE_TYPE_COMMENT = 8;
1976 var NODE_TYPE_DOCUMENT = 9;
1977 var NODE_TYPE_DOCUMENT_FRAGMENT = 11;
1981 * @name angular.Module
1985 * Interface for configuring angular {@link angular.module modules}.
1988 function setupModuleLoader(window) {
1990 var $injectorMinErr = minErr('$injector');
1991 var ngMinErr = minErr('ng');
1993 function ensure(obj, name, factory) {
1994 return obj[name] || (obj[name] = factory());
1997 var angular = ensure(window, 'angular', Object);
1999 // We need to expose `angular.$$minErr` to modules such as `ngResource` that reference it during bootstrap
2000 angular.$$minErr = angular.$$minErr || minErr;
2002 return ensure(angular, 'module', function() {
2003 /** @type {Object.<string, angular.Module>} */
2008 * @name angular.module
2012 * The `angular.module` is a global place for creating, registering and retrieving Angular
2014 * All modules (angular core or 3rd party) that should be available to an application must be
2015 * registered using this mechanism.
2017 * Passing one argument retrieves an existing {@link angular.Module},
2018 * whereas passing more than one argument creates a new {@link angular.Module}
2023 * A module is a collection of services, directives, controllers, filters, and configuration information.
2024 * `angular.module` is used to configure the {@link auto.$injector $injector}.
2027 * // Create a new module
2028 * var myModule = angular.module('myModule', []);
2030 * // register a new service
2031 * myModule.value('appName', 'MyCoolApp');
2033 * // configure existing services inside initialization blocks.
2034 * myModule.config(['$locationProvider', function($locationProvider) {
2035 * // Configure existing providers
2036 * $locationProvider.hashPrefix('!');
2040 * Then you can create an injector and load your modules like this:
2043 * var injector = angular.injector(['ng', 'myModule'])
2046 * However it's more likely that you'll just use
2047 * {@link ng.directive:ngApp ngApp} or
2048 * {@link angular.bootstrap} to simplify this process for you.
2050 * @param {!string} name The name of the module to create or retrieve.
2051 * @param {!Array.<string>=} requires If specified then new module is being created. If
2052 * unspecified then the module is being retrieved for further configuration.
2053 * @param {Function=} configFn Optional configuration function for the module. Same as
2054 * {@link angular.Module#config Module#config()}.
2055 * @returns {angular.Module} new module with the {@link angular.Module} api.
2057 return function module(name, requires, configFn) {
2058 var assertNotHasOwnProperty = function(name, context) {
2059 if (name === 'hasOwnProperty') {
2060 throw ngMinErr('badname', 'hasOwnProperty is not a valid {0} name', context);
2064 assertNotHasOwnProperty(name, 'module');
2065 if (requires && modules.hasOwnProperty(name)) {
2066 modules[name] = null;
2068 return ensure(modules, name, function() {
2070 throw $injectorMinErr('nomod', "Module '{0}' is not available! You either misspelled " +
2071 "the module name or forgot to load it. If registering a module ensure that you " +
2072 "specify the dependencies as the second argument.", name);
2075 /** @type {!Array.<Array.<*>>} */
2076 var invokeQueue = [];
2078 /** @type {!Array.<Function>} */
2079 var configBlocks = [];
2081 /** @type {!Array.<Function>} */
2084 var config = invokeLater('$injector', 'invoke', 'push', configBlocks);
2086 /** @type {angular.Module} */
2087 var moduleInstance = {
2089 _invokeQueue: invokeQueue,
2090 _configBlocks: configBlocks,
2091 _runBlocks: runBlocks,
2095 * @name angular.Module#requires
2099 * Holds the list of modules which the injector will load before the current module is
2106 * @name angular.Module#name
2110 * Name of the module.
2117 * @name angular.Module#provider
2119 * @param {string} name service name
2120 * @param {Function} providerType Construction function for creating new instance of the
2123 * See {@link auto.$provide#provider $provide.provider()}.
2125 provider: invokeLaterAndSetModuleName('$provide', 'provider'),
2129 * @name angular.Module#factory
2131 * @param {string} name service name
2132 * @param {Function} providerFunction Function for creating new instance of the service.
2134 * See {@link auto.$provide#factory $provide.factory()}.
2136 factory: invokeLaterAndSetModuleName('$provide', 'factory'),
2140 * @name angular.Module#service
2142 * @param {string} name service name
2143 * @param {Function} constructor A constructor function that will be instantiated.
2145 * See {@link auto.$provide#service $provide.service()}.
2147 service: invokeLaterAndSetModuleName('$provide', 'service'),
2151 * @name angular.Module#value
2153 * @param {string} name service name
2154 * @param {*} object Service instance object.
2156 * See {@link auto.$provide#value $provide.value()}.
2158 value: invokeLater('$provide', 'value'),
2162 * @name angular.Module#constant
2164 * @param {string} name constant name
2165 * @param {*} object Constant value.
2167 * Because the constants are fixed, they get applied before other provide methods.
2168 * See {@link auto.$provide#constant $provide.constant()}.
2170 constant: invokeLater('$provide', 'constant', 'unshift'),
2174 * @name angular.Module#decorator
2176 * @param {string} name The name of the service to decorate.
2177 * @param {Function} decorFn This function will be invoked when the service needs to be
2178 * instantiated and should return the decorated service instance.
2180 * See {@link auto.$provide#decorator $provide.decorator()}.
2182 decorator: invokeLaterAndSetModuleName('$provide', 'decorator'),
2186 * @name angular.Module#animation
2188 * @param {string} name animation name
2189 * @param {Function} animationFactory Factory function for creating new instance of an
2193 * **NOTE**: animations take effect only if the **ngAnimate** module is loaded.
2196 * Defines an animation hook that can be later used with
2197 * {@link $animate $animate} service and directives that use this service.
2200 * module.animation('.animation-name', function($inject1, $inject2) {
2202 * eventName : function(element, done) {
2203 * //code to run the animation
2204 * //once complete, then run done()
2205 * return function cancellationFunction(element) {
2206 * //code to cancel the animation
2213 * See {@link ng.$animateProvider#register $animateProvider.register()} and
2214 * {@link ngAnimate ngAnimate module} for more information.
2216 animation: invokeLaterAndSetModuleName('$animateProvider', 'register'),
2220 * @name angular.Module#filter
2222 * @param {string} name Filter name - this must be a valid angular expression identifier
2223 * @param {Function} filterFactory Factory function for creating new instance of filter.
2225 * See {@link ng.$filterProvider#register $filterProvider.register()}.
2227 * <div class="alert alert-warning">
2228 * **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`.
2229 * Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace
2230 * your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
2231 * (`myapp_subsection_filterx`).
2234 filter: invokeLaterAndSetModuleName('$filterProvider', 'register'),
2238 * @name angular.Module#controller
2240 * @param {string|Object} name Controller name, or an object map of controllers where the
2241 * keys are the names and the values are the constructors.
2242 * @param {Function} constructor Controller constructor function.
2244 * See {@link ng.$controllerProvider#register $controllerProvider.register()}.
2246 controller: invokeLaterAndSetModuleName('$controllerProvider', 'register'),
2250 * @name angular.Module#directive
2252 * @param {string|Object} name Directive name, or an object map of directives where the
2253 * keys are the names and the values are the factories.
2254 * @param {Function} directiveFactory Factory function for creating new instance of
2257 * See {@link ng.$compileProvider#directive $compileProvider.directive()}.
2259 directive: invokeLaterAndSetModuleName('$compileProvider', 'directive'),
2263 * @name angular.Module#component
2265 * @param {string} name Name of the component in camel-case (i.e. myComp which will match as my-comp)
2266 * @param {Object} options Component definition object (a simplified
2267 * {@link ng.$compile#directive-definition-object directive definition object})
2270 * See {@link ng.$compileProvider#component $compileProvider.component()}.
2272 component: invokeLaterAndSetModuleName('$compileProvider', 'component'),
2276 * @name angular.Module#config
2278 * @param {Function} configFn Execute this function on module load. Useful for service
2281 * Use this method to register work which needs to be performed on module loading.
2282 * For more about how to configure services, see
2283 * {@link providers#provider-recipe Provider Recipe}.
2289 * @name angular.Module#run
2291 * @param {Function} initializationFn Execute this function after injector creation.
2292 * Useful for application initialization.
2294 * Use this method to register work which should be performed when the injector is done
2295 * loading all modules.
2297 run: function(block) {
2298 runBlocks.push(block);
2307 return moduleInstance;
2310 * @param {string} provider
2311 * @param {string} method
2312 * @param {String=} insertMethod
2313 * @returns {angular.Module}
2315 function invokeLater(provider, method, insertMethod, queue) {
2316 if (!queue) queue = invokeQueue;
2318 queue[insertMethod || 'push']([provider, method, arguments]);
2319 return moduleInstance;
2324 * @param {string} provider
2325 * @param {string} method
2326 * @returns {angular.Module}
2328 function invokeLaterAndSetModuleName(provider, method) {
2329 return function(recipeName, factoryFunction) {
2330 if (factoryFunction && isFunction(factoryFunction)) factoryFunction.$$moduleName = name;
2331 invokeQueue.push([provider, method, arguments]);
2332 return moduleInstance;
2341 /* global: toDebugString: true */
2343 function serializeObject(obj) {
2346 return JSON.stringify(obj, function(key, val) {
2347 val = toJsonReplacer(key, val);
2348 if (isObject(val)) {
2350 if (seen.indexOf(val) >= 0) return '...';
2358 function toDebugString(obj) {
2359 if (typeof obj === 'function') {
2360 return obj.toString().replace(/ \{[\s\S]*$/, '');
2361 } else if (isUndefined(obj)) {
2363 } else if (typeof obj !== 'string') {
2364 return serializeObject(obj);
2369 /* global angularModule: true,
2374 htmlAnchorDirective,
2383 ngBindHtmlDirective,
2384 ngBindTemplateDirective,
2386 ngClassEvenDirective,
2387 ngClassOddDirective,
2389 ngControllerDirective,
2394 ngIncludeFillContentDirective,
2396 ngNonBindableDirective,
2397 ngPluralizeDirective,
2402 ngSwitchWhenDirective,
2403 ngSwitchDefaultDirective,
2405 ngTranscludeDirective,
2418 ngModelOptionsDirective,
2419 ngAttributeAliasDirectives,
2422 $AnchorScrollProvider,
2424 $CoreAnimateCssProvider,
2425 $$CoreAnimateJsProvider,
2426 $$CoreAnimateQueueProvider,
2427 $$AnimateRunnerFactoryProvider,
2428 $$AnimateAsyncRunFactoryProvider,
2430 $CacheFactoryProvider,
2431 $ControllerProvider,
2434 $ExceptionHandlerProvider,
2436 $$ForceReflowProvider,
2437 $InterpolateProvider,
2441 $HttpParamSerializerProvider,
2442 $HttpParamSerializerJQLikeProvider,
2443 $HttpBackendProvider,
2444 $xhrFactoryProvider,
2451 $$SanitizeUriProvider,
2453 $SceDelegateProvider,
2455 $TemplateCacheProvider,
2456 $TemplateRequestProvider,
2457 $$TestabilityProvider,
2462 $$CookieReaderProvider
2468 * @name angular.version
2471 * An object that contains information about the current AngularJS version.
2473 * This object has the following properties:
2475 * - `full` – `{string}` – Full version string, such as "0.9.18".
2476 * - `major` – `{number}` – Major version number, such as "0".
2477 * - `minor` – `{number}` – Minor version number, such as "9".
2478 * - `dot` – `{number}` – Dot version number, such as "18".
2479 * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat".
2482 full: '1.5.5', // all of these placeholder strings will be replaced by grunt's
2483 major: 1, // package task
2486 codeName: 'material-conspiration'
2490 function publishExternalAPI(angular) {
2492 'bootstrap': bootstrap,
2499 'injector': createInjector,
2503 'fromJson': fromJson,
2504 'identity': identity,
2505 'isUndefined': isUndefined,
2506 'isDefined': isDefined,
2507 'isString': isString,
2508 'isFunction': isFunction,
2509 'isObject': isObject,
2510 'isNumber': isNumber,
2511 'isElement': isElement,
2515 'lowercase': lowercase,
2516 'uppercase': uppercase,
2517 'callbacks': {counter: 0},
2518 'getTestability': getTestability,
2521 'reloadWithDebugInfo': reloadWithDebugInfo
2524 angularModule = setupModuleLoader(window);
2526 angularModule('ng', ['ngLocale'], ['$provide',
2527 function ngModule($provide) {
2528 // $$sanitizeUriProvider needs to be before $compileProvider as it is used by it.
2530 $$sanitizeUri: $$SanitizeUriProvider
2532 $provide.provider('$compile', $CompileProvider).
2534 a: htmlAnchorDirective,
2535 input: inputDirective,
2536 textarea: inputDirective,
2537 form: formDirective,
2538 script: scriptDirective,
2539 select: selectDirective,
2540 style: styleDirective,
2541 option: optionDirective,
2542 ngBind: ngBindDirective,
2543 ngBindHtml: ngBindHtmlDirective,
2544 ngBindTemplate: ngBindTemplateDirective,
2545 ngClass: ngClassDirective,
2546 ngClassEven: ngClassEvenDirective,
2547 ngClassOdd: ngClassOddDirective,
2548 ngCloak: ngCloakDirective,
2549 ngController: ngControllerDirective,
2550 ngForm: ngFormDirective,
2551 ngHide: ngHideDirective,
2552 ngIf: ngIfDirective,
2553 ngInclude: ngIncludeDirective,
2554 ngInit: ngInitDirective,
2555 ngNonBindable: ngNonBindableDirective,
2556 ngPluralize: ngPluralizeDirective,
2557 ngRepeat: ngRepeatDirective,
2558 ngShow: ngShowDirective,
2559 ngStyle: ngStyleDirective,
2560 ngSwitch: ngSwitchDirective,
2561 ngSwitchWhen: ngSwitchWhenDirective,
2562 ngSwitchDefault: ngSwitchDefaultDirective,
2563 ngOptions: ngOptionsDirective,
2564 ngTransclude: ngTranscludeDirective,
2565 ngModel: ngModelDirective,
2566 ngList: ngListDirective,
2567 ngChange: ngChangeDirective,
2568 pattern: patternDirective,
2569 ngPattern: patternDirective,
2570 required: requiredDirective,
2571 ngRequired: requiredDirective,
2572 minlength: minlengthDirective,
2573 ngMinlength: minlengthDirective,
2574 maxlength: maxlengthDirective,
2575 ngMaxlength: maxlengthDirective,
2576 ngValue: ngValueDirective,
2577 ngModelOptions: ngModelOptionsDirective
2580 ngInclude: ngIncludeFillContentDirective
2582 directive(ngAttributeAliasDirectives).
2583 directive(ngEventDirectives);
2585 $anchorScroll: $AnchorScrollProvider,
2586 $animate: $AnimateProvider,
2587 $animateCss: $CoreAnimateCssProvider,
2588 $$animateJs: $$CoreAnimateJsProvider,
2589 $$animateQueue: $$CoreAnimateQueueProvider,
2590 $$AnimateRunner: $$AnimateRunnerFactoryProvider,
2591 $$animateAsyncRun: $$AnimateAsyncRunFactoryProvider,
2592 $browser: $BrowserProvider,
2593 $cacheFactory: $CacheFactoryProvider,
2594 $controller: $ControllerProvider,
2595 $document: $DocumentProvider,
2596 $exceptionHandler: $ExceptionHandlerProvider,
2597 $filter: $FilterProvider,
2598 $$forceReflow: $$ForceReflowProvider,
2599 $interpolate: $InterpolateProvider,
2600 $interval: $IntervalProvider,
2601 $http: $HttpProvider,
2602 $httpParamSerializer: $HttpParamSerializerProvider,
2603 $httpParamSerializerJQLike: $HttpParamSerializerJQLikeProvider,
2604 $httpBackend: $HttpBackendProvider,
2605 $xhrFactory: $xhrFactoryProvider,
2606 $location: $LocationProvider,
2608 $parse: $ParseProvider,
2609 $rootScope: $RootScopeProvider,
2613 $sceDelegate: $SceDelegateProvider,
2614 $sniffer: $SnifferProvider,
2615 $templateCache: $TemplateCacheProvider,
2616 $templateRequest: $TemplateRequestProvider,
2617 $$testability: $$TestabilityProvider,
2618 $timeout: $TimeoutProvider,
2619 $window: $WindowProvider,
2620 $$rAF: $$RAFProvider,
2621 $$jqLite: $$jqLiteProvider,
2622 $$HashMap: $$HashMapProvider,
2623 $$cookieReader: $$CookieReaderProvider
2629 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2630 * Any commits to this file should be reviewed with security in mind. *
2631 * Changes to this file can potentially create security vulnerabilities. *
2632 * An approval from 2 Core members with history of modifying *
2633 * this file is required. *
2635 * Does the change somehow allow for arbitrary javascript to be executed? *
2636 * Or allows for someone to change the prototype of built-in objects? *
2637 * Or gives undesired access to variables likes document or window? *
2638 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2640 /* global JQLitePrototype: true,
2641 addEventListenerFn: true,
2642 removeEventListenerFn: true,
2647 //////////////////////////////////
2649 //////////////////////////////////
2653 * @name angular.element
2658 * Wraps a raw DOM element or HTML string as a [jQuery](http://jquery.com) element.
2660 * If jQuery is available, `angular.element` is an alias for the
2661 * [jQuery](http://api.jquery.com/jQuery/) function. If jQuery is not available, `angular.element`
2662 * delegates to Angular's built-in subset of jQuery, called "jQuery lite" or **jqLite**.
2664 * jqLite is a tiny, API-compatible subset of jQuery that allows
2665 * Angular to manipulate the DOM in a cross-browser compatible way. jqLite implements only the most
2666 * commonly needed functionality with the goal of having a very small footprint.
2668 * To use `jQuery`, simply ensure it is loaded before the `angular.js` file. You can also use the
2669 * {@link ngJq `ngJq`} directive to specify that jqlite should be used over jQuery, or to use a
2670 * specific version of jQuery if multiple versions exist on the page.
2672 * <div class="alert alert-info">**Note:** All element references in Angular are always wrapped with jQuery or
2673 * jqLite (such as the element argument in a directive's compile / link function). They are never raw DOM references.</div>
2675 * <div class="alert alert-warning">**Note:** Keep in mind that this function will not find elements
2676 * by tag name / CSS selector. For lookups by tag name, try instead `angular.element(document).find(...)`
2677 * or `$document.find()`, or use the standard DOM APIs, e.g. `document.querySelectorAll()`.</div>
2679 * ## Angular's jqLite
2680 * jqLite provides only the following jQuery methods:
2682 * - [`addClass()`](http://api.jquery.com/addClass/)
2683 * - [`after()`](http://api.jquery.com/after/)
2684 * - [`append()`](http://api.jquery.com/append/)
2685 * - [`attr()`](http://api.jquery.com/attr/) - Does not support functions as parameters
2686 * - [`bind()`](http://api.jquery.com/bind/) - Does not support namespaces, selectors or eventData
2687 * - [`children()`](http://api.jquery.com/children/) - Does not support selectors
2688 * - [`clone()`](http://api.jquery.com/clone/)
2689 * - [`contents()`](http://api.jquery.com/contents/)
2690 * - [`css()`](http://api.jquery.com/css/) - Only retrieves inline-styles, does not call `getComputedStyle()`.
2691 * As a setter, does not convert numbers to strings or append 'px', and also does not have automatic property prefixing.
2692 * - [`data()`](http://api.jquery.com/data/)
2693 * - [`detach()`](http://api.jquery.com/detach/)
2694 * - [`empty()`](http://api.jquery.com/empty/)
2695 * - [`eq()`](http://api.jquery.com/eq/)
2696 * - [`find()`](http://api.jquery.com/find/) - Limited to lookups by tag name
2697 * - [`hasClass()`](http://api.jquery.com/hasClass/)
2698 * - [`html()`](http://api.jquery.com/html/)
2699 * - [`next()`](http://api.jquery.com/next/) - Does not support selectors
2700 * - [`on()`](http://api.jquery.com/on/) - Does not support namespaces, selectors or eventData
2701 * - [`off()`](http://api.jquery.com/off/) - Does not support namespaces, selectors or event object as parameter
2702 * - [`one()`](http://api.jquery.com/one/) - Does not support namespaces or selectors
2703 * - [`parent()`](http://api.jquery.com/parent/) - Does not support selectors
2704 * - [`prepend()`](http://api.jquery.com/prepend/)
2705 * - [`prop()`](http://api.jquery.com/prop/)
2706 * - [`ready()`](http://api.jquery.com/ready/)
2707 * - [`remove()`](http://api.jquery.com/remove/)
2708 * - [`removeAttr()`](http://api.jquery.com/removeAttr/)
2709 * - [`removeClass()`](http://api.jquery.com/removeClass/)
2710 * - [`removeData()`](http://api.jquery.com/removeData/)
2711 * - [`replaceWith()`](http://api.jquery.com/replaceWith/)
2712 * - [`text()`](http://api.jquery.com/text/)
2713 * - [`toggleClass()`](http://api.jquery.com/toggleClass/)
2714 * - [`triggerHandler()`](http://api.jquery.com/triggerHandler/) - Passes a dummy event object to handlers.
2715 * - [`unbind()`](http://api.jquery.com/unbind/) - Does not support namespaces or event object as parameter
2716 * - [`val()`](http://api.jquery.com/val/)
2717 * - [`wrap()`](http://api.jquery.com/wrap/)
2719 * ## jQuery/jqLite Extras
2720 * Angular also provides the following additional methods and events to both jQuery and jqLite:
2723 * - `$destroy` - AngularJS intercepts all jqLite/jQuery's DOM destruction apis and fires this event
2724 * on all DOM nodes being removed. This can be used to clean up any 3rd party bindings to the DOM
2725 * element before it is removed.
2728 * - `controller(name)` - retrieves the controller of the current element or its parent. By default
2729 * retrieves controller associated with the `ngController` directive. If `name` is provided as
2730 * camelCase directive name, then the controller for this directive will be retrieved (e.g.
2732 * - `injector()` - retrieves the injector of the current element or its parent.
2733 * - `scope()` - retrieves the {@link ng.$rootScope.Scope scope} of the current
2734 * element or its parent. Requires {@link guide/production#disabling-debug-data Debug Data} to
2736 * - `isolateScope()` - retrieves an isolate {@link ng.$rootScope.Scope scope} if one is attached directly to the
2737 * current element. This getter should be used only on elements that contain a directive which starts a new isolate
2738 * scope. Calling `scope()` on this element always returns the original non-isolate scope.
2739 * Requires {@link guide/production#disabling-debug-data Debug Data} to be enabled.
2740 * - `inheritedData()` - same as `data()`, but walks up the DOM until a value is found or the top
2741 * parent element is reached.
2743 * @knownIssue You cannot spy on `angular.element` if you are using Jasmine version 1.x. See
2744 * https://github.com/angular/angular.js/issues/14251 for more information.
2746 * @param {string|DOMElement} element HTML string or DOMElement to be wrapped into jQuery.
2747 * @returns {Object} jQuery object.
2750 JQLite.expando = 'ng339';
2752 var jqCache = JQLite.cache = {},
2754 addEventListenerFn = function(element, type, fn) {
2755 element.addEventListener(type, fn, false);
2757 removeEventListenerFn = function(element, type, fn) {
2758 element.removeEventListener(type, fn, false);
2762 * !!! This is an undocumented "private" function !!!
2764 JQLite._data = function(node) {
2765 //jQuery always returns an object on cache miss
2766 return this.cache[node[this.expando]] || {};
2769 function jqNextId() { return ++jqId; }
2772 var SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g;
2773 var MOZ_HACK_REGEXP = /^moz([A-Z])/;
2774 var MOUSE_EVENT_MAP= { mouseleave: "mouseout", mouseenter: "mouseover"};
2775 var jqLiteMinErr = minErr('jqLite');
2778 * Converts snake_case to camelCase.
2779 * Also there is special case for Moz prefix starting with upper case letter.
2780 * @param name Name to normalize
2782 function camelCase(name) {
2784 replace(SPECIAL_CHARS_REGEXP, function(_, separator, letter, offset) {
2785 return offset ? letter.toUpperCase() : letter;
2787 replace(MOZ_HACK_REGEXP, 'Moz$1');
2790 var SINGLE_TAG_REGEXP = /^<([\w-]+)\s*\/?>(?:<\/\1>|)$/;
2791 var HTML_REGEXP = /<|&#?\w+;/;
2792 var TAG_NAME_REGEXP = /<([\w:-]+)/;
2793 var XHTML_TAG_REGEXP = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^>]*)\/>/gi;
2796 'option': [1, '<select multiple="multiple">', '</select>'],
2798 'thead': [1, '<table>', '</table>'],
2799 'col': [2, '<table><colgroup>', '</colgroup></table>'],
2800 'tr': [2, '<table><tbody>', '</tbody></table>'],
2801 'td': [3, '<table><tbody><tr>', '</tr></tbody></table>'],
2802 '_default': [0, "", ""]
2805 wrapMap.optgroup = wrapMap.option;
2806 wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
2807 wrapMap.th = wrapMap.td;
2810 function jqLiteIsTextNode(html) {
2811 return !HTML_REGEXP.test(html);
2814 function jqLiteAcceptsData(node) {
2815 // The window object can accept data but has no nodeType
2816 // Otherwise we are only interested in elements (1) and documents (9)
2817 var nodeType = node.nodeType;
2818 return nodeType === NODE_TYPE_ELEMENT || !nodeType || nodeType === NODE_TYPE_DOCUMENT;
2821 function jqLiteHasData(node) {
2822 for (var key in jqCache[node.ng339]) {
2828 function jqLiteCleanData(nodes) {
2829 for (var i = 0, ii = nodes.length; i < ii; i++) {
2830 jqLiteRemoveData(nodes[i]);
2834 function jqLiteBuildFragment(html, context) {
2836 fragment = context.createDocumentFragment(),
2839 if (jqLiteIsTextNode(html)) {
2840 // Convert non-html into a text node
2841 nodes.push(context.createTextNode(html));
2843 // Convert html into DOM nodes
2844 tmp = tmp || fragment.appendChild(context.createElement("div"));
2845 tag = (TAG_NAME_REGEXP.exec(html) || ["", ""])[1].toLowerCase();
2846 wrap = wrapMap[tag] || wrapMap._default;
2847 tmp.innerHTML = wrap[1] + html.replace(XHTML_TAG_REGEXP, "<$1></$2>") + wrap[2];
2849 // Descend through wrappers to the right content
2852 tmp = tmp.lastChild;
2855 nodes = concat(nodes, tmp.childNodes);
2857 tmp = fragment.firstChild;
2858 tmp.textContent = "";
2861 // Remove wrapper from fragment
2862 fragment.textContent = "";
2863 fragment.innerHTML = ""; // Clear inner HTML
2864 forEach(nodes, function(node) {
2865 fragment.appendChild(node);
2871 function jqLiteParseHTML(html, context) {
2872 context = context || window.document;
2875 if ((parsed = SINGLE_TAG_REGEXP.exec(html))) {
2876 return [context.createElement(parsed[1])];
2879 if ((parsed = jqLiteBuildFragment(html, context))) {
2880 return parsed.childNodes;
2886 function jqLiteWrapNode(node, wrapper) {
2887 var parent = node.parentNode;
2890 parent.replaceChild(wrapper, node);
2893 wrapper.appendChild(node);
2897 // IE9-11 has no method "contains" in SVG element and in Node.prototype. Bug #10259.
2898 var jqLiteContains = window.Node.prototype.contains || function(arg) {
2899 // jshint bitwise: false
2900 return !!(this.compareDocumentPosition(arg) & 16);
2901 // jshint bitwise: true
2904 /////////////////////////////////////////////
2905 function JQLite(element) {
2906 if (element instanceof JQLite) {
2912 if (isString(element)) {
2913 element = trim(element);
2916 if (!(this instanceof JQLite)) {
2917 if (argIsString && element.charAt(0) != '<') {
2918 throw jqLiteMinErr('nosel', 'Looking up elements via selectors is not supported by jqLite! See: http://docs.angularjs.org/api/angular.element');
2920 return new JQLite(element);
2924 jqLiteAddNodes(this, jqLiteParseHTML(element));
2926 jqLiteAddNodes(this, element);
2930 function jqLiteClone(element) {
2931 return element.cloneNode(true);
2934 function jqLiteDealoc(element, onlyDescendants) {
2935 if (!onlyDescendants) jqLiteRemoveData(element);
2937 if (element.querySelectorAll) {
2938 var descendants = element.querySelectorAll('*');
2939 for (var i = 0, l = descendants.length; i < l; i++) {
2940 jqLiteRemoveData(descendants[i]);
2945 function jqLiteOff(element, type, fn, unsupported) {
2946 if (isDefined(unsupported)) throw jqLiteMinErr('offargs', 'jqLite#off() does not support the `selector` argument');
2948 var expandoStore = jqLiteExpandoStore(element);
2949 var events = expandoStore && expandoStore.events;
2950 var handle = expandoStore && expandoStore.handle;
2952 if (!handle) return; //no listeners registered
2955 for (type in events) {
2956 if (type !== '$destroy') {
2957 removeEventListenerFn(element, type, handle);
2959 delete events[type];
2963 var removeHandler = function(type) {
2964 var listenerFns = events[type];
2965 if (isDefined(fn)) {
2966 arrayRemove(listenerFns || [], fn);
2968 if (!(isDefined(fn) && listenerFns && listenerFns.length > 0)) {
2969 removeEventListenerFn(element, type, handle);
2970 delete events[type];
2974 forEach(type.split(' '), function(type) {
2975 removeHandler(type);
2976 if (MOUSE_EVENT_MAP[type]) {
2977 removeHandler(MOUSE_EVENT_MAP[type]);
2983 function jqLiteRemoveData(element, name) {
2984 var expandoId = element.ng339;
2985 var expandoStore = expandoId && jqCache[expandoId];
2989 delete expandoStore.data[name];
2993 if (expandoStore.handle) {
2994 if (expandoStore.events.$destroy) {
2995 expandoStore.handle({}, '$destroy');
2999 delete jqCache[expandoId];
3000 element.ng339 = undefined; // don't delete DOM expandos. IE and Chrome don't like it
3005 function jqLiteExpandoStore(element, createIfNecessary) {
3006 var expandoId = element.ng339,
3007 expandoStore = expandoId && jqCache[expandoId];
3009 if (createIfNecessary && !expandoStore) {
3010 element.ng339 = expandoId = jqNextId();
3011 expandoStore = jqCache[expandoId] = {events: {}, data: {}, handle: undefined};
3014 return expandoStore;
3018 function jqLiteData(element, key, value) {
3019 if (jqLiteAcceptsData(element)) {
3021 var isSimpleSetter = isDefined(value);
3022 var isSimpleGetter = !isSimpleSetter && key && !isObject(key);
3023 var massGetter = !key;
3024 var expandoStore = jqLiteExpandoStore(element, !isSimpleGetter);
3025 var data = expandoStore && expandoStore.data;
3027 if (isSimpleSetter) { // data('key', value)
3030 if (massGetter) { // data()
3033 if (isSimpleGetter) { // data('key')
3034 // don't force creation of expandoStore if it doesn't exist yet
3035 return data && data[key];
3036 } else { // mass-setter: data({key1: val1, key2: val2})
3044 function jqLiteHasClass(element, selector) {
3045 if (!element.getAttribute) return false;
3046 return ((" " + (element.getAttribute('class') || '') + " ").replace(/[\n\t]/g, " ").
3047 indexOf(" " + selector + " ") > -1);
3050 function jqLiteRemoveClass(element, cssClasses) {
3051 if (cssClasses && element.setAttribute) {
3052 forEach(cssClasses.split(' '), function(cssClass) {
3053 element.setAttribute('class', trim(
3054 (" " + (element.getAttribute('class') || '') + " ")
3055 .replace(/[\n\t]/g, " ")
3056 .replace(" " + trim(cssClass) + " ", " "))
3062 function jqLiteAddClass(element, cssClasses) {
3063 if (cssClasses && element.setAttribute) {
3064 var existingClasses = (' ' + (element.getAttribute('class') || '') + ' ')
3065 .replace(/[\n\t]/g, " ");
3067 forEach(cssClasses.split(' '), function(cssClass) {
3068 cssClass = trim(cssClass);
3069 if (existingClasses.indexOf(' ' + cssClass + ' ') === -1) {
3070 existingClasses += cssClass + ' ';
3074 element.setAttribute('class', trim(existingClasses));
3079 function jqLiteAddNodes(root, elements) {
3080 // THIS CODE IS VERY HOT. Don't make changes without benchmarking.
3084 // if a Node (the most common case)
3085 if (elements.nodeType) {
3086 root[root.length++] = elements;
3088 var length = elements.length;
3090 // if an Array or NodeList and not a Window
3091 if (typeof length === 'number' && elements.window !== elements) {
3093 for (var i = 0; i < length; i++) {
3094 root[root.length++] = elements[i];
3098 root[root.length++] = elements;
3105 function jqLiteController(element, name) {
3106 return jqLiteInheritedData(element, '$' + (name || 'ngController') + 'Controller');
3109 function jqLiteInheritedData(element, name, value) {
3110 // if element is the document object work with the html element instead
3111 // this makes $(document).scope() possible
3112 if (element.nodeType == NODE_TYPE_DOCUMENT) {
3113 element = element.documentElement;
3115 var names = isArray(name) ? name : [name];
3118 for (var i = 0, ii = names.length; i < ii; i++) {
3119 if (isDefined(value = jqLite.data(element, names[i]))) return value;
3122 // If dealing with a document fragment node with a host element, and no parent, use the host
3123 // element as the parent. This enables directives within a Shadow DOM or polyfilled Shadow DOM
3124 // to lookup parent controllers.
3125 element = element.parentNode || (element.nodeType === NODE_TYPE_DOCUMENT_FRAGMENT && element.host);
3129 function jqLiteEmpty(element) {
3130 jqLiteDealoc(element, true);
3131 while (element.firstChild) {
3132 element.removeChild(element.firstChild);
3136 function jqLiteRemove(element, keepData) {
3137 if (!keepData) jqLiteDealoc(element);
3138 var parent = element.parentNode;
3139 if (parent) parent.removeChild(element);
3143 function jqLiteDocumentLoaded(action, win) {
3144 win = win || window;
3145 if (win.document.readyState === 'complete') {
3146 // Force the action to be run async for consistent behavior
3147 // from the action's point of view
3148 // i.e. it will definitely not be in a $apply
3149 win.setTimeout(action);
3151 // No need to unbind this handler as load is only ever called once
3152 jqLite(win).on('load', action);
3156 //////////////////////////////////////////
3157 // Functions which are declared directly.
3158 //////////////////////////////////////////
3159 var JQLitePrototype = JQLite.prototype = {
3160 ready: function(fn) {
3163 function trigger() {
3169 // check if document is already loaded
3170 if (window.document.readyState === 'complete') {
3171 window.setTimeout(trigger);
3173 this.on('DOMContentLoaded', trigger); // works for modern browsers and IE9
3174 // we can not use jqLite since we are not done loading and jQuery could be loaded later.
3176 JQLite(window).on('load', trigger); // fallback to window.onload for others
3180 toString: function() {
3182 forEach(this, function(e) { value.push('' + e);});
3183 return '[' + value.join(', ') + ']';
3186 eq: function(index) {
3187 return (index >= 0) ? jqLite(this[index]) : jqLite(this[this.length + index]);
3196 //////////////////////////////////////////
3197 // Functions iterating getter/setters.
3198 // these functions return self on setter and
3200 //////////////////////////////////////////
3201 var BOOLEAN_ATTR = {};
3202 forEach('multiple,selected,checked,disabled,readOnly,required,open'.split(','), function(value) {
3203 BOOLEAN_ATTR[lowercase(value)] = value;
3205 var BOOLEAN_ELEMENTS = {};
3206 forEach('input,select,option,textarea,button,form,details'.split(','), function(value) {
3207 BOOLEAN_ELEMENTS[value] = true;
3209 var ALIASED_ATTR = {
3210 'ngMinlength': 'minlength',
3211 'ngMaxlength': 'maxlength',
3214 'ngPattern': 'pattern'
3217 function getBooleanAttrName(element, name) {
3218 // check dom last since we will most likely fail on name
3219 var booleanAttr = BOOLEAN_ATTR[name.toLowerCase()];
3221 // booleanAttr is here twice to minimize DOM access
3222 return booleanAttr && BOOLEAN_ELEMENTS[nodeName_(element)] && booleanAttr;
3225 function getAliasedAttrName(name) {
3226 return ALIASED_ATTR[name];
3231 removeData: jqLiteRemoveData,
3232 hasData: jqLiteHasData,
3233 cleanData: jqLiteCleanData
3234 }, function(fn, name) {
3240 inheritedData: jqLiteInheritedData,
3242 scope: function(element) {
3243 // Can't use jqLiteData here directly so we stay compatible with jQuery!
3244 return jqLite.data(element, '$scope') || jqLiteInheritedData(element.parentNode || element, ['$isolateScope', '$scope']);
3247 isolateScope: function(element) {
3248 // Can't use jqLiteData here directly so we stay compatible with jQuery!
3249 return jqLite.data(element, '$isolateScope') || jqLite.data(element, '$isolateScopeNoTemplate');
3252 controller: jqLiteController,
3254 injector: function(element) {
3255 return jqLiteInheritedData(element, '$injector');
3258 removeAttr: function(element, name) {
3259 element.removeAttribute(name);
3262 hasClass: jqLiteHasClass,
3264 css: function(element, name, value) {
3265 name = camelCase(name);
3267 if (isDefined(value)) {
3268 element.style[name] = value;
3270 return element.style[name];
3274 attr: function(element, name, value) {
3275 var nodeType = element.nodeType;
3276 if (nodeType === NODE_TYPE_TEXT || nodeType === NODE_TYPE_ATTRIBUTE || nodeType === NODE_TYPE_COMMENT) {
3279 var lowercasedName = lowercase(name);
3280 if (BOOLEAN_ATTR[lowercasedName]) {
3281 if (isDefined(value)) {
3283 element[name] = true;
3284 element.setAttribute(name, lowercasedName);
3286 element[name] = false;
3287 element.removeAttribute(lowercasedName);
3290 return (element[name] ||
3291 (element.attributes.getNamedItem(name) || noop).specified)
3295 } else if (isDefined(value)) {
3296 element.setAttribute(name, value);
3297 } else if (element.getAttribute) {
3298 // the extra argument "2" is to get the right thing for a.href in IE, see jQuery code
3299 // some elements (e.g. Document) don't have get attribute, so return undefined
3300 var ret = element.getAttribute(name, 2);
3301 // normalize non-existing attributes to undefined (as jQuery)
3302 return ret === null ? undefined : ret;
3306 prop: function(element, name, value) {
3307 if (isDefined(value)) {
3308 element[name] = value;
3310 return element[name];
3318 function getText(element, value) {
3319 if (isUndefined(value)) {
3320 var nodeType = element.nodeType;
3321 return (nodeType === NODE_TYPE_ELEMENT || nodeType === NODE_TYPE_TEXT) ? element.textContent : '';
3323 element.textContent = value;
3327 val: function(element, value) {
3328 if (isUndefined(value)) {
3329 if (element.multiple && nodeName_(element) === 'select') {
3331 forEach(element.options, function(option) {
3332 if (option.selected) {
3333 result.push(option.value || option.text);
3336 return result.length === 0 ? null : result;
3338 return element.value;
3340 element.value = value;
3343 html: function(element, value) {
3344 if (isUndefined(value)) {
3345 return element.innerHTML;
3347 jqLiteDealoc(element, true);
3348 element.innerHTML = value;
3352 }, function(fn, name) {
3354 * Properties: writes return selection, reads return first value
3356 JQLite.prototype[name] = function(arg1, arg2) {
3358 var nodeCount = this.length;
3360 // jqLiteHasClass has only two arguments, but is a getter-only fn, so we need to special-case it
3361 // in a way that survives minification.
3362 // jqLiteEmpty takes no arguments but is a setter.
3363 if (fn !== jqLiteEmpty &&
3364 (isUndefined((fn.length == 2 && (fn !== jqLiteHasClass && fn !== jqLiteController)) ? arg1 : arg2))) {
3365 if (isObject(arg1)) {
3367 // we are a write, but the object properties are the key/values
3368 for (i = 0; i < nodeCount; i++) {
3369 if (fn === jqLiteData) {
3370 // data() takes the whole object in jQuery
3374 fn(this[i], key, arg1[key]);
3378 // return self for chaining
3381 // we are a read, so read the first child.
3382 // TODO: do we still need this?
3384 // Only if we have $dv do we iterate over all, otherwise it is just the first element.
3385 var jj = (isUndefined(value)) ? Math.min(nodeCount, 1) : nodeCount;
3386 for (var j = 0; j < jj; j++) {
3387 var nodeValue = fn(this[j], arg1, arg2);
3388 value = value ? value + nodeValue : nodeValue;
3393 // we are a write, so apply to all children
3394 for (i = 0; i < nodeCount; i++) {
3395 fn(this[i], arg1, arg2);
3397 // return self for chaining
3403 function createEventHandler(element, events) {
3404 var eventHandler = function(event, type) {
3405 // jQuery specific api
3406 event.isDefaultPrevented = function() {
3407 return event.defaultPrevented;
3410 var eventFns = events[type || event.type];
3411 var eventFnsLength = eventFns ? eventFns.length : 0;
3413 if (!eventFnsLength) return;
3415 if (isUndefined(event.immediatePropagationStopped)) {
3416 var originalStopImmediatePropagation = event.stopImmediatePropagation;
3417 event.stopImmediatePropagation = function() {
3418 event.immediatePropagationStopped = true;
3420 if (event.stopPropagation) {
3421 event.stopPropagation();
3424 if (originalStopImmediatePropagation) {
3425 originalStopImmediatePropagation.call(event);
3430 event.isImmediatePropagationStopped = function() {
3431 return event.immediatePropagationStopped === true;
3434 // Some events have special handlers that wrap the real handler
3435 var handlerWrapper = eventFns.specialHandlerWrapper || defaultHandlerWrapper;
3437 // Copy event handlers in case event handlers array is modified during execution.
3438 if ((eventFnsLength > 1)) {
3439 eventFns = shallowCopy(eventFns);
3442 for (var i = 0; i < eventFnsLength; i++) {
3443 if (!event.isImmediatePropagationStopped()) {
3444 handlerWrapper(element, event, eventFns[i]);
3449 // TODO: this is a hack for angularMocks/clearDataCache that makes it possible to deregister all
3450 // events on `element`
3451 eventHandler.elem = element;
3452 return eventHandler;
3455 function defaultHandlerWrapper(element, event, handler) {
3456 handler.call(element, event);
3459 function specialMouseHandlerWrapper(target, event, handler) {
3460 // Refer to jQuery's implementation of mouseenter & mouseleave
3461 // Read about mouseenter and mouseleave:
3462 // http://www.quirksmode.org/js/events_mouse.html#link8
3463 var related = event.relatedTarget;
3464 // For mousenter/leave call the handler if related is outside the target.
3465 // NB: No relatedTarget if the mouse left/entered the browser window
3466 if (!related || (related !== target && !jqLiteContains.call(target, related))) {
3467 handler.call(target, event);
3471 //////////////////////////////////////////
3472 // Functions iterating traversal.
3473 // These functions chain results into a single
3475 //////////////////////////////////////////
3477 removeData: jqLiteRemoveData,
3479 on: function jqLiteOn(element, type, fn, unsupported) {
3480 if (isDefined(unsupported)) throw jqLiteMinErr('onargs', 'jqLite#on() does not support the `selector` or `eventData` parameters');
3482 // Do not add event handlers to non-elements because they will not be cleaned up.
3483 if (!jqLiteAcceptsData(element)) {
3487 var expandoStore = jqLiteExpandoStore(element, true);
3488 var events = expandoStore.events;
3489 var handle = expandoStore.handle;
3492 handle = expandoStore.handle = createEventHandler(element, events);
3495 // http://jsperf.com/string-indexof-vs-split
3496 var types = type.indexOf(' ') >= 0 ? type.split(' ') : [type];
3497 var i = types.length;
3499 var addHandler = function(type, specialHandlerWrapper, noEventListener) {
3500 var eventFns = events[type];
3503 eventFns = events[type] = [];
3504 eventFns.specialHandlerWrapper = specialHandlerWrapper;
3505 if (type !== '$destroy' && !noEventListener) {
3506 addEventListenerFn(element, type, handle);
3515 if (MOUSE_EVENT_MAP[type]) {
3516 addHandler(MOUSE_EVENT_MAP[type], specialMouseHandlerWrapper);
3517 addHandler(type, undefined, true);
3526 one: function(element, type, fn) {
3527 element = jqLite(element);
3529 //add the listener twice so that when it is called
3530 //you can remove the original function and still be
3531 //able to call element.off(ev, fn) normally
3532 element.on(type, function onFn() {
3533 element.off(type, fn);
3534 element.off(type, onFn);
3536 element.on(type, fn);
3539 replaceWith: function(element, replaceNode) {
3540 var index, parent = element.parentNode;
3541 jqLiteDealoc(element);
3542 forEach(new JQLite(replaceNode), function(node) {
3544 parent.insertBefore(node, index.nextSibling);
3546 parent.replaceChild(node, element);
3552 children: function(element) {
3554 forEach(element.childNodes, function(element) {
3555 if (element.nodeType === NODE_TYPE_ELEMENT) {
3556 children.push(element);
3562 contents: function(element) {
3563 return element.contentDocument || element.childNodes || [];
3566 append: function(element, node) {
3567 var nodeType = element.nodeType;
3568 if (nodeType !== NODE_TYPE_ELEMENT && nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT) return;
3570 node = new JQLite(node);
3572 for (var i = 0, ii = node.length; i < ii; i++) {
3573 var child = node[i];
3574 element.appendChild(child);
3578 prepend: function(element, node) {
3579 if (element.nodeType === NODE_TYPE_ELEMENT) {
3580 var index = element.firstChild;
3581 forEach(new JQLite(node), function(child) {
3582 element.insertBefore(child, index);
3587 wrap: function(element, wrapNode) {
3588 jqLiteWrapNode(element, jqLite(wrapNode).eq(0).clone()[0]);
3591 remove: jqLiteRemove,
3593 detach: function(element) {
3594 jqLiteRemove(element, true);
3597 after: function(element, newElement) {
3598 var index = element, parent = element.parentNode;
3599 newElement = new JQLite(newElement);
3601 for (var i = 0, ii = newElement.length; i < ii; i++) {
3602 var node = newElement[i];
3603 parent.insertBefore(node, index.nextSibling);
3608 addClass: jqLiteAddClass,
3609 removeClass: jqLiteRemoveClass,
3611 toggleClass: function(element, selector, condition) {
3613 forEach(selector.split(' '), function(className) {
3614 var classCondition = condition;
3615 if (isUndefined(classCondition)) {
3616 classCondition = !jqLiteHasClass(element, className);
3618 (classCondition ? jqLiteAddClass : jqLiteRemoveClass)(element, className);
3623 parent: function(element) {
3624 var parent = element.parentNode;
3625 return parent && parent.nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT ? parent : null;
3628 next: function(element) {
3629 return element.nextElementSibling;
3632 find: function(element, selector) {
3633 if (element.getElementsByTagName) {
3634 return element.getElementsByTagName(selector);
3642 triggerHandler: function(element, event, extraParameters) {
3644 var dummyEvent, eventFnsCopy, handlerArgs;
3645 var eventName = event.type || event;
3646 var expandoStore = jqLiteExpandoStore(element);
3647 var events = expandoStore && expandoStore.events;
3648 var eventFns = events && events[eventName];
3651 // Create a dummy event to pass to the handlers
3653 preventDefault: function() { this.defaultPrevented = true; },
3654 isDefaultPrevented: function() { return this.defaultPrevented === true; },
3655 stopImmediatePropagation: function() { this.immediatePropagationStopped = true; },
3656 isImmediatePropagationStopped: function() { return this.immediatePropagationStopped === true; },
3657 stopPropagation: noop,
3662 // If a custom event was provided then extend our dummy event with it
3664 dummyEvent = extend(dummyEvent, event);
3667 // Copy event handlers in case event handlers array is modified during execution.
3668 eventFnsCopy = shallowCopy(eventFns);
3669 handlerArgs = extraParameters ? [dummyEvent].concat(extraParameters) : [dummyEvent];
3671 forEach(eventFnsCopy, function(fn) {
3672 if (!dummyEvent.isImmediatePropagationStopped()) {
3673 fn.apply(element, handlerArgs);
3678 }, function(fn, name) {
3680 * chaining functions
3682 JQLite.prototype[name] = function(arg1, arg2, arg3) {
3685 for (var i = 0, ii = this.length; i < ii; i++) {
3686 if (isUndefined(value)) {
3687 value = fn(this[i], arg1, arg2, arg3);
3688 if (isDefined(value)) {
3689 // any function which returns a value needs to be wrapped
3690 value = jqLite(value);
3693 jqLiteAddNodes(value, fn(this[i], arg1, arg2, arg3));
3696 return isDefined(value) ? value : this;
3699 // bind legacy bind/unbind to on/off
3700 JQLite.prototype.bind = JQLite.prototype.on;
3701 JQLite.prototype.unbind = JQLite.prototype.off;
3705 // Provider for private $$jqLite service
3706 function $$jqLiteProvider() {
3707 this.$get = function $$jqLite() {
3708 return extend(JQLite, {
3709 hasClass: function(node, classes) {
3710 if (node.attr) node = node[0];
3711 return jqLiteHasClass(node, classes);
3713 addClass: function(node, classes) {
3714 if (node.attr) node = node[0];
3715 return jqLiteAddClass(node, classes);
3717 removeClass: function(node, classes) {
3718 if (node.attr) node = node[0];
3719 return jqLiteRemoveClass(node, classes);
3726 * Computes a hash of an 'obj'.
3729 * number is number as string
3730 * object is either result of calling $$hashKey function on the object or uniquely generated id,
3731 * that is also assigned to the $$hashKey property of the object.
3734 * @returns {string} hash string such that the same input will have the same hash string.
3735 * The resulting string key is in 'type:hashKey' format.
3737 function hashKey(obj, nextUidFn) {
3738 var key = obj && obj.$$hashKey;
3741 if (typeof key === 'function') {
3742 key = obj.$$hashKey();
3747 var objType = typeof obj;
3748 if (objType == 'function' || (objType == 'object' && obj !== null)) {
3749 key = obj.$$hashKey = objType + ':' + (nextUidFn || nextUid)();
3751 key = objType + ':' + obj;
3758 * HashMap which can use objects as keys
3760 function HashMap(array, isolatedUid) {
3763 this.nextUid = function() {
3767 forEach(array, this.put, this);
3769 HashMap.prototype = {
3771 * Store key value pair
3772 * @param key key to store can be any type
3773 * @param value value to store can be any type
3775 put: function(key, value) {
3776 this[hashKey(key, this.nextUid)] = value;
3781 * @returns {Object} the value for the key
3783 get: function(key) {
3784 return this[hashKey(key, this.nextUid)];
3788 * Remove the key/value pair
3791 remove: function(key) {
3792 var value = this[key = hashKey(key, this.nextUid)];
3798 var $$HashMapProvider = [function() {
3799 this.$get = [function() {
3807 * @name angular.injector
3811 * Creates an injector object that can be used for retrieving services as well as for
3812 * dependency injection (see {@link guide/di dependency injection}).
3814 * @param {Array.<string|Function>} modules A list of module functions or their aliases. See
3815 * {@link angular.module}. The `ng` module must be explicitly added.
3816 * @param {boolean=} [strictDi=false] Whether the injector should be in strict mode, which
3817 * disallows argument name annotation inference.
3818 * @returns {injector} Injector object. See {@link auto.$injector $injector}.
3823 * // create an injector
3824 * var $injector = angular.injector(['ng']);
3826 * // use the injector to kick off your application
3827 * // use the type inference to auto inject arguments, or use implicit injection
3828 * $injector.invoke(function($rootScope, $compile, $document) {
3829 * $compile($document)($rootScope);
3830 * $rootScope.$digest();
3834 * Sometimes you want to get access to the injector of a currently running Angular app
3835 * from outside Angular. Perhaps, you want to inject and compile some markup after the
3836 * application has been bootstrapped. You can do this using the extra `injector()` added
3837 * to JQuery/jqLite elements. See {@link angular.element}.
3839 * *This is fairly rare but could be the case if a third party library is injecting the
3842 * In the following example a new block of HTML containing a `ng-controller`
3843 * directive is added to the end of the document body by JQuery. We then compile and link
3844 * it into the current AngularJS scope.
3847 * var $div = $('<div ng-controller="MyCtrl">{{content.label}}</div>');
3848 * $(document.body).append($div);
3850 * angular.element(document).injector().invoke(function($compile) {
3851 * var scope = angular.element($div).scope();
3852 * $compile($div)(scope);
3864 * Implicit module which gets automatically added to each {@link auto.$injector $injector}.
3867 var ARROW_ARG = /^([^\(]+?)=>/;
3868 var FN_ARGS = /^[^\(]*\(\s*([^\)]*)\)/m;
3869 var FN_ARG_SPLIT = /,/;
3870 var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/;
3871 var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
3872 var $injectorMinErr = minErr('$injector');
3874 function extractArgs(fn) {
3875 var fnText = Function.prototype.toString.call(fn).replace(STRIP_COMMENTS, ''),
3876 args = fnText.match(ARROW_ARG) || fnText.match(FN_ARGS);
3880 function anonFn(fn) {
3881 // For anonymous functions, showing at the very least the function signature can help in
3883 var args = extractArgs(fn);
3885 return 'function(' + (args[1] || '').replace(/[\s\r\n]+/, ' ') + ')';
3890 function annotate(fn, strictDi, name) {
3895 if (typeof fn === 'function') {
3896 if (!($inject = fn.$inject)) {
3900 if (!isString(name) || !name) {
3901 name = fn.name || anonFn(fn);
3903 throw $injectorMinErr('strictdi',
3904 '{0} is not using explicit annotation and cannot be invoked in strict mode', name);
3906 argDecl = extractArgs(fn);
3907 forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg) {
3908 arg.replace(FN_ARG, function(all, underscore, name) {
3913 fn.$inject = $inject;
3915 } else if (isArray(fn)) {
3916 last = fn.length - 1;
3917 assertArgFn(fn[last], 'fn');
3918 $inject = fn.slice(0, last);
3920 assertArgFn(fn, 'fn', true);
3925 ///////////////////////////////////////
3933 * `$injector` is used to retrieve object instances as defined by
3934 * {@link auto.$provide provider}, instantiate types, invoke methods,
3937 * The following always holds true:
3940 * var $injector = angular.injector();
3941 * expect($injector.get('$injector')).toBe($injector);
3942 * expect($injector.invoke(function($injector) {
3944 * })).toBe($injector);
3947 * # Injection Function Annotation
3949 * JavaScript does not have annotations, and annotations are needed for dependency injection. The
3950 * following are all valid ways of annotating function with injection arguments and are equivalent.
3953 * // inferred (only works if code not minified/obfuscated)
3954 * $injector.invoke(function(serviceA){});
3957 * function explicit(serviceA) {};
3958 * explicit.$inject = ['serviceA'];
3959 * $injector.invoke(explicit);
3962 * $injector.invoke(['serviceA', function(serviceA){}]);
3967 * In JavaScript calling `toString()` on a function returns the function definition. The definition
3968 * can then be parsed and the function arguments can be extracted. This method of discovering
3969 * annotations is disallowed when the injector is in strict mode.
3970 * *NOTE:* This does not work with minification, and obfuscation tools since these tools change the
3973 * ## `$inject` Annotation
3974 * By adding an `$inject` property onto a function the injection parameters can be specified.
3977 * As an array of injection names, where the last item in the array is the function to call.
3982 * @name $injector#get
3985 * Return an instance of the service.
3987 * @param {string} name The name of the instance to retrieve.
3988 * @param {string=} caller An optional string to provide the origin of the function call for error messages.
3989 * @return {*} The instance.
3994 * @name $injector#invoke
3997 * Invoke the method and supply the method arguments from the `$injector`.
3999 * @param {Function|Array.<string|Function>} fn The injectable function to invoke. Function parameters are
4000 * injected according to the {@link guide/di $inject Annotation} rules.
4001 * @param {Object=} self The `this` for the invoked method.
4002 * @param {Object=} locals Optional object. If preset then any argument names are read from this
4003 * object first, before the `$injector` is consulted.
4004 * @returns {*} the value returned by the invoked `fn` function.
4009 * @name $injector#has
4012 * Allows the user to query if the particular service exists.
4014 * @param {string} name Name of the service to query.
4015 * @returns {boolean} `true` if injector has given service.
4020 * @name $injector#instantiate
4022 * Create a new instance of JS type. The method takes a constructor function, invokes the new
4023 * operator, and supplies all of the arguments to the constructor function as specified by the
4024 * constructor annotation.
4026 * @param {Function} Type Annotated constructor function.
4027 * @param {Object=} locals Optional object. If preset then any argument names are read from this
4028 * object first, before the `$injector` is consulted.
4029 * @returns {Object} new instance of `Type`.
4034 * @name $injector#annotate
4037 * Returns an array of service names which the function is requesting for injection. This API is
4038 * used by the injector to determine which services need to be injected into the function when the
4039 * function is invoked. There are three ways in which the function can be annotated with the needed
4044 * The simplest form is to extract the dependencies from the arguments of the function. This is done
4045 * by converting the function into a string using `toString()` method and extracting the argument
4049 * function MyController($scope, $route) {
4054 * expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
4057 * You can disallow this method by using strict injection mode.
4059 * This method does not work with code minification / obfuscation. For this reason the following
4060 * annotation strategies are supported.
4062 * # The `$inject` property
4064 * If a function has an `$inject` property and its value is an array of strings, then the strings
4065 * represent names of services to be injected into the function.
4068 * var MyController = function(obfuscatedScope, obfuscatedRoute) {
4071 * // Define function dependencies
4072 * MyController['$inject'] = ['$scope', '$route'];
4075 * expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
4078 * # The array notation
4080 * It is often desirable to inline Injected functions and that's when setting the `$inject` property
4081 * is very inconvenient. In these situations using the array notation to specify the dependencies in
4082 * a way that survives minification is a better choice:
4085 * // We wish to write this (not minification / obfuscation safe)
4086 * injector.invoke(function($compile, $rootScope) {
4090 * // We are forced to write break inlining
4091 * var tmpFn = function(obfuscatedCompile, obfuscatedRootScope) {
4094 * tmpFn.$inject = ['$compile', '$rootScope'];
4095 * injector.invoke(tmpFn);
4097 * // To better support inline function the inline annotation is supported
4098 * injector.invoke(['$compile', '$rootScope', function(obfCompile, obfRootScope) {
4103 * expect(injector.annotate(
4104 * ['$compile', '$rootScope', function(obfus_$compile, obfus_$rootScope) {}])
4105 * ).toEqual(['$compile', '$rootScope']);
4108 * @param {Function|Array.<string|Function>} fn Function for which dependent service names need to
4109 * be retrieved as described above.
4111 * @param {boolean=} [strictDi=false] Disallow argument name annotation inference.
4113 * @returns {Array.<string>} The names of the services which the function requires.
4125 * The {@link auto.$provide $provide} service has a number of methods for registering components
4126 * with the {@link auto.$injector $injector}. Many of these functions are also exposed on
4127 * {@link angular.Module}.
4129 * An Angular **service** is a singleton object created by a **service factory**. These **service
4130 * factories** are functions which, in turn, are created by a **service provider**.
4131 * The **service providers** are constructor functions. When instantiated they must contain a
4132 * property called `$get`, which holds the **service factory** function.
4134 * When you request a service, the {@link auto.$injector $injector} is responsible for finding the
4135 * correct **service provider**, instantiating it and then calling its `$get` **service factory**
4136 * function to get the instance of the **service**.
4138 * Often services have no configuration options and there is no need to add methods to the service
4139 * provider. The provider will be no more than a constructor function with a `$get` property. For
4140 * these cases the {@link auto.$provide $provide} service has additional helper methods to register
4141 * services without specifying a provider.
4143 * * {@link auto.$provide#provider provider(provider)} - registers a **service provider** with the
4144 * {@link auto.$injector $injector}
4145 * * {@link auto.$provide#constant constant(obj)} - registers a value/object that can be accessed by
4146 * providers and services.
4147 * * {@link auto.$provide#value value(obj)} - registers a value/object that can only be accessed by
4148 * services, not providers.
4149 * * {@link auto.$provide#factory factory(fn)} - registers a service **factory function**, `fn`,
4150 * that will be wrapped in a **service provider** object, whose `$get` property will contain the
4151 * given factory function.
4152 * * {@link auto.$provide#service service(class)} - registers a **constructor function**, `class`
4153 * that will be wrapped in a **service provider** object, whose `$get` property will instantiate
4154 * a new object using the given constructor function.
4156 * See the individual methods for more information and examples.
4161 * @name $provide#provider
4164 * Register a **provider function** with the {@link auto.$injector $injector}. Provider functions
4165 * are constructor functions, whose instances are responsible for "providing" a factory for a
4168 * Service provider names start with the name of the service they provide followed by `Provider`.
4169 * For example, the {@link ng.$log $log} service has a provider called
4170 * {@link ng.$logProvider $logProvider}.
4172 * Service provider objects can have additional methods which allow configuration of the provider
4173 * and its service. Importantly, you can configure what kind of service is created by the `$get`
4174 * method, or how that service will act. For example, the {@link ng.$logProvider $logProvider} has a
4175 * method {@link ng.$logProvider#debugEnabled debugEnabled}
4176 * which lets you specify whether the {@link ng.$log $log} service will log debug messages to the
4179 * @param {string} name The name of the instance. NOTE: the provider will be available under `name +
4181 * @param {(Object|function())} provider If the provider is:
4183 * - `Object`: then it should have a `$get` method. The `$get` method will be invoked using
4184 * {@link auto.$injector#invoke $injector.invoke()} when an instance needs to be created.
4185 * - `Constructor`: a new instance of the provider will be created using
4186 * {@link auto.$injector#instantiate $injector.instantiate()}, then treated as `object`.
4188 * @returns {Object} registered provider instance
4192 * The following example shows how to create a simple event tracking service and register it using
4193 * {@link auto.$provide#provider $provide.provider()}.
4196 * // Define the eventTracker provider
4197 * function EventTrackerProvider() {
4198 * var trackingUrl = '/track';
4200 * // A provider method for configuring where the tracked events should been saved
4201 * this.setTrackingUrl = function(url) {
4202 * trackingUrl = url;
4205 * // The service factory function
4206 * this.$get = ['$http', function($http) {
4207 * var trackedEvents = {};
4209 * // Call this to track an event
4210 * event: function(event) {
4211 * var count = trackedEvents[event] || 0;
4213 * trackedEvents[event] = count;
4216 * // Call this to save the tracked events to the trackingUrl
4217 * save: function() {
4218 * $http.post(trackingUrl, trackedEvents);
4224 * describe('eventTracker', function() {
4227 * beforeEach(module(function($provide) {
4228 * // Register the eventTracker provider
4229 * $provide.provider('eventTracker', EventTrackerProvider);
4232 * beforeEach(module(function(eventTrackerProvider) {
4233 * // Configure eventTracker provider
4234 * eventTrackerProvider.setTrackingUrl('/custom-track');
4237 * it('tracks events', inject(function(eventTracker) {
4238 * expect(eventTracker.event('login')).toEqual(1);
4239 * expect(eventTracker.event('login')).toEqual(2);
4242 * it('saves to the tracking url', inject(function(eventTracker, $http) {
4243 * postSpy = spyOn($http, 'post');
4244 * eventTracker.event('login');
4245 * eventTracker.save();
4246 * expect(postSpy).toHaveBeenCalled();
4247 * expect(postSpy.mostRecentCall.args[0]).not.toEqual('/track');
4248 * expect(postSpy.mostRecentCall.args[0]).toEqual('/custom-track');
4249 * expect(postSpy.mostRecentCall.args[1]).toEqual({ 'login': 1 });
4257 * @name $provide#factory
4260 * Register a **service factory**, which will be called to return the service instance.
4261 * This is short for registering a service where its provider consists of only a `$get` property,
4262 * which is the given service factory function.
4263 * You should use {@link auto.$provide#factory $provide.factory(getFn)} if you do not need to
4264 * configure your service in a provider.
4266 * @param {string} name The name of the instance.
4267 * @param {Function|Array.<string|Function>} $getFn The injectable $getFn for the instance creation.
4268 * Internally this is a short hand for `$provide.provider(name, {$get: $getFn})`.
4269 * @returns {Object} registered provider instance
4272 * Here is an example of registering a service
4274 * $provide.factory('ping', ['$http', function($http) {
4275 * return function ping() {
4276 * return $http.send('/ping');
4280 * You would then inject and use this service like this:
4282 * someModule.controller('Ctrl', ['ping', function(ping) {
4291 * @name $provide#service
4294 * Register a **service constructor**, which will be invoked with `new` to create the service
4296 * This is short for registering a service where its provider's `$get` property is a factory
4297 * function that returns an instance instantiated by the injector from the service constructor
4300 * Internally it looks a bit like this:
4304 * $get: function() {
4305 * return $injector.instantiate(constructor);
4311 * You should use {@link auto.$provide#service $provide.service(class)} if you define your service
4314 * @param {string} name The name of the instance.
4315 * @param {Function|Array.<string|Function>} constructor An injectable class (constructor function)
4316 * that will be instantiated.
4317 * @returns {Object} registered provider instance
4320 * Here is an example of registering a service using
4321 * {@link auto.$provide#service $provide.service(class)}.
4323 * var Ping = function($http) {
4324 * this.$http = $http;
4327 * Ping.$inject = ['$http'];
4329 * Ping.prototype.send = function() {
4330 * return this.$http.get('/ping');
4332 * $provide.service('ping', Ping);
4334 * You would then inject and use this service like this:
4336 * someModule.controller('Ctrl', ['ping', function(ping) {
4345 * @name $provide#value
4348 * Register a **value service** with the {@link auto.$injector $injector}, such as a string, a
4349 * number, an array, an object or a function. This is short for registering a service where its
4350 * provider's `$get` property is a factory function that takes no arguments and returns the **value
4351 * service**. That also means it is not possible to inject other services into a value service.
4353 * Value services are similar to constant services, except that they cannot be injected into a
4354 * module configuration function (see {@link angular.Module#config}) but they can be overridden by
4355 * an Angular {@link auto.$provide#decorator decorator}.
4357 * @param {string} name The name of the instance.
4358 * @param {*} value The value.
4359 * @returns {Object} registered provider instance
4362 * Here are some examples of creating value services.
4364 * $provide.value('ADMIN_USER', 'admin');
4366 * $provide.value('RoleLookup', { admin: 0, writer: 1, reader: 2 });
4368 * $provide.value('halfOf', function(value) {
4377 * @name $provide#constant
4380 * Register a **constant service** with the {@link auto.$injector $injector}, such as a string,
4381 * a number, an array, an object or a function. Like the {@link auto.$provide#value value}, it is not
4382 * possible to inject other services into a constant.
4384 * But unlike {@link auto.$provide#value value}, a constant can be
4385 * injected into a module configuration function (see {@link angular.Module#config}) and it cannot
4386 * be overridden by an Angular {@link auto.$provide#decorator decorator}.
4388 * @param {string} name The name of the constant.
4389 * @param {*} value The constant value.
4390 * @returns {Object} registered instance
4393 * Here a some examples of creating constants:
4395 * $provide.constant('SHARD_HEIGHT', 306);
4397 * $provide.constant('MY_COLOURS', ['red', 'blue', 'grey']);
4399 * $provide.constant('double', function(value) {
4408 * @name $provide#decorator
4411 * Register a **service decorator** with the {@link auto.$injector $injector}. A service decorator
4412 * intercepts the creation of a service, allowing it to override or modify the behavior of the
4413 * service. The object returned by the decorator may be the original service, or a new service
4414 * object which replaces or wraps and delegates to the original service.
4416 * @param {string} name The name of the service to decorate.
4417 * @param {Function|Array.<string|Function>} decorator This function will be invoked when the service needs to be
4418 * instantiated and should return the decorated service instance. The function is called using
4419 * the {@link auto.$injector#invoke injector.invoke} method and is therefore fully injectable.
4420 * Local injection arguments:
4422 * * `$delegate` - The original service instance, which can be monkey patched, configured,
4423 * decorated or delegated to.
4426 * Here we decorate the {@link ng.$log $log} service to convert warnings to errors by intercepting
4427 * calls to {@link ng.$log#error $log.warn()}.
4429 * $provide.decorator('$log', ['$delegate', function($delegate) {
4430 * $delegate.warn = $delegate.error;
4437 function createInjector(modulesToLoad, strictDi) {
4438 strictDi = (strictDi === true);
4439 var INSTANTIATING = {},
4440 providerSuffix = 'Provider',
4442 loadedModules = new HashMap([], true),
4445 provider: supportObject(provider),
4446 factory: supportObject(factory),
4447 service: supportObject(service),
4448 value: supportObject(value),
4449 constant: supportObject(constant),
4450 decorator: decorator
4453 providerInjector = (providerCache.$injector =
4454 createInternalInjector(providerCache, function(serviceName, caller) {
4455 if (angular.isString(caller)) {
4458 throw $injectorMinErr('unpr', "Unknown provider: {0}", path.join(' <- '));
4461 protoInstanceInjector =
4462 createInternalInjector(instanceCache, function(serviceName, caller) {
4463 var provider = providerInjector.get(serviceName + providerSuffix, caller);
4464 return instanceInjector.invoke(
4465 provider.$get, provider, undefined, serviceName);
4467 instanceInjector = protoInstanceInjector;
4469 providerCache['$injector' + providerSuffix] = { $get: valueFn(protoInstanceInjector) };
4470 var runBlocks = loadModules(modulesToLoad);
4471 instanceInjector = protoInstanceInjector.get('$injector');
4472 instanceInjector.strictDi = strictDi;
4473 forEach(runBlocks, function(fn) { if (fn) instanceInjector.invoke(fn); });
4475 return instanceInjector;
4477 ////////////////////////////////////
4479 ////////////////////////////////////
4481 function supportObject(delegate) {
4482 return function(key, value) {
4483 if (isObject(key)) {
4484 forEach(key, reverseParams(delegate));
4486 return delegate(key, value);
4491 function provider(name, provider_) {
4492 assertNotHasOwnProperty(name, 'service');
4493 if (isFunction(provider_) || isArray(provider_)) {
4494 provider_ = providerInjector.instantiate(provider_);
4496 if (!provider_.$get) {
4497 throw $injectorMinErr('pget', "Provider '{0}' must define $get factory method.", name);
4499 return providerCache[name + providerSuffix] = provider_;
4502 function enforceReturnValue(name, factory) {
4503 return function enforcedReturnValue() {
4504 var result = instanceInjector.invoke(factory, this);
4505 if (isUndefined(result)) {
4506 throw $injectorMinErr('undef', "Provider '{0}' must return a value from $get factory method.", name);
4512 function factory(name, factoryFn, enforce) {
4513 return provider(name, {
4514 $get: enforce !== false ? enforceReturnValue(name, factoryFn) : factoryFn
4518 function service(name, constructor) {
4519 return factory(name, ['$injector', function($injector) {
4520 return $injector.instantiate(constructor);
4524 function value(name, val) { return factory(name, valueFn(val), false); }
4526 function constant(name, value) {
4527 assertNotHasOwnProperty(name, 'constant');
4528 providerCache[name] = value;
4529 instanceCache[name] = value;
4532 function decorator(serviceName, decorFn) {
4533 var origProvider = providerInjector.get(serviceName + providerSuffix),
4534 orig$get = origProvider.$get;
4536 origProvider.$get = function() {
4537 var origInstance = instanceInjector.invoke(orig$get, origProvider);
4538 return instanceInjector.invoke(decorFn, null, {$delegate: origInstance});
4542 ////////////////////////////////////
4544 ////////////////////////////////////
4545 function loadModules(modulesToLoad) {
4546 assertArg(isUndefined(modulesToLoad) || isArray(modulesToLoad), 'modulesToLoad', 'not an array');
4547 var runBlocks = [], moduleFn;
4548 forEach(modulesToLoad, function(module) {
4549 if (loadedModules.get(module)) return;
4550 loadedModules.put(module, true);
4552 function runInvokeQueue(queue) {
4554 for (i = 0, ii = queue.length; i < ii; i++) {
4555 var invokeArgs = queue[i],
4556 provider = providerInjector.get(invokeArgs[0]);
4558 provider[invokeArgs[1]].apply(provider, invokeArgs[2]);
4563 if (isString(module)) {
4564 moduleFn = angularModule(module);
4565 runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks);
4566 runInvokeQueue(moduleFn._invokeQueue);
4567 runInvokeQueue(moduleFn._configBlocks);
4568 } else if (isFunction(module)) {
4569 runBlocks.push(providerInjector.invoke(module));
4570 } else if (isArray(module)) {
4571 runBlocks.push(providerInjector.invoke(module));
4573 assertArgFn(module, 'module');
4576 if (isArray(module)) {
4577 module = module[module.length - 1];
4579 if (e.message && e.stack && e.stack.indexOf(e.message) == -1) {
4580 // Safari & FF's stack traces don't contain error.message content
4581 // unlike those of Chrome and IE
4582 // So if stack doesn't contain message, we create a new string that contains both.
4583 // Since error.stack is read-only in Safari, I'm overriding e and not e.stack here.
4585 e = e.message + '\n' + e.stack;
4587 throw $injectorMinErr('modulerr', "Failed to instantiate module {0} due to:\n{1}",
4588 module, e.stack || e.message || e);
4594 ////////////////////////////////////
4595 // internal Injector
4596 ////////////////////////////////////
4598 function createInternalInjector(cache, factory) {
4600 function getService(serviceName, caller) {
4601 if (cache.hasOwnProperty(serviceName)) {
4602 if (cache[serviceName] === INSTANTIATING) {
4603 throw $injectorMinErr('cdep', 'Circular dependency found: {0}',
4604 serviceName + ' <- ' + path.join(' <- '));
4606 return cache[serviceName];
4609 path.unshift(serviceName);
4610 cache[serviceName] = INSTANTIATING;
4611 return cache[serviceName] = factory(serviceName, caller);
4613 if (cache[serviceName] === INSTANTIATING) {
4614 delete cache[serviceName];
4624 function injectionArgs(fn, locals, serviceName) {
4626 $inject = createInjector.$$annotate(fn, strictDi, serviceName);
4628 for (var i = 0, length = $inject.length; i < length; i++) {
4629 var key = $inject[i];
4630 if (typeof key !== 'string') {
4631 throw $injectorMinErr('itkn',
4632 'Incorrect injection token! Expected service name as string, got {0}', key);
4634 args.push(locals && locals.hasOwnProperty(key) ? locals[key] :
4635 getService(key, serviceName));
4640 function isClass(func) {
4641 // IE 9-11 do not support classes and IE9 leaks with the code below.
4645 // Workaround for MS Edge.
4646 // Check https://connect.microsoft.com/IE/Feedback/Details/2211653
4647 return typeof func === 'function'
4648 && /^(?:class\s|constructor\()/.test(Function.prototype.toString.call(func));
4651 function invoke(fn, self, locals, serviceName) {
4652 if (typeof locals === 'string') {
4653 serviceName = locals;
4657 var args = injectionArgs(fn, locals, serviceName);
4659 fn = fn[fn.length - 1];
4663 // http://jsperf.com/angularjs-invoke-apply-vs-switch
4665 return fn.apply(self, args);
4668 return new (Function.prototype.bind.apply(fn, args))();
4673 function instantiate(Type, locals, serviceName) {
4674 // Check if Type is annotated and use just the given function at n-1 as parameter
4675 // e.g. someModule.factory('greeter', ['$window', function(renamed$window) {}]);
4676 var ctor = (isArray(Type) ? Type[Type.length - 1] : Type);
4677 var args = injectionArgs(Type, locals, serviceName);
4678 // Empty object at position 0 is ignored for invocation with `new`, but required.
4680 return new (Function.prototype.bind.apply(ctor, args))();
4686 instantiate: instantiate,
4688 annotate: createInjector.$$annotate,
4689 has: function(name) {
4690 return providerCache.hasOwnProperty(name + providerSuffix) || cache.hasOwnProperty(name);
4696 createInjector.$$annotate = annotate;
4700 * @name $anchorScrollProvider
4703 * Use `$anchorScrollProvider` to disable automatic scrolling whenever
4704 * {@link ng.$location#hash $location.hash()} changes.
4706 function $AnchorScrollProvider() {
4708 var autoScrollingEnabled = true;
4712 * @name $anchorScrollProvider#disableAutoScrolling
4715 * By default, {@link ng.$anchorScroll $anchorScroll()} will automatically detect changes to
4716 * {@link ng.$location#hash $location.hash()} and scroll to the element matching the new hash.<br />
4717 * Use this method to disable automatic scrolling.
4719 * If automatic scrolling is disabled, one must explicitly call
4720 * {@link ng.$anchorScroll $anchorScroll()} in order to scroll to the element related to the
4723 this.disableAutoScrolling = function() {
4724 autoScrollingEnabled = false;
4729 * @name $anchorScroll
4732 * @requires $location
4733 * @requires $rootScope
4736 * When called, it scrolls to the element related to the specified `hash` or (if omitted) to the
4737 * current value of {@link ng.$location#hash $location.hash()}, according to the rules specified
4739 * [HTML5 spec](http://www.w3.org/html/wg/drafts/html/master/browsers.html#the-indicated-part-of-the-document).
4741 * It also watches the {@link ng.$location#hash $location.hash()} and automatically scrolls to
4742 * match any anchor whenever it changes. This can be disabled by calling
4743 * {@link ng.$anchorScrollProvider#disableAutoScrolling $anchorScrollProvider.disableAutoScrolling()}.
4745 * Additionally, you can use its {@link ng.$anchorScroll#yOffset yOffset} property to specify a
4746 * vertical scroll-offset (either fixed or dynamic).
4748 * @param {string=} hash The hash specifying the element to scroll to. If omitted, the value of
4749 * {@link ng.$location#hash $location.hash()} will be used.
4751 * @property {(number|function|jqLite)} yOffset
4752 * If set, specifies a vertical scroll-offset. This is often useful when there are fixed
4753 * positioned elements at the top of the page, such as navbars, headers etc.
4755 * `yOffset` can be specified in various ways:
4756 * - **number**: A fixed number of pixels to be used as offset.<br /><br />
4757 * - **function**: A getter function called everytime `$anchorScroll()` is executed. Must return
4758 * a number representing the offset (in pixels).<br /><br />
4759 * - **jqLite**: A jqLite/jQuery element to be used for specifying the offset. The distance from
4760 * the top of the page to the element's bottom will be used as offset.<br />
4761 * **Note**: The element will be taken into account only as long as its `position` is set to
4762 * `fixed`. This option is useful, when dealing with responsive navbars/headers that adjust
4763 * their height and/or positioning according to the viewport's size.
4766 * <div class="alert alert-warning">
4767 * In order for `yOffset` to work properly, scrolling should take place on the document's root and
4768 * not some child element.
4772 <example module="anchorScrollExample">
4773 <file name="index.html">
4774 <div id="scrollArea" ng-controller="ScrollController">
4775 <a ng-click="gotoBottom()">Go to bottom</a>
4776 <a id="bottom"></a> You're at the bottom!
4779 <file name="script.js">
4780 angular.module('anchorScrollExample', [])
4781 .controller('ScrollController', ['$scope', '$location', '$anchorScroll',
4782 function ($scope, $location, $anchorScroll) {
4783 $scope.gotoBottom = function() {
4784 // set the location.hash to the id of
4785 // the element you wish to scroll to.
4786 $location.hash('bottom');
4788 // call $anchorScroll()
4793 <file name="style.css">
4807 * The example below illustrates the use of a vertical scroll-offset (specified as a fixed value).
4808 * See {@link ng.$anchorScroll#yOffset $anchorScroll.yOffset} for more details.
4811 <example module="anchorScrollOffsetExample">
4812 <file name="index.html">
4813 <div class="fixed-header" ng-controller="headerCtrl">
4814 <a href="" ng-click="gotoAnchor(x)" ng-repeat="x in [1,2,3,4,5]">
4818 <div id="anchor{{x}}" class="anchor" ng-repeat="x in [1,2,3,4,5]">
4822 <file name="script.js">
4823 angular.module('anchorScrollOffsetExample', [])
4824 .run(['$anchorScroll', function($anchorScroll) {
4825 $anchorScroll.yOffset = 50; // always scroll by 50 extra pixels
4827 .controller('headerCtrl', ['$anchorScroll', '$location', '$scope',
4828 function ($anchorScroll, $location, $scope) {
4829 $scope.gotoAnchor = function(x) {
4830 var newHash = 'anchor' + x;
4831 if ($location.hash() !== newHash) {
4832 // set the $location.hash to `newHash` and
4833 // $anchorScroll will automatically scroll to it
4834 $location.hash('anchor' + x);
4836 // call $anchorScroll() explicitly,
4837 // since $location.hash hasn't changed
4844 <file name="style.css">
4850 border: 2px dashed DarkOrchid;
4851 padding: 10px 10px 200px 10px;
4855 background-color: rgba(0, 0, 0, 0.2);
4858 top: 0; left: 0; right: 0;
4862 display: inline-block;
4868 this.$get = ['$window', '$location', '$rootScope', function($window, $location, $rootScope) {
4869 var document = $window.document;
4871 // Helper function to get first anchor from a NodeList
4872 // (using `Array#some()` instead of `angular#forEach()` since it's more performant
4873 // and working in all supported browsers.)
4874 function getFirstAnchor(list) {
4876 Array.prototype.some.call(list, function(element) {
4877 if (nodeName_(element) === 'a') {
4885 function getYOffset() {
4887 var offset = scroll.yOffset;
4889 if (isFunction(offset)) {
4891 } else if (isElement(offset)) {
4892 var elem = offset[0];
4893 var style = $window.getComputedStyle(elem);
4894 if (style.position !== 'fixed') {
4897 offset = elem.getBoundingClientRect().bottom;
4899 } else if (!isNumber(offset)) {
4906 function scrollTo(elem) {
4908 elem.scrollIntoView();
4910 var offset = getYOffset();
4913 // `offset` is the number of pixels we should scroll UP in order to align `elem` properly.
4914 // This is true ONLY if the call to `elem.scrollIntoView()` initially aligns `elem` at the
4915 // top of the viewport.
4917 // IF the number of pixels from the top of `elem` to the end of the page's content is less
4918 // than the height of the viewport, then `elem.scrollIntoView()` will align the `elem` some
4919 // way down the page.
4921 // This is often the case for elements near the bottom of the page.
4923 // In such cases we do not need to scroll the whole `offset` up, just the difference between
4924 // the top of the element and the offset, which is enough to align the top of `elem` at the
4925 // desired position.
4926 var elemTop = elem.getBoundingClientRect().top;
4927 $window.scrollBy(0, elemTop - offset);
4930 $window.scrollTo(0, 0);
4934 function scroll(hash) {
4935 hash = isString(hash) ? hash : $location.hash();
4938 // empty hash, scroll to the top of the page
4939 if (!hash) scrollTo(null);
4941 // element with given id
4942 else if ((elm = document.getElementById(hash))) scrollTo(elm);
4944 // first anchor with given name :-D
4945 else if ((elm = getFirstAnchor(document.getElementsByName(hash)))) scrollTo(elm);
4947 // no element and hash == 'top', scroll to the top of the page
4948 else if (hash === 'top') scrollTo(null);
4951 // does not scroll when user clicks on anchor link that is currently on
4952 // (no url change, no $location.hash() change), browser native does scroll
4953 if (autoScrollingEnabled) {
4954 $rootScope.$watch(function autoScrollWatch() {return $location.hash();},
4955 function autoScrollWatchAction(newVal, oldVal) {
4956 // skip the initial scroll if $location.hash is empty
4957 if (newVal === oldVal && newVal === '') return;
4959 jqLiteDocumentLoaded(function() {
4960 $rootScope.$evalAsync(scroll);
4969 var $animateMinErr = minErr('$animate');
4970 var ELEMENT_NODE = 1;
4971 var NG_ANIMATE_CLASSNAME = 'ng-animate';
4973 function mergeClasses(a,b) {
4974 if (!a && !b) return '';
4977 if (isArray(a)) a = a.join(' ');
4978 if (isArray(b)) b = b.join(' ');
4982 function extractElementNode(element) {
4983 for (var i = 0; i < element.length; i++) {
4984 var elm = element[i];
4985 if (elm.nodeType === ELEMENT_NODE) {
4991 function splitClasses(classes) {
4992 if (isString(classes)) {
4993 classes = classes.split(' ');
4996 // Use createMap() to prevent class assumptions involving property names in
4998 var obj = createMap();
4999 forEach(classes, function(klass) {
5000 // sometimes the split leaves empty string values
5001 // incase extra spaces were applied to the options
5009 // if any other type of options value besides an Object value is
5010 // passed into the $animate.method() animation then this helper code
5011 // will be run which will ignore it. While this patch is not the
5012 // greatest solution to this, a lot of existing plugins depend on
5013 // $animate to either call the callback (< 1.2) or return a promise
5014 // that can be changed. This helper function ensures that the options
5015 // are wiped clean incase a callback function is provided.
5016 function prepareAnimateOptions(options) {
5017 return isObject(options)
5022 var $$CoreAnimateJsProvider = function() {
5026 // this is prefixed with Core since it conflicts with
5027 // the animateQueueProvider defined in ngAnimate/animateQueue.js
5028 var $$CoreAnimateQueueProvider = function() {
5029 var postDigestQueue = new HashMap();
5030 var postDigestElements = [];
5032 this.$get = ['$$AnimateRunner', '$rootScope',
5033 function($$AnimateRunner, $rootScope) {
5040 push: function(element, event, options, domOperation) {
5041 domOperation && domOperation();
5043 options = options || {};
5044 options.from && element.css(options.from);
5045 options.to && element.css(options.to);
5047 if (options.addClass || options.removeClass) {
5048 addRemoveClassesPostDigest(element, options.addClass, options.removeClass);
5051 var runner = new $$AnimateRunner(); // jshint ignore:line
5053 // since there are no animations to run the runner needs to be
5054 // notified that the animation call is complete.
5061 function updateData(data, classes, value) {
5062 var changed = false;
5064 classes = isString(classes) ? classes.split(' ') :
5065 isArray(classes) ? classes : [];
5066 forEach(classes, function(className) {
5069 data[className] = value;
5076 function handleCSSClassChanges() {
5077 forEach(postDigestElements, function(element) {
5078 var data = postDigestQueue.get(element);
5080 var existing = splitClasses(element.attr('class'));
5083 forEach(data, function(status, className) {
5084 var hasClass = !!existing[className];
5085 if (status !== hasClass) {
5087 toAdd += (toAdd.length ? ' ' : '') + className;
5089 toRemove += (toRemove.length ? ' ' : '') + className;
5094 forEach(element, function(elm) {
5095 toAdd && jqLiteAddClass(elm, toAdd);
5096 toRemove && jqLiteRemoveClass(elm, toRemove);
5098 postDigestQueue.remove(element);
5101 postDigestElements.length = 0;
5105 function addRemoveClassesPostDigest(element, add, remove) {
5106 var data = postDigestQueue.get(element) || {};
5108 var classesAdded = updateData(data, add, true);
5109 var classesRemoved = updateData(data, remove, false);
5111 if (classesAdded || classesRemoved) {
5113 postDigestQueue.put(element, data);
5114 postDigestElements.push(element);
5116 if (postDigestElements.length === 1) {
5117 $rootScope.$$postDigest(handleCSSClassChanges);
5126 * @name $animateProvider
5129 * Default implementation of $animate that doesn't perform any animations, instead just
5130 * synchronously performs DOM updates and resolves the returned runner promise.
5132 * In order to enable animations the `ngAnimate` module has to be loaded.
5134 * To see the functional implementation check out `src/ngAnimate/animate.js`.
5136 var $AnimateProvider = ['$provide', function($provide) {
5137 var provider = this;
5139 this.$$registeredAnimations = Object.create(null);
5143 * @name $animateProvider#register
5146 * Registers a new injectable animation factory function. The factory function produces the
5147 * animation object which contains callback functions for each event that is expected to be
5150 * * `eventFn`: `function(element, ... , doneFunction, options)`
5151 * The element to animate, the `doneFunction` and the options fed into the animation. Depending
5152 * on the type of animation additional arguments will be injected into the animation function. The
5153 * list below explains the function signatures for the different animation methods:
5155 * - setClass: function(element, addedClasses, removedClasses, doneFunction, options)
5156 * - addClass: function(element, addedClasses, doneFunction, options)
5157 * - removeClass: function(element, removedClasses, doneFunction, options)
5158 * - enter, leave, move: function(element, doneFunction, options)
5159 * - animate: function(element, fromStyles, toStyles, doneFunction, options)
5161 * Make sure to trigger the `doneFunction` once the animation is fully complete.
5165 * //enter, leave, move signature
5166 * eventFn : function(element, done, options) {
5167 * //code to run the animation
5168 * //once complete, then run done()
5169 * return function endFunction(wasCancelled) {
5170 * //code to cancel the animation
5176 * @param {string} name The name of the animation (this is what the class-based CSS value will be compared to).
5177 * @param {Function} factory The factory function that will be executed to return the animation
5180 this.register = function(name, factory) {
5181 if (name && name.charAt(0) !== '.') {
5182 throw $animateMinErr('notcsel', "Expecting class selector starting with '.' got '{0}'.", name);
5185 var key = name + '-animation';
5186 provider.$$registeredAnimations[name.substr(1)] = key;
5187 $provide.factory(key, factory);
5192 * @name $animateProvider#classNameFilter
5195 * Sets and/or returns the CSS class regular expression that is checked when performing
5196 * an animation. Upon bootstrap the classNameFilter value is not set at all and will
5197 * therefore enable $animate to attempt to perform an animation on any element that is triggered.
5198 * When setting the `classNameFilter` value, animations will only be performed on elements
5199 * that successfully match the filter expression. This in turn can boost performance
5200 * for low-powered devices as well as applications containing a lot of structural operations.
5201 * @param {RegExp=} expression The className expression which will be checked against all animations
5202 * @return {RegExp} The current CSS className expression value. If null then there is no expression value
5204 this.classNameFilter = function(expression) {
5205 if (arguments.length === 1) {
5206 this.$$classNameFilter = (expression instanceof RegExp) ? expression : null;
5207 if (this.$$classNameFilter) {
5208 var reservedRegex = new RegExp("(\\s+|\\/)" + NG_ANIMATE_CLASSNAME + "(\\s+|\\/)");
5209 if (reservedRegex.test(this.$$classNameFilter.toString())) {
5210 throw $animateMinErr('nongcls','$animateProvider.classNameFilter(regex) prohibits accepting a regex value which matches/contains the "{0}" CSS class.', NG_ANIMATE_CLASSNAME);
5215 return this.$$classNameFilter;
5218 this.$get = ['$$animateQueue', function($$animateQueue) {
5219 function domInsert(element, parentElement, afterElement) {
5220 // if for some reason the previous element was removed
5221 // from the dom sometime before this code runs then let's
5222 // just stick to using the parent element as the anchor
5224 var afterNode = extractElementNode(afterElement);
5225 if (afterNode && !afterNode.parentNode && !afterNode.previousElementSibling) {
5226 afterElement = null;
5229 afterElement ? afterElement.after(element) : parentElement.prepend(element);
5235 * @description The $animate service exposes a series of DOM utility methods that provide support
5236 * for animation hooks. The default behavior is the application of DOM operations, however,
5237 * when an animation is detected (and animations are enabled), $animate will do the heavy lifting
5238 * to ensure that animation runs with the triggered DOM operation.
5240 * By default $animate doesn't trigger any animations. This is because the `ngAnimate` module isn't
5241 * included and only when it is active then the animation hooks that `$animate` triggers will be
5242 * functional. Once active then all structural `ng-` directives will trigger animations as they perform
5243 * their DOM-related operations (enter, leave and move). Other directives such as `ngClass`,
5244 * `ngShow`, `ngHide` and `ngMessages` also provide support for animations.
5246 * It is recommended that the`$animate` service is always used when executing DOM-related procedures within directives.
5248 * To learn more about enabling animation support, click here to visit the
5249 * {@link ngAnimate ngAnimate module page}.
5252 // we don't call it directly since non-existant arguments may
5253 // be interpreted as null within the sub enabled function
5260 * @description Sets up an event listener to fire whenever the animation event (enter, leave, move, etc...)
5261 * has fired on the given element or among any of its children. Once the listener is fired, the provided callback
5262 * is fired with the following params:
5265 * $animate.on('enter', container,
5266 * function callback(element, phase) {
5267 * // cool we detected an enter animation within the container
5272 * @param {string} event the animation event that will be captured (e.g. enter, leave, move, addClass, removeClass, etc...)
5273 * @param {DOMElement} container the container element that will capture each of the animation events that are fired on itself
5274 * as well as among its children
5275 * @param {Function} callback the callback function that will be fired when the listener is triggered
5277 * The arguments present in the callback function are:
5278 * * `element` - The captured DOM element that the animation was fired on.
5279 * * `phase` - The phase of the animation. The two possible phases are **start** (when the animation starts) and **close** (when it ends).
5281 on: $$animateQueue.on,
5286 * @name $animate#off
5288 * @description Deregisters an event listener based on the event which has been associated with the provided element. This method
5289 * can be used in three different ways depending on the arguments:
5292 * // remove all the animation event listeners listening for `enter`
5293 * $animate.off('enter');
5295 * // remove listeners for all animation events from the container element
5296 * $animate.off(container);
5298 * // remove all the animation event listeners listening for `enter` on the given element and its children
5299 * $animate.off('enter', container);
5301 * // remove the event listener function provided by `callback` that is set
5302 * // to listen for `enter` on the given `container` as well as its children
5303 * $animate.off('enter', container, callback);
5306 * @param {string|DOMElement} event|container the animation event (e.g. enter, leave, move,
5307 * addClass, removeClass, etc...), or the container element. If it is the element, all other
5308 * arguments are ignored.
5309 * @param {DOMElement=} container the container element the event listener was placed on
5310 * @param {Function=} callback the callback function that was registered as the listener
5312 off: $$animateQueue.off,
5316 * @name $animate#pin
5318 * @description Associates the provided element with a host parent element to allow the element to be animated even if it exists
5319 * outside of the DOM structure of the Angular application. By doing so, any animation triggered via `$animate` can be issued on the
5320 * element despite being outside the realm of the application or within another application. Say for example if the application
5321 * was bootstrapped on an element that is somewhere inside of the `<body>` tag, but we wanted to allow for an element to be situated
5322 * as a direct child of `document.body`, then this can be achieved by pinning the element via `$animate.pin(element)`. Keep in mind
5323 * that calling `$animate.pin(element, parentElement)` will not actually insert into the DOM anywhere; it will just create the association.
5325 * Note that this feature is only active when the `ngAnimate` module is used.
5327 * @param {DOMElement} element the external element that will be pinned
5328 * @param {DOMElement} parentElement the host parent element that will be associated with the external element
5330 pin: $$animateQueue.pin,
5335 * @name $animate#enabled
5337 * @description Used to get and set whether animations are enabled or not on the entire application or on an element and its children. This
5338 * function can be called in four ways:
5341 * // returns true or false
5342 * $animate.enabled();
5344 * // changes the enabled state for all animations
5345 * $animate.enabled(false);
5346 * $animate.enabled(true);
5348 * // returns true or false if animations are enabled for an element
5349 * $animate.enabled(element);
5351 * // changes the enabled state for an element and its children
5352 * $animate.enabled(element, true);
5353 * $animate.enabled(element, false);
5356 * @param {DOMElement=} element the element that will be considered for checking/setting the enabled state
5357 * @param {boolean=} enabled whether or not the animations will be enabled for the element
5359 * @return {boolean} whether or not animations are enabled
5361 enabled: $$animateQueue.enabled,
5365 * @name $animate#cancel
5367 * @description Cancels the provided animation.
5369 * @param {Promise} animationPromise The animation promise that is returned when an animation is started.
5371 cancel: function(runner) {
5372 runner.end && runner.end();
5378 * @name $animate#enter
5380 * @description Inserts the element into the DOM either after the `after` element (if provided) or
5381 * as the first child within the `parent` element and then triggers an animation.
5382 * A promise is returned that will be resolved during the next digest once the animation
5385 * @param {DOMElement} element the element which will be inserted into the DOM
5386 * @param {DOMElement} parent the parent element which will append the element as
5387 * a child (so long as the after element is not present)
5388 * @param {DOMElement=} after the sibling element after which the element will be appended
5389 * @param {object=} options an optional collection of options/styles that will be applied to the element
5391 * @return {Promise} the animation callback promise
5393 enter: function(element, parent, after, options) {
5394 parent = parent && jqLite(parent);
5395 after = after && jqLite(after);
5396 parent = parent || after.parent();
5397 domInsert(element, parent, after);
5398 return $$animateQueue.push(element, 'enter', prepareAnimateOptions(options));
5404 * @name $animate#move
5406 * @description Inserts (moves) the element into its new position in the DOM either after
5407 * the `after` element (if provided) or as the first child within the `parent` element
5408 * and then triggers an animation. A promise is returned that will be resolved
5409 * during the next digest once the animation has completed.
5411 * @param {DOMElement} element the element which will be moved into the new DOM position
5412 * @param {DOMElement} parent the parent element which will append the element as
5413 * a child (so long as the after element is not present)
5414 * @param {DOMElement=} after the sibling element after which the element will be appended
5415 * @param {object=} options an optional collection of options/styles that will be applied to the element
5417 * @return {Promise} the animation callback promise
5419 move: function(element, parent, after, options) {
5420 parent = parent && jqLite(parent);
5421 after = after && jqLite(after);
5422 parent = parent || after.parent();
5423 domInsert(element, parent, after);
5424 return $$animateQueue.push(element, 'move', prepareAnimateOptions(options));
5429 * @name $animate#leave
5431 * @description Triggers an animation and then removes the element from the DOM.
5432 * When the function is called a promise is returned that will be resolved during the next
5433 * digest once the animation has completed.
5435 * @param {DOMElement} element the element which will be removed from the DOM
5436 * @param {object=} options an optional collection of options/styles that will be applied to the element
5438 * @return {Promise} the animation callback promise
5440 leave: function(element, options) {
5441 return $$animateQueue.push(element, 'leave', prepareAnimateOptions(options), function() {
5448 * @name $animate#addClass
5451 * @description Triggers an addClass animation surrounding the addition of the provided CSS class(es). Upon
5452 * execution, the addClass operation will only be handled after the next digest and it will not trigger an
5453 * animation if element already contains the CSS class or if the class is removed at a later step.
5454 * Note that class-based animations are treated differently compared to structural animations
5455 * (like enter, move and leave) since the CSS classes may be added/removed at different points
5456 * depending if CSS or JavaScript animations are used.
5458 * @param {DOMElement} element the element which the CSS classes will be applied to
5459 * @param {string} className the CSS class(es) that will be added (multiple classes are separated via spaces)
5460 * @param {object=} options an optional collection of options/styles that will be applied to the element
5462 * @return {Promise} the animation callback promise
5464 addClass: function(element, className, options) {
5465 options = prepareAnimateOptions(options);
5466 options.addClass = mergeClasses(options.addclass, className);
5467 return $$animateQueue.push(element, 'addClass', options);
5472 * @name $animate#removeClass
5475 * @description Triggers a removeClass animation surrounding the removal of the provided CSS class(es). Upon
5476 * execution, the removeClass operation will only be handled after the next digest and it will not trigger an
5477 * animation if element does not contain the CSS class or if the class is added at a later step.
5478 * Note that class-based animations are treated differently compared to structural animations
5479 * (like enter, move and leave) since the CSS classes may be added/removed at different points
5480 * depending if CSS or JavaScript animations are used.
5482 * @param {DOMElement} element the element which the CSS classes will be applied to
5483 * @param {string} className the CSS class(es) that will be removed (multiple classes are separated via spaces)
5484 * @param {object=} options an optional collection of options/styles that will be applied to the element
5486 * @return {Promise} the animation callback promise
5488 removeClass: function(element, className, options) {
5489 options = prepareAnimateOptions(options);
5490 options.removeClass = mergeClasses(options.removeClass, className);
5491 return $$animateQueue.push(element, 'removeClass', options);
5496 * @name $animate#setClass
5499 * @description Performs both the addition and removal of a CSS classes on an element and (during the process)
5500 * triggers an animation surrounding the class addition/removal. Much like `$animate.addClass` and
5501 * `$animate.removeClass`, `setClass` will only evaluate the classes being added/removed once a digest has
5502 * passed. Note that class-based animations are treated differently compared to structural animations
5503 * (like enter, move and leave) since the CSS classes may be added/removed at different points
5504 * depending if CSS or JavaScript animations are used.
5506 * @param {DOMElement} element the element which the CSS classes will be applied to
5507 * @param {string} add the CSS class(es) that will be added (multiple classes are separated via spaces)
5508 * @param {string} remove the CSS class(es) that will be removed (multiple classes are separated via spaces)
5509 * @param {object=} options an optional collection of options/styles that will be applied to the element
5511 * @return {Promise} the animation callback promise
5513 setClass: function(element, add, remove, options) {
5514 options = prepareAnimateOptions(options);
5515 options.addClass = mergeClasses(options.addClass, add);
5516 options.removeClass = mergeClasses(options.removeClass, remove);
5517 return $$animateQueue.push(element, 'setClass', options);
5522 * @name $animate#animate
5525 * @description Performs an inline animation on the element which applies the provided to and from CSS styles to the element.
5526 * If any detected CSS transition, keyframe or JavaScript matches the provided className value, then the animation will take
5527 * on the provided styles. For example, if a transition animation is set for the given classNamem, then the provided `from` and
5528 * `to` styles will be applied alongside the given transition. If the CSS style provided in `from` does not have a corresponding
5529 * style in `to`, the style in `from` is applied immediately, and no animation is run.
5530 * If a JavaScript animation is detected then the provided styles will be given in as function parameters into the `animate`
5531 * method (or as part of the `options` parameter):
5534 * ngModule.animation('.my-inline-animation', function() {
5536 * animate : function(element, from, to, done, options) {
5544 * @param {DOMElement} element the element which the CSS styles will be applied to
5545 * @param {object} from the from (starting) CSS styles that will be applied to the element and across the animation.
5546 * @param {object} to the to (destination) CSS styles that will be applied to the element and across the animation.
5547 * @param {string=} className an optional CSS class that will be applied to the element for the duration of the animation. If
5548 * this value is left as empty then a CSS class of `ng-inline-animate` will be applied to the element.
5549 * (Note that if no animation is detected then this value will not be applied to the element.)
5550 * @param {object=} options an optional collection of options/styles that will be applied to the element
5552 * @return {Promise} the animation callback promise
5554 animate: function(element, from, to, className, options) {
5555 options = prepareAnimateOptions(options);
5556 options.from = options.from ? extend(options.from, from) : from;
5557 options.to = options.to ? extend(options.to, to) : to;
5559 className = className || 'ng-inline-animate';
5560 options.tempClasses = mergeClasses(options.tempClasses, className);
5561 return $$animateQueue.push(element, 'animate', options);
5567 var $$AnimateAsyncRunFactoryProvider = function() {
5568 this.$get = ['$$rAF', function($$rAF) {
5571 function waitForTick(fn) {
5573 if (waitQueue.length > 1) return;
5575 for (var i = 0; i < waitQueue.length; i++) {
5584 waitForTick(function() {
5587 return function(callback) {
5588 passed ? callback() : waitForTick(callback);
5594 var $$AnimateRunnerFactoryProvider = function() {
5595 this.$get = ['$q', '$sniffer', '$$animateAsyncRun', '$document', '$timeout',
5596 function($q, $sniffer, $$animateAsyncRun, $document, $timeout) {
5598 var INITIAL_STATE = 0;
5599 var DONE_PENDING_STATE = 1;
5600 var DONE_COMPLETE_STATE = 2;
5602 AnimateRunner.chain = function(chain, callback) {
5607 if (index === chain.length) {
5612 chain[index](function(response) {
5613 if (response === false) {
5623 AnimateRunner.all = function(runners, callback) {
5626 forEach(runners, function(runner) {
5627 runner.done(onProgress);
5630 function onProgress(response) {
5631 status = status && response;
5632 if (++count === runners.length) {
5638 function AnimateRunner(host) {
5641 var rafTick = $$animateAsyncRun();
5642 var timeoutTick = function(fn) {
5643 $timeout(fn, 0, false);
5646 this._doneCallbacks = [];
5647 this._tick = function(fn) {
5648 var doc = $document[0];
5650 // the document may not be ready or attached
5651 // to the module for some internal tests
5652 if (doc && doc.hidden) {
5661 AnimateRunner.prototype = {
5662 setHost: function(host) {
5663 this.host = host || {};
5666 done: function(fn) {
5667 if (this._state === DONE_COMPLETE_STATE) {
5670 this._doneCallbacks.push(fn);
5676 getPromise: function() {
5677 if (!this.promise) {
5679 this.promise = $q(function(resolve, reject) {
5680 self.done(function(status) {
5681 status === false ? reject() : resolve();
5685 return this.promise;
5688 then: function(resolveHandler, rejectHandler) {
5689 return this.getPromise().then(resolveHandler, rejectHandler);
5692 'catch': function(handler) {
5693 return this.getPromise()['catch'](handler);
5696 'finally': function(handler) {
5697 return this.getPromise()['finally'](handler);
5701 if (this.host.pause) {
5706 resume: function() {
5707 if (this.host.resume) {
5713 if (this.host.end) {
5716 this._resolve(true);
5719 cancel: function() {
5720 if (this.host.cancel) {
5723 this._resolve(false);
5726 complete: function(response) {
5728 if (self._state === INITIAL_STATE) {
5729 self._state = DONE_PENDING_STATE;
5730 self._tick(function() {
5731 self._resolve(response);
5736 _resolve: function(response) {
5737 if (this._state !== DONE_COMPLETE_STATE) {
5738 forEach(this._doneCallbacks, function(fn) {
5741 this._doneCallbacks.length = 0;
5742 this._state = DONE_COMPLETE_STATE;
5747 return AnimateRunner;
5757 * This is the core version of `$animateCss`. By default, only when the `ngAnimate` is included,
5758 * then the `$animateCss` service will actually perform animations.
5760 * Click here {@link ngAnimate.$animateCss to read the documentation for $animateCss}.
5762 var $CoreAnimateCssProvider = function() {
5763 this.$get = ['$$rAF', '$q', '$$AnimateRunner', function($$rAF, $q, $$AnimateRunner) {
5765 return function(element, initialOptions) {
5766 // all of the animation functions should create
5767 // a copy of the options data, however, if a
5768 // parent service has already created a copy then
5769 // we should stick to using that
5770 var options = initialOptions || {};
5771 if (!options.$$prepared) {
5772 options = copy(options);
5775 // there is no point in applying the styles since
5776 // there is no animation that goes on at all in
5777 // this version of $animateCss.
5778 if (options.cleanupStyles) {
5779 options.from = options.to = null;
5783 element.css(options.from);
5784 options.from = null;
5787 /* jshint newcap: false */
5788 var closed, runner = new $$AnimateRunner();
5796 applyAnimationContents();
5805 function applyAnimationContents() {
5806 if (options.addClass) {
5807 element.addClass(options.addClass);
5808 options.addClass = null;
5810 if (options.removeClass) {
5811 element.removeClass(options.removeClass);
5812 options.removeClass = null;
5815 element.css(options.to);
5823 /* global stripHash: true */
5826 * ! This is a private undocumented service !
5831 * This object has two goals:
5833 * - hide all the global state in the browser caused by the window object
5834 * - abstract away all the browser specific features and inconsistencies
5836 * For tests we provide {@link ngMock.$browser mock implementation} of the `$browser`
5837 * service, which can be used for convenient testing of the application without the interaction with
5838 * the real browser apis.
5841 * @param {object} window The global window object.
5842 * @param {object} document jQuery wrapped document.
5843 * @param {object} $log window.console or an object with the same interface.
5844 * @param {object} $sniffer $sniffer service
5846 function Browser(window, document, $log, $sniffer) {
5848 location = window.location,
5849 history = window.history,
5850 setTimeout = window.setTimeout,
5851 clearTimeout = window.clearTimeout,
5852 pendingDeferIds = {};
5854 self.isMock = false;
5856 var outstandingRequestCount = 0;
5857 var outstandingRequestCallbacks = [];
5859 // TODO(vojta): remove this temporary api
5860 self.$$completeOutstandingRequest = completeOutstandingRequest;
5861 self.$$incOutstandingRequestCount = function() { outstandingRequestCount++; };
5864 * Executes the `fn` function(supports currying) and decrements the `outstandingRequestCallbacks`
5865 * counter. If the counter reaches 0, all the `outstandingRequestCallbacks` are executed.
5867 function completeOutstandingRequest(fn) {
5869 fn.apply(null, sliceArgs(arguments, 1));
5871 outstandingRequestCount--;
5872 if (outstandingRequestCount === 0) {
5873 while (outstandingRequestCallbacks.length) {
5875 outstandingRequestCallbacks.pop()();
5884 function getHash(url) {
5885 var index = url.indexOf('#');
5886 return index === -1 ? '' : url.substr(index);
5891 * Note: this method is used only by scenario runner
5892 * TODO(vojta): prefix this method with $$ ?
5893 * @param {function()} callback Function that will be called when no outstanding request
5895 self.notifyWhenNoOutstandingRequests = function(callback) {
5896 if (outstandingRequestCount === 0) {
5899 outstandingRequestCallbacks.push(callback);
5903 //////////////////////////////////////////////////////////////
5905 //////////////////////////////////////////////////////////////
5907 var cachedState, lastHistoryState,
5908 lastBrowserUrl = location.href,
5909 baseElement = document.find('base'),
5910 pendingLocation = null,
5911 getCurrentState = !$sniffer.history ? noop : function getCurrentState() {
5913 return history.state;
5915 // MSIE can reportedly throw when there is no state (UNCONFIRMED).
5920 lastHistoryState = cachedState;
5923 * @name $browser#url
5927 * Without any argument, this method just returns current value of location.href.
5930 * With at least one argument, this method sets url to new value.
5931 * If html5 history api supported, pushState/replaceState is used, otherwise
5932 * location.href/location.replace is used.
5933 * Returns its own instance to allow chaining
5935 * NOTE: this api is intended for use only by the $location service. Please use the
5936 * {@link ng.$location $location service} to change url.
5938 * @param {string} url New url (when used as setter)
5939 * @param {boolean=} replace Should new url replace current history record?
5940 * @param {object=} state object to use with pushState/replaceState
5942 self.url = function(url, replace, state) {
5943 // In modern browsers `history.state` is `null` by default; treating it separately
5944 // from `undefined` would cause `$browser.url('/foo')` to change `history.state`
5945 // to undefined via `pushState`. Instead, let's change `undefined` to `null` here.
5946 if (isUndefined(state)) {
5950 // Android Browser BFCache causes location, history reference to become stale.
5951 if (location !== window.location) location = window.location;
5952 if (history !== window.history) history = window.history;
5956 var sameState = lastHistoryState === state;
5958 // Don't change anything if previous and current URLs and states match. This also prevents
5959 // IE<10 from getting into redirect loop when in LocationHashbangInHtml5Url mode.
5960 // See https://github.com/angular/angular.js/commit/ffb2701
5961 if (lastBrowserUrl === url && (!$sniffer.history || sameState)) {
5964 var sameBase = lastBrowserUrl && stripHash(lastBrowserUrl) === stripHash(url);
5965 lastBrowserUrl = url;
5966 lastHistoryState = state;
5967 // Don't use history API if only the hash changed
5968 // due to a bug in IE10/IE11 which leads
5969 // to not firing a `hashchange` nor `popstate` event
5970 // in some cases (see #9143).
5971 if ($sniffer.history && (!sameBase || !sameState)) {
5972 history[replace ? 'replaceState' : 'pushState'](state, '', url);
5974 // Do the assignment again so that those two variables are referentially identical.
5975 lastHistoryState = cachedState;
5977 if (!sameBase || pendingLocation) {
5978 pendingLocation = url;
5981 location.replace(url);
5982 } else if (!sameBase) {
5983 location.href = url;
5985 location.hash = getHash(url);
5987 if (location.href !== url) {
5988 pendingLocation = url;
5994 // - pendingLocation is needed as browsers don't allow to read out
5995 // the new location.href if a reload happened or if there is a bug like in iOS 9 (see
5996 // https://openradar.appspot.com/22186109).
5997 // - the replacement is a workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=407172
5998 return pendingLocation || location.href.replace(/%27/g,"'");
6003 * @name $browser#state
6006 * This method is a getter.
6008 * Return history.state or null if history.state is undefined.
6010 * @returns {object} state
6012 self.state = function() {
6016 var urlChangeListeners = [],
6017 urlChangeInit = false;
6019 function cacheStateAndFireUrlChange() {
6020 pendingLocation = null;
6025 // This variable should be used *only* inside the cacheState function.
6026 var lastCachedState = null;
6027 function cacheState() {
6028 // This should be the only place in $browser where `history.state` is read.
6029 cachedState = getCurrentState();
6030 cachedState = isUndefined(cachedState) ? null : cachedState;
6032 // Prevent callbacks fo fire twice if both hashchange & popstate were fired.
6033 if (equals(cachedState, lastCachedState)) {
6034 cachedState = lastCachedState;
6036 lastCachedState = cachedState;
6039 function fireUrlChange() {
6040 if (lastBrowserUrl === self.url() && lastHistoryState === cachedState) {
6044 lastBrowserUrl = self.url();
6045 lastHistoryState = cachedState;
6046 forEach(urlChangeListeners, function(listener) {
6047 listener(self.url(), cachedState);
6052 * @name $browser#onUrlChange
6055 * Register callback function that will be called, when url changes.
6057 * It's only called when the url is changed from outside of angular:
6058 * - user types different url into address bar
6059 * - user clicks on history (forward/back) button
6060 * - user clicks on a link
6062 * It's not called when url is changed by $browser.url() method
6064 * The listener gets called with new url as parameter.
6066 * NOTE: this api is intended for use only by the $location service. Please use the
6067 * {@link ng.$location $location service} to monitor url changes in angular apps.
6069 * @param {function(string)} listener Listener function to be called when url changes.
6070 * @return {function(string)} Returns the registered listener fn - handy if the fn is anonymous.
6072 self.onUrlChange = function(callback) {
6073 // TODO(vojta): refactor to use node's syntax for events
6074 if (!urlChangeInit) {
6075 // We listen on both (hashchange/popstate) when available, as some browsers (e.g. Opera)
6076 // don't fire popstate when user change the address bar and don't fire hashchange when url
6077 // changed by push/replaceState
6079 // html5 history api - popstate event
6080 if ($sniffer.history) jqLite(window).on('popstate', cacheStateAndFireUrlChange);
6082 jqLite(window).on('hashchange', cacheStateAndFireUrlChange);
6084 urlChangeInit = true;
6087 urlChangeListeners.push(callback);
6093 * Remove popstate and hashchange handler from window.
6095 * NOTE: this api is intended for use only by $rootScope.
6097 self.$$applicationDestroyed = function() {
6098 jqLite(window).off('hashchange popstate', cacheStateAndFireUrlChange);
6102 * Checks whether the url has changed outside of Angular.
6103 * Needs to be exported to be able to check for changes that have been done in sync,
6104 * as hashchange/popstate events fire in async.
6106 self.$$checkUrlChange = fireUrlChange;
6108 //////////////////////////////////////////////////////////////
6110 //////////////////////////////////////////////////////////////
6113 * @name $browser#baseHref
6116 * Returns current <base href>
6117 * (always relative - without domain)
6119 * @returns {string} The current base href
6121 self.baseHref = function() {
6122 var href = baseElement.attr('href');
6123 return href ? href.replace(/^(https?\:)?\/\/[^\/]*/, '') : '';
6127 * @name $browser#defer
6128 * @param {function()} fn A function, who's execution should be deferred.
6129 * @param {number=} [delay=0] of milliseconds to defer the function execution.
6130 * @returns {*} DeferId that can be used to cancel the task via `$browser.defer.cancel()`.
6133 * Executes a fn asynchronously via `setTimeout(fn, delay)`.
6135 * Unlike when calling `setTimeout` directly, in test this function is mocked and instead of using
6136 * `setTimeout` in tests, the fns are queued in an array, which can be programmatically flushed
6137 * via `$browser.defer.flush()`.
6140 self.defer = function(fn, delay) {
6142 outstandingRequestCount++;
6143 timeoutId = setTimeout(function() {
6144 delete pendingDeferIds[timeoutId];
6145 completeOutstandingRequest(fn);
6147 pendingDeferIds[timeoutId] = true;
6153 * @name $browser#defer.cancel
6156 * Cancels a deferred task identified with `deferId`.
6158 * @param {*} deferId Token returned by the `$browser.defer` function.
6159 * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully
6162 self.defer.cancel = function(deferId) {
6163 if (pendingDeferIds[deferId]) {
6164 delete pendingDeferIds[deferId];
6165 clearTimeout(deferId);
6166 completeOutstandingRequest(noop);
6174 function $BrowserProvider() {
6175 this.$get = ['$window', '$log', '$sniffer', '$document',
6176 function($window, $log, $sniffer, $document) {
6177 return new Browser($window, $document, $log, $sniffer);
6183 * @name $cacheFactory
6186 * Factory that constructs {@link $cacheFactory.Cache Cache} objects and gives access to
6191 * var cache = $cacheFactory('cacheId');
6192 * expect($cacheFactory.get('cacheId')).toBe(cache);
6193 * expect($cacheFactory.get('noSuchCacheId')).not.toBeDefined();
6195 * cache.put("key", "value");
6196 * cache.put("another key", "another value");
6198 * // We've specified no options on creation
6199 * expect(cache.info()).toEqual({id: 'cacheId', size: 2});
6204 * @param {string} cacheId Name or id of the newly created cache.
6205 * @param {object=} options Options object that specifies the cache behavior. Properties:
6207 * - `{number=}` `capacity` — turns the cache into LRU cache.
6209 * @returns {object} Newly created cache object with the following set of methods:
6211 * - `{object}` `info()` — Returns id, size, and options of cache.
6212 * - `{{*}}` `put({string} key, {*} value)` — Puts a new key-value pair into the cache and returns
6214 * - `{{*}}` `get({string} key)` — Returns cached value for `key` or undefined for cache miss.
6215 * - `{void}` `remove({string} key)` — Removes a key-value pair from the cache.
6216 * - `{void}` `removeAll()` — Removes all cached values.
6217 * - `{void}` `destroy()` — Removes references to this cache from $cacheFactory.
6220 <example module="cacheExampleApp">
6221 <file name="index.html">
6222 <div ng-controller="CacheController">
6223 <input ng-model="newCacheKey" placeholder="Key">
6224 <input ng-model="newCacheValue" placeholder="Value">
6225 <button ng-click="put(newCacheKey, newCacheValue)">Cache</button>
6227 <p ng-if="keys.length">Cached Values</p>
6228 <div ng-repeat="key in keys">
6229 <span ng-bind="key"></span>
6231 <b ng-bind="cache.get(key)"></b>
6235 <div ng-repeat="(key, value) in cache.info()">
6236 <span ng-bind="key"></span>
6238 <b ng-bind="value"></b>
6242 <file name="script.js">
6243 angular.module('cacheExampleApp', []).
6244 controller('CacheController', ['$scope', '$cacheFactory', function($scope, $cacheFactory) {
6246 $scope.cache = $cacheFactory('cacheId');
6247 $scope.put = function(key, value) {
6248 if (angular.isUndefined($scope.cache.get(key))) {
6249 $scope.keys.push(key);
6251 $scope.cache.put(key, angular.isUndefined(value) ? null : value);
6255 <file name="style.css">
6262 function $CacheFactoryProvider() {
6264 this.$get = function() {
6267 function cacheFactory(cacheId, options) {
6268 if (cacheId in caches) {
6269 throw minErr('$cacheFactory')('iid', "CacheId '{0}' is already taken!", cacheId);
6273 stats = extend({}, options, {id: cacheId}),
6275 capacity = (options && options.capacity) || Number.MAX_VALUE,
6276 lruHash = createMap(),
6282 * @name $cacheFactory.Cache
6285 * A cache object used to store and retrieve data, primarily used by
6286 * {@link $http $http} and the {@link ng.directive:script script} directive to cache
6287 * templates and other data.
6290 * angular.module('superCache')
6291 * .factory('superCache', ['$cacheFactory', function($cacheFactory) {
6292 * return $cacheFactory('super-cache');
6299 * it('should behave like a cache', inject(function(superCache) {
6300 * superCache.put('key', 'value');
6301 * superCache.put('another key', 'another value');
6303 * expect(superCache.info()).toEqual({
6304 * id: 'super-cache',
6308 * superCache.remove('another key');
6309 * expect(superCache.get('another key')).toBeUndefined();
6311 * superCache.removeAll();
6312 * expect(superCache.info()).toEqual({
6313 * id: 'super-cache',
6319 return caches[cacheId] = {
6323 * @name $cacheFactory.Cache#put
6327 * Inserts a named entry into the {@link $cacheFactory.Cache Cache} object to be
6328 * retrieved later, and incrementing the size of the cache if the key was not already
6329 * present in the cache. If behaving like an LRU cache, it will also remove stale
6330 * entries from the set.
6332 * It will not insert undefined values into the cache.
6334 * @param {string} key the key under which the cached data is stored.
6335 * @param {*} value the value to store alongside the key. If it is undefined, the key
6336 * will not be stored.
6337 * @returns {*} the value stored.
6339 put: function(key, value) {
6340 if (isUndefined(value)) return;
6341 if (capacity < Number.MAX_VALUE) {
6342 var lruEntry = lruHash[key] || (lruHash[key] = {key: key});
6347 if (!(key in data)) size++;
6350 if (size > capacity) {
6351 this.remove(staleEnd.key);
6359 * @name $cacheFactory.Cache#get
6363 * Retrieves named data stored in the {@link $cacheFactory.Cache Cache} object.
6365 * @param {string} key the key of the data to be retrieved
6366 * @returns {*} the value stored.
6368 get: function(key) {
6369 if (capacity < Number.MAX_VALUE) {
6370 var lruEntry = lruHash[key];
6372 if (!lruEntry) return;
6383 * @name $cacheFactory.Cache#remove
6387 * Removes an entry from the {@link $cacheFactory.Cache Cache} object.
6389 * @param {string} key the key of the entry to be removed
6391 remove: function(key) {
6392 if (capacity < Number.MAX_VALUE) {
6393 var lruEntry = lruHash[key];
6395 if (!lruEntry) return;
6397 if (lruEntry == freshEnd) freshEnd = lruEntry.p;
6398 if (lruEntry == staleEnd) staleEnd = lruEntry.n;
6399 link(lruEntry.n,lruEntry.p);
6401 delete lruHash[key];
6404 if (!(key in data)) return;
6413 * @name $cacheFactory.Cache#removeAll
6417 * Clears the cache object of any entries.
6419 removeAll: function() {
6422 lruHash = createMap();
6423 freshEnd = staleEnd = null;
6429 * @name $cacheFactory.Cache#destroy
6433 * Destroys the {@link $cacheFactory.Cache Cache} object entirely,
6434 * removing it from the {@link $cacheFactory $cacheFactory} set.
6436 destroy: function() {
6440 delete caches[cacheId];
6446 * @name $cacheFactory.Cache#info
6450 * Retrieve information regarding a particular {@link $cacheFactory.Cache Cache}.
6452 * @returns {object} an object with the following properties:
6454 * <li>**id**: the id of the cache instance</li>
6455 * <li>**size**: the number of entries kept in the cache instance</li>
6456 * <li>**...**: any additional properties from the options object when creating the
6461 return extend({}, stats, {size: size});
6467 * makes the `entry` the freshEnd of the LRU linked list
6469 function refresh(entry) {
6470 if (entry != freshEnd) {
6473 } else if (staleEnd == entry) {
6477 link(entry.n, entry.p);
6478 link(entry, freshEnd);
6486 * bidirectionally links two entries of the LRU linked list
6488 function link(nextEntry, prevEntry) {
6489 if (nextEntry != prevEntry) {
6490 if (nextEntry) nextEntry.p = prevEntry; //p stands for previous, 'prev' didn't minify
6491 if (prevEntry) prevEntry.n = nextEntry; //n stands for next, 'next' didn't minify
6499 * @name $cacheFactory#info
6502 * Get information about all the caches that have been created
6504 * @returns {Object} - key-value map of `cacheId` to the result of calling `cache#info`
6506 cacheFactory.info = function() {
6508 forEach(caches, function(cache, cacheId) {
6509 info[cacheId] = cache.info();
6517 * @name $cacheFactory#get
6520 * Get access to a cache object by the `cacheId` used when it was created.
6522 * @param {string} cacheId Name or id of a cache to access.
6523 * @returns {object} Cache object identified by the cacheId or undefined if no such cache.
6525 cacheFactory.get = function(cacheId) {
6526 return caches[cacheId];
6530 return cacheFactory;
6536 * @name $templateCache
6539 * The first time a template is used, it is loaded in the template cache for quick retrieval. You
6540 * can load templates directly into the cache in a `script` tag, or by consuming the
6541 * `$templateCache` service directly.
6543 * Adding via the `script` tag:
6546 * <script type="text/ng-template" id="templateId.html">
6547 * <p>This is the content of the template</p>
6551 * **Note:** the `script` tag containing the template does not need to be included in the `head` of
6552 * the document, but it must be a descendent of the {@link ng.$rootElement $rootElement} (IE,
6553 * element with ng-app attribute), otherwise the template will be ignored.
6555 * Adding via the `$templateCache` service:
6558 * var myApp = angular.module('myApp', []);
6559 * myApp.run(function($templateCache) {
6560 * $templateCache.put('templateId.html', 'This is the content of the template');
6564 * To retrieve the template later, simply use it in your HTML:
6566 * <div ng-include=" 'templateId.html' "></div>
6569 * or get it via Javascript:
6571 * $templateCache.get('templateId.html')
6574 * See {@link ng.$cacheFactory $cacheFactory}.
6577 function $TemplateCacheProvider() {
6578 this.$get = ['$cacheFactory', function($cacheFactory) {
6579 return $cacheFactory('templates');
6583 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
6584 * Any commits to this file should be reviewed with security in mind. *
6585 * Changes to this file can potentially create security vulnerabilities. *
6586 * An approval from 2 Core members with history of modifying *
6587 * this file is required. *
6589 * Does the change somehow allow for arbitrary javascript to be executed? *
6590 * Or allows for someone to change the prototype of built-in objects? *
6591 * Or gives undesired access to variables likes document or window? *
6592 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
6594 /* ! VARIABLE/FUNCTION NAMING CONVENTIONS THAT APPLY TO THIS FILE!
6596 * DOM-related variables:
6598 * - "node" - DOM Node
6599 * - "element" - DOM Element or Node
6600 * - "$node" or "$element" - jqLite-wrapped node or element
6603 * Compiler related stuff:
6605 * - "linkFn" - linking fn of a single directive
6606 * - "nodeLinkFn" - function that aggregates all linking fns for a particular node
6607 * - "childLinkFn" - function that aggregates all linking fns for child nodes of a particular node
6608 * - "compositeLinkFn" - function that aggregates all linking fns for a compilation root (nodeList)
6618 * Compiles an HTML string or DOM into a template and produces a template function, which
6619 * can then be used to link {@link ng.$rootScope.Scope `scope`} and the template together.
6621 * The compilation is a process of walking the DOM tree and matching DOM elements to
6622 * {@link ng.$compileProvider#directive directives}.
6624 * <div class="alert alert-warning">
6625 * **Note:** This document is an in-depth reference of all directive options.
6626 * For a gentle introduction to directives with examples of common use cases,
6627 * see the {@link guide/directive directive guide}.
6630 * ## Comprehensive Directive API
6632 * There are many different options for a directive.
6634 * The difference resides in the return value of the factory function.
6635 * You can either return a "Directive Definition Object" (see below) that defines the directive properties,
6636 * or just the `postLink` function (all other properties will have the default values).
6638 * <div class="alert alert-success">
6639 * **Best Practice:** It's recommended to use the "directive definition object" form.
6642 * Here's an example directive declared with a Directive Definition Object:
6645 * var myModule = angular.module(...);
6647 * myModule.directive('directiveName', function factory(injectables) {
6648 * var directiveDefinitionObject = {
6650 * template: '<div></div>', // or // function(tElement, tAttrs) { ... },
6652 * // templateUrl: 'directive.html', // or // function(tElement, tAttrs) { ... },
6653 * transclude: false,
6655 * templateNamespace: 'html',
6657 * controller: function($scope, $element, $attrs, $transclude, otherInjectables) { ... },
6658 * controllerAs: 'stringIdentifier',
6659 * bindToController: false,
6660 * require: 'siblingDirectiveName', // or // ['^parentDirectiveName', '?optionalDirectiveName', '?^optionalParent'],
6661 * compile: function compile(tElement, tAttrs, transclude) {
6663 * pre: function preLink(scope, iElement, iAttrs, controller) { ... },
6664 * post: function postLink(scope, iElement, iAttrs, controller) { ... }
6667 * // return function postLink( ... ) { ... }
6671 * // pre: function preLink(scope, iElement, iAttrs, controller) { ... },
6672 * // post: function postLink(scope, iElement, iAttrs, controller) { ... }
6675 * // link: function postLink( ... ) { ... }
6677 * return directiveDefinitionObject;
6681 * <div class="alert alert-warning">
6682 * **Note:** Any unspecified options will use the default value. You can see the default values below.
6685 * Therefore the above can be simplified as:
6688 * var myModule = angular.module(...);
6690 * myModule.directive('directiveName', function factory(injectables) {
6691 * var directiveDefinitionObject = {
6692 * link: function postLink(scope, iElement, iAttrs) { ... }
6694 * return directiveDefinitionObject;
6696 * // return function postLink(scope, iElement, iAttrs) { ... }
6702 * ### Directive Definition Object
6704 * The directive definition object provides instructions to the {@link ng.$compile
6705 * compiler}. The attributes are:
6707 * #### `multiElement`
6708 * When this property is set to true, the HTML compiler will collect DOM nodes between
6709 * nodes with the attributes `directive-name-start` and `directive-name-end`, and group them
6710 * together as the directive elements. It is recommended that this feature be used on directives
6711 * which are not strictly behavioral (such as {@link ngClick}), and which
6712 * do not manipulate or replace child nodes (such as {@link ngInclude}).
6715 * When there are multiple directives defined on a single DOM element, sometimes it
6716 * is necessary to specify the order in which the directives are applied. The `priority` is used
6717 * to sort the directives before their `compile` functions get called. Priority is defined as a
6718 * number. Directives with greater numerical `priority` are compiled first. Pre-link functions
6719 * are also run in priority order, but post-link functions are run in reverse order. The order
6720 * of directives with the same priority is undefined. The default priority is `0`.
6723 * If set to true then the current `priority` will be the last set of directives
6724 * which will execute (any directives at the current priority will still execute
6725 * as the order of execution on same `priority` is undefined). Note that expressions
6726 * and other directives used in the directive's template will also be excluded from execution.
6729 * The scope property can be `true`, an object or a falsy value:
6731 * * **falsy:** No scope will be created for the directive. The directive will use its parent's scope.
6733 * * **`true`:** A new child scope that prototypically inherits from its parent will be created for
6734 * the directive's element. If multiple directives on the same element request a new scope,
6735 * only one new scope is created. The new scope rule does not apply for the root of the template
6736 * since the root of the template always gets a new scope.
6738 * * **`{...}` (an object hash):** A new "isolate" scope is created for the directive's element. The
6739 * 'isolate' scope differs from normal scope in that it does not prototypically inherit from its parent
6740 * scope. This is useful when creating reusable components, which should not accidentally read or modify
6741 * data in the parent scope.
6743 * The 'isolate' scope object hash defines a set of local scope properties derived from attributes on the
6744 * directive's element. These local properties are useful for aliasing values for templates. The keys in
6745 * the object hash map to the name of the property on the isolate scope; the values define how the property
6746 * is bound to the parent scope, via matching attributes on the directive's element:
6748 * * `@` or `@attr` - bind a local scope property to the value of DOM attribute. The result is
6749 * always a string since DOM attributes are strings. If no `attr` name is specified then the
6750 * attribute name is assumed to be the same as the local name. Given `<my-component
6751 * my-attr="hello {{name}}">` and the isolate scope definition `scope: { localName:'@myAttr' }`,
6752 * the directive's scope property `localName` will reflect the interpolated value of `hello
6753 * {{name}}`. As the `name` attribute changes so will the `localName` property on the directive's
6754 * scope. The `name` is read from the parent scope (not the directive's scope).
6756 * * `=` or `=attr` - set up a bidirectional binding between a local scope property and an expression
6757 * passed via the attribute `attr`. The expression is evaluated in the context of the parent scope.
6758 * If no `attr` name is specified then the attribute name is assumed to be the same as the local
6759 * name. Given `<my-component my-attr="parentModel">` and the isolate scope definition `scope: {
6760 * localModel: '=myAttr' }`, the property `localModel` on the directive's scope will reflect the
6761 * value of `parentModel` on the parent scope. Changes to `parentModel` will be reflected in
6762 * `localModel` and vice versa. Optional attributes should be marked as such with a question mark:
6763 * `=?` or `=?attr`. If the binding expression is non-assignable, or if the attribute isn't
6764 * optional and doesn't exist, an exception ({@link error/$compile/nonassign `$compile:nonassign`})
6765 * will be thrown upon discovering changes to the local value, since it will be impossible to sync
6766 * them back to the parent scope. By default, the {@link ng.$rootScope.Scope#$watch `$watch`}
6767 * method is used for tracking changes, and the equality check is based on object identity.
6768 * However, if an object literal or an array literal is passed as the binding expression, the
6769 * equality check is done by value (using the {@link angular.equals} function). It's also possible
6770 * to watch the evaluated value shallowly with {@link ng.$rootScope.Scope#$watchCollection
6771 * `$watchCollection`}: use `=*` or `=*attr` (`=*?` or `=*?attr` if the attribute is optional).
6773 * * `<` or `<attr` - set up a one-way (one-directional) binding between a local scope property and an
6774 * expression passed via the attribute `attr`. The expression is evaluated in the context of the
6775 * parent scope. If no `attr` name is specified then the attribute name is assumed to be the same as the
6776 * local name. You can also make the binding optional by adding `?`: `<?` or `<?attr`.
6778 * For example, given `<my-component my-attr="parentModel">` and directive definition of
6779 * `scope: { localModel:'<myAttr' }`, then the isolated scope property `localModel` will reflect the
6780 * value of `parentModel` on the parent scope. Any changes to `parentModel` will be reflected
6781 * in `localModel`, but changes in `localModel` will not reflect in `parentModel`. There are however
6783 * 1. one-way binding does not copy the value from the parent to the isolate scope, it simply
6784 * sets the same value. That means if your bound value is an object, changes to its properties
6785 * in the isolated scope will be reflected in the parent scope (because both reference the same object).
6786 * 2. one-way binding watches changes to the **identity** of the parent value. That means the
6787 * {@link ng.$rootScope.Scope#$watch `$watch`} on the parent value only fires if the reference
6788 * to the value has changed. In most cases, this should not be of concern, but can be important
6789 * to know if you one-way bind to an object, and then replace that object in the isolated scope.
6790 * If you now change a property of the object in your parent scope, the change will not be
6791 * propagated to the isolated scope, because the identity of the object on the parent scope
6792 * has not changed. Instead you must assign a new object.
6794 * One-way binding is useful if you do not plan to propagate changes to your isolated scope bindings
6795 * back to the parent. However, it does not make this completely impossible.
6797 * * `&` or `&attr` - provides a way to execute an expression in the context of the parent scope. If
6798 * no `attr` name is specified then the attribute name is assumed to be the same as the local name.
6799 * Given `<my-component my-attr="count = count + value">` and the isolate scope definition `scope: {
6800 * localFn:'&myAttr' }`, the isolate scope property `localFn` will point to a function wrapper for
6801 * the `count = count + value` expression. Often it's desirable to pass data from the isolated scope
6802 * via an expression to the parent scope. This can be done by passing a map of local variable names
6803 * and values into the expression wrapper fn. For example, if the expression is `increment(amount)`
6804 * then we can specify the amount value by calling the `localFn` as `localFn({amount: 22})`.
6806 * In general it's possible to apply more than one directive to one element, but there might be limitations
6807 * depending on the type of scope required by the directives. The following points will help explain these limitations.
6808 * For simplicity only two directives are taken into account, but it is also applicable for several directives:
6810 * * **no scope** + **no scope** => Two directives which don't require their own scope will use their parent's scope
6811 * * **child scope** + **no scope** => Both directives will share one single child scope
6812 * * **child scope** + **child scope** => Both directives will share one single child scope
6813 * * **isolated scope** + **no scope** => The isolated directive will use it's own created isolated scope. The other directive will use
6814 * its parent's scope
6815 * * **isolated scope** + **child scope** => **Won't work!** Only one scope can be related to one element. Therefore these directives cannot
6816 * be applied to the same element.
6817 * * **isolated scope** + **isolated scope** => **Won't work!** Only one scope can be related to one element. Therefore these directives
6818 * cannot be applied to the same element.
6821 * #### `bindToController`
6822 * This property is used to bind scope properties directly to the controller. It can be either
6823 * `true` or an object hash with the same format as the `scope` property. Additionally, a controller
6824 * alias must be set, either by using `controllerAs: 'myAlias'` or by specifying the alias in the controller
6825 * definition: `controller: 'myCtrl as myAlias'`.
6827 * When an isolate scope is used for a directive (see above), `bindToController: true` will
6828 * allow a component to have its properties bound to the controller, rather than to scope.
6830 * After the controller is instantiated, the initial values of the isolate scope bindings will be bound to the controller
6831 * properties. You can access these bindings once they have been initialized by providing a controller method called
6832 * `$onInit`, which is called after all the controllers on an element have been constructed and had their bindings
6835 * <div class="alert alert-warning">
6836 * **Deprecation warning:** although bindings for non-ES6 class controllers are currently
6837 * bound to `this` before the controller constructor is called, this use is now deprecated. Please place initialization
6838 * code that relies upon bindings inside a `$onInit` method on the controller, instead.
6841 * It is also possible to set `bindToController` to an object hash with the same format as the `scope` property.
6842 * This will set up the scope bindings to the controller directly. Note that `scope` can still be used
6843 * to define which kind of scope is created. By default, no scope is created. Use `scope: {}` to create an isolate
6844 * scope (useful for component directives).
6846 * If both `bindToController` and `scope` are defined and have object hashes, `bindToController` overrides `scope`.
6850 * Controller constructor function. The controller is instantiated before the
6851 * pre-linking phase and can be accessed by other directives (see
6852 * `require` attribute). This allows the directives to communicate with each other and augment
6853 * each other's behavior. The controller is injectable (and supports bracket notation) with the following locals:
6855 * * `$scope` - Current scope associated with the element
6856 * * `$element` - Current element
6857 * * `$attrs` - Current attributes object for the element
6858 * * `$transclude` - A transclude linking function pre-bound to the correct transclusion scope:
6859 * `function([scope], cloneLinkingFn, futureParentElement, slotName)`:
6860 * * `scope`: (optional) override the scope.
6861 * * `cloneLinkingFn`: (optional) argument to create clones of the original transcluded content.
6862 * * `futureParentElement` (optional):
6863 * * defines the parent to which the `cloneLinkingFn` will add the cloned elements.
6864 * * default: `$element.parent()` resp. `$element` for `transclude:'element'` resp. `transclude:true`.
6865 * * only needed for transcludes that are allowed to contain non html elements (e.g. SVG elements)
6866 * and when the `cloneLinkinFn` is passed,
6867 * as those elements need to created and cloned in a special way when they are defined outside their
6868 * usual containers (e.g. like `<svg>`).
6869 * * See also the `directive.templateNamespace` property.
6870 * * `slotName`: (optional) the name of the slot to transclude. If falsy (e.g. `null`, `undefined` or `''`)
6871 * then the default translusion is provided.
6872 * The `$transclude` function also has a method on it, `$transclude.isSlotFilled(slotName)`, which returns
6873 * `true` if the specified slot contains content (i.e. one or more DOM nodes).
6875 * The controller can provide the following methods that act as life-cycle hooks:
6876 * * `$onInit()` - Called on each controller after all the controllers on an element have been constructed and
6877 * had their bindings initialized (and before the pre & post linking functions for the directives on
6878 * this element). This is a good place to put initialization code for your controller.
6879 * * `$onChanges(changesObj)` - Called whenever one-way (`<`) or interpolation (`@`) bindings are updated. The
6880 * `changesObj` is a hash whose keys are the names of the bound properties that have changed, and the values are an
6881 * object of the form `{ currentValue, previousValue, isFirstChange() }`. Use this hook to trigger updates within a
6882 * component such as cloning the bound value to prevent accidental mutation of the outer value.
6883 * * `$onDestroy()` - Called on a controller when its containing scope is destroyed. Use this hook for releasing
6884 * external resources, watches and event handlers. Note that components have their `$onDestroy()` hooks called in
6885 * the same order as the `$scope.$broadcast` events are triggered, which is top down. This means that parent
6886 * components will have their `$onDestroy()` hook called before child components.
6887 * * `$postLink()` - Called after this controller's element and its children have been linked. Similar to the post-link
6888 * function this hook can be used to set up DOM event handlers and do direct DOM manipulation.
6889 * Note that child elements that contain `templateUrl` directives will not have been compiled and linked since
6890 * they are waiting for their template to load asynchronously and their own compilation and linking has been
6891 * suspended until that occurs.
6895 * Require another directive and inject its controller as the fourth argument to the linking function. The
6896 * `require` property can be a string, an array or an object:
6897 * * a **string** containing the name of the directive to pass to the linking function
6898 * * an **array** containing the names of directives to pass to the linking function. The argument passed to the
6899 * linking function will be an array of controllers in the same order as the names in the `require` property
6900 * * an **object** whose property values are the names of the directives to pass to the linking function. The argument
6901 * passed to the linking function will also be an object with matching keys, whose values will hold the corresponding
6904 * If the `require` property is an object and `bindToController` is truthy, then the required controllers are
6905 * bound to the controller using the keys of the `require` property. This binding occurs after all the controllers
6906 * have been constructed but before `$onInit` is called.
6907 * See the {@link $compileProvider#component} helper for an example of how this can be used.
6909 * If no such required directive(s) can be found, or if the directive does not have a controller, then an error is
6910 * raised (unless no link function is specified and the required controllers are not being bound to the directive
6911 * controller, in which case error checking is skipped). The name can be prefixed with:
6913 * * (no prefix) - Locate the required controller on the current element. Throw an error if not found.
6914 * * `?` - Attempt to locate the required controller or pass `null` to the `link` fn if not found.
6915 * * `^` - Locate the required controller by searching the element and its parents. Throw an error if not found.
6916 * * `^^` - Locate the required controller by searching the element's parents. Throw an error if not found.
6917 * * `?^` - Attempt to locate the required controller by searching the element and its parents or pass
6918 * `null` to the `link` fn if not found.
6919 * * `?^^` - Attempt to locate the required controller by searching the element's parents, or pass
6920 * `null` to the `link` fn if not found.
6923 * #### `controllerAs`
6924 * Identifier name for a reference to the controller in the directive's scope.
6925 * This allows the controller to be referenced from the directive template. This is especially
6926 * useful when a directive is used as component, i.e. with an `isolate` scope. It's also possible
6927 * to use it in a directive without an `isolate` / `new` scope, but you need to be aware that the
6928 * `controllerAs` reference might overwrite a property that already exists on the parent scope.
6932 * String of subset of `EACM` which restricts the directive to a specific directive
6933 * declaration style. If omitted, the defaults (elements and attributes) are used.
6935 * * `E` - Element name (default): `<my-directive></my-directive>`
6936 * * `A` - Attribute (default): `<div my-directive="exp"></div>`
6937 * * `C` - Class: `<div class="my-directive: exp;"></div>`
6938 * * `M` - Comment: `<!-- directive: my-directive exp -->`
6941 * #### `templateNamespace`
6942 * String representing the document type used by the markup in the template.
6943 * AngularJS needs this information as those elements need to be created and cloned
6944 * in a special way when they are defined outside their usual containers like `<svg>` and `<math>`.
6946 * * `html` - All root nodes in the template are HTML. Root nodes may also be
6947 * top-level elements such as `<svg>` or `<math>`.
6948 * * `svg` - The root nodes in the template are SVG elements (excluding `<math>`).
6949 * * `math` - The root nodes in the template are MathML elements (excluding `<svg>`).
6951 * If no `templateNamespace` is specified, then the namespace is considered to be `html`.
6954 * HTML markup that may:
6955 * * Replace the contents of the directive's element (default).
6956 * * Replace the directive's element itself (if `replace` is true - DEPRECATED).
6957 * * Wrap the contents of the directive's element (if `transclude` is true).
6961 * * A string. For example `<div red-on-hover>{{delete_str}}</div>`.
6962 * * A function which takes two arguments `tElement` and `tAttrs` (described in the `compile`
6963 * function api below) and returns a string value.
6966 * #### `templateUrl`
6967 * This is similar to `template` but the template is loaded from the specified URL, asynchronously.
6969 * Because template loading is asynchronous the compiler will suspend compilation of directives on that element
6970 * for later when the template has been resolved. In the meantime it will continue to compile and link
6971 * sibling and parent elements as though this element had not contained any directives.
6973 * The compiler does not suspend the entire compilation to wait for templates to be loaded because this
6974 * would result in the whole app "stalling" until all templates are loaded asynchronously - even in the
6975 * case when only one deeply nested directive has `templateUrl`.
6977 * Template loading is asynchronous even if the template has been preloaded into the {@link $templateCache}
6979 * You can specify `templateUrl` as a string representing the URL or as a function which takes two
6980 * arguments `tElement` and `tAttrs` (described in the `compile` function api below) and returns
6981 * a string value representing the url. In either case, the template URL is passed through {@link
6982 * $sce#getTrustedResourceUrl $sce.getTrustedResourceUrl}.
6985 * #### `replace` ([*DEPRECATED*!], will be removed in next major release - i.e. v2.0)
6986 * specify what the template should replace. Defaults to `false`.
6988 * * `true` - the template will replace the directive's element.
6989 * * `false` - the template will replace the contents of the directive's element.
6991 * The replacement process migrates all of the attributes / classes from the old element to the new
6992 * one. See the {@link guide/directive#template-expanding-directive
6993 * Directives Guide} for an example.
6995 * There are very few scenarios where element replacement is required for the application function,
6996 * the main one being reusable custom components that are used within SVG contexts
6997 * (because SVG doesn't work with custom elements in the DOM tree).
7000 * Extract the contents of the element where the directive appears and make it available to the directive.
7001 * The contents are compiled and provided to the directive as a **transclusion function**. See the
7002 * {@link $compile#transclusion Transclusion} section below.
7008 * function compile(tElement, tAttrs, transclude) { ... }
7011 * The compile function deals with transforming the template DOM. Since most directives do not do
7012 * template transformation, it is not used often. The compile function takes the following arguments:
7014 * * `tElement` - template element - The element where the directive has been declared. It is
7015 * safe to do template transformation on the element and child elements only.
7017 * * `tAttrs` - template attributes - Normalized list of attributes declared on this element shared
7018 * between all directive compile functions.
7020 * * `transclude` - [*DEPRECATED*!] A transclude linking function: `function(scope, cloneLinkingFn)`
7022 * <div class="alert alert-warning">
7023 * **Note:** The template instance and the link instance may be different objects if the template has
7024 * been cloned. For this reason it is **not** safe to do anything other than DOM transformations that
7025 * apply to all cloned DOM nodes within the compile function. Specifically, DOM listener registration
7026 * should be done in a linking function rather than in a compile function.
7029 * <div class="alert alert-warning">
7030 * **Note:** The compile function cannot handle directives that recursively use themselves in their
7031 * own templates or compile functions. Compiling these directives results in an infinite loop and
7032 * stack overflow errors.
7034 * This can be avoided by manually using $compile in the postLink function to imperatively compile
7035 * a directive's template instead of relying on automatic template compilation via `template` or
7036 * `templateUrl` declaration or manual compilation inside the compile function.
7039 * <div class="alert alert-danger">
7040 * **Note:** The `transclude` function that is passed to the compile function is deprecated, as it
7041 * e.g. does not know about the right outer scope. Please use the transclude function that is passed
7042 * to the link function instead.
7045 * A compile function can have a return value which can be either a function or an object.
7047 * * returning a (post-link) function - is equivalent to registering the linking function via the
7048 * `link` property of the config object when the compile function is empty.
7050 * * returning an object with function(s) registered via `pre` and `post` properties - allows you to
7051 * control when a linking function should be called during the linking phase. See info about
7052 * pre-linking and post-linking functions below.
7056 * This property is used only if the `compile` property is not defined.
7059 * function link(scope, iElement, iAttrs, controller, transcludeFn) { ... }
7062 * The link function is responsible for registering DOM listeners as well as updating the DOM. It is
7063 * executed after the template has been cloned. This is where most of the directive logic will be
7066 * * `scope` - {@link ng.$rootScope.Scope Scope} - The scope to be used by the
7067 * directive for registering {@link ng.$rootScope.Scope#$watch watches}.
7069 * * `iElement` - instance element - The element where the directive is to be used. It is safe to
7070 * manipulate the children of the element only in `postLink` function since the children have
7071 * already been linked.
7073 * * `iAttrs` - instance attributes - Normalized list of attributes declared on this element shared
7074 * between all directive linking functions.
7076 * * `controller` - the directive's required controller instance(s) - Instances are shared
7077 * among all directives, which allows the directives to use the controllers as a communication
7078 * channel. The exact value depends on the directive's `require` property:
7079 * * no controller(s) required: the directive's own controller, or `undefined` if it doesn't have one
7080 * * `string`: the controller instance
7081 * * `array`: array of controller instances
7083 * If a required controller cannot be found, and it is optional, the instance is `null`,
7084 * otherwise the {@link error:$compile:ctreq Missing Required Controller} error is thrown.
7086 * Note that you can also require the directive's own controller - it will be made available like
7087 * any other controller.
7089 * * `transcludeFn` - A transclude linking function pre-bound to the correct transclusion scope.
7090 * This is the same as the `$transclude`
7091 * parameter of directive controllers, see there for details.
7092 * `function([scope], cloneLinkingFn, futureParentElement)`.
7094 * #### Pre-linking function
7096 * Executed before the child elements are linked. Not safe to do DOM transformation since the
7097 * compiler linking function will fail to locate the correct elements for linking.
7099 * #### Post-linking function
7101 * Executed after the child elements are linked.
7103 * Note that child elements that contain `templateUrl` directives will not have been compiled
7104 * and linked since they are waiting for their template to load asynchronously and their own
7105 * compilation and linking has been suspended until that occurs.
7107 * It is safe to do DOM transformation in the post-linking function on elements that are not waiting
7108 * for their async templates to be resolved.
7113 * Transclusion is the process of extracting a collection of DOM elements from one part of the DOM and
7114 * copying them to another part of the DOM, while maintaining their connection to the original AngularJS
7115 * scope from where they were taken.
7117 * Transclusion is used (often with {@link ngTransclude}) to insert the
7118 * original contents of a directive's element into a specified place in the template of the directive.
7119 * The benefit of transclusion, over simply moving the DOM elements manually, is that the transcluded
7120 * content has access to the properties on the scope from which it was taken, even if the directive
7121 * has isolated scope.
7122 * See the {@link guide/directive#creating-a-directive-that-wraps-other-elements Directives Guide}.
7124 * This makes it possible for the widget to have private state for its template, while the transcluded
7125 * content has access to its originating scope.
7127 * <div class="alert alert-warning">
7128 * **Note:** When testing an element transclude directive you must not place the directive at the root of the
7129 * DOM fragment that is being compiled. See {@link guide/unit-testing#testing-transclusion-directives
7130 * Testing Transclusion Directives}.
7133 * There are three kinds of transclusion depending upon whether you want to transclude just the contents of the
7134 * directive's element, the entire element or multiple parts of the element contents:
7136 * * `true` - transclude the content (i.e. the child nodes) of the directive's element.
7137 * * `'element'` - transclude the whole of the directive's element including any directives on this
7138 * element that defined at a lower priority than this directive. When used, the `template`
7139 * property is ignored.
7140 * * **`{...}` (an object hash):** - map elements of the content onto transclusion "slots" in the template.
7142 * **Mult-slot transclusion** is declared by providing an object for the `transclude` property.
7144 * This object is a map where the keys are the name of the slot to fill and the value is an element selector
7145 * used to match the HTML to the slot. The element selector should be in normalized form (e.g. `myElement`)
7146 * and will match the standard element variants (e.g. `my-element`, `my:element`, `data-my-element`, etc).
7148 * For further information check out the guide on {@link guide/directive#matching-directives Matching Directives}
7150 * If the element selector is prefixed with a `?` then that slot is optional.
7152 * For example, the transclude object `{ slotA: '?myCustomElement' }` maps `<my-custom-element>` elements to
7153 * the `slotA` slot, which can be accessed via the `$transclude` function or via the {@link ngTransclude} directive.
7155 * Slots that are not marked as optional (`?`) will trigger a compile time error if there are no matching elements
7156 * in the transclude content. If you wish to know if an optional slot was filled with content, then you can call
7157 * `$transclude.isSlotFilled(slotName)` on the transclude function passed to the directive's link function and
7158 * injectable into the directive's controller.
7161 * #### Transclusion Functions
7163 * When a directive requests transclusion, the compiler extracts its contents and provides a **transclusion
7164 * function** to the directive's `link` function and `controller`. This transclusion function is a special
7165 * **linking function** that will return the compiled contents linked to a new transclusion scope.
7167 * <div class="alert alert-info">
7168 * If you are just using {@link ngTransclude} then you don't need to worry about this function, since
7169 * ngTransclude will deal with it for us.
7172 * If you want to manually control the insertion and removal of the transcluded content in your directive
7173 * then you must use this transclude function. When you call a transclude function it returns a a jqLite/JQuery
7174 * object that contains the compiled DOM, which is linked to the correct transclusion scope.
7176 * When you call a transclusion function you can pass in a **clone attach function**. This function accepts
7177 * two parameters, `function(clone, scope) { ... }`, where the `clone` is a fresh compiled copy of your transcluded
7178 * content and the `scope` is the newly created transclusion scope, to which the clone is bound.
7180 * <div class="alert alert-info">
7181 * **Best Practice**: Always provide a `cloneFn` (clone attach function) when you call a transclude function
7182 * since you then get a fresh clone of the original DOM and also have access to the new transclusion scope.
7185 * It is normal practice to attach your transcluded content (`clone`) to the DOM inside your **clone
7186 * attach function**:
7189 * var transcludedContent, transclusionScope;
7191 * $transclude(function(clone, scope) {
7192 * element.append(clone);
7193 * transcludedContent = clone;
7194 * transclusionScope = scope;
7198 * Later, if you want to remove the transcluded content from your DOM then you should also destroy the
7199 * associated transclusion scope:
7202 * transcludedContent.remove();
7203 * transclusionScope.$destroy();
7206 * <div class="alert alert-info">
7207 * **Best Practice**: if you intend to add and remove transcluded content manually in your directive
7208 * (by calling the transclude function to get the DOM and calling `element.remove()` to remove it),
7209 * then you are also responsible for calling `$destroy` on the transclusion scope.
7212 * The built-in DOM manipulation directives, such as {@link ngIf}, {@link ngSwitch} and {@link ngRepeat}
7213 * automatically destroy their transcluded clones as necessary so you do not need to worry about this if
7214 * you are simply using {@link ngTransclude} to inject the transclusion into your directive.
7217 * #### Transclusion Scopes
7219 * When you call a transclude function it returns a DOM fragment that is pre-bound to a **transclusion
7220 * scope**. This scope is special, in that it is a child of the directive's scope (and so gets destroyed
7221 * when the directive's scope gets destroyed) but it inherits the properties of the scope from which it
7224 * For example consider a directive that uses transclusion and isolated scope. The DOM hierarchy might look
7230 * <div transclusion>
7236 * The `$parent` scope hierarchy will look like this:
7244 * but the scopes will inherit prototypically from different scopes to their `$parent`.
7255 * The {@link ng.$compile.directive.Attributes Attributes} object - passed as a parameter in the
7256 * `link()` or `compile()` functions. It has a variety of uses.
7258 * * *Accessing normalized attribute names:* Directives like 'ngBind' can be expressed in many ways:
7259 * 'ng:bind', `data-ng-bind`, or 'x-ng-bind'. The attributes object allows for normalized access
7260 * to the attributes.
7262 * * *Directive inter-communication:* All directives share the same instance of the attributes
7263 * object which allows the directives to use the attributes object as inter directive
7266 * * *Supports interpolation:* Interpolation attributes are assigned to the attribute object
7267 * allowing other directives to read the interpolated value.
7269 * * *Observing interpolated attributes:* Use `$observe` to observe the value changes of attributes
7270 * that contain interpolation (e.g. `src="{{bar}}"`). Not only is this very efficient but it's also
7271 * the only way to easily get the actual value because during the linking phase the interpolation
7272 * hasn't been evaluated yet and so the value is at this time set to `undefined`.
7275 * function linkingFn(scope, elm, attrs, ctrl) {
7276 * // get the attribute value
7277 * console.log(attrs.ngModel);
7279 * // change the attribute
7280 * attrs.$set('ngModel', 'new value');
7282 * // observe changes to interpolated attribute
7283 * attrs.$observe('ngModel', function(value) {
7284 * console.log('ngModel has changed value to ' + value);
7291 * <div class="alert alert-warning">
7292 * **Note**: Typically directives are registered with `module.directive`. The example below is
7293 * to illustrate how `$compile` works.
7296 <example module="compileExample">
7297 <file name="index.html">
7299 angular.module('compileExample', [], function($compileProvider) {
7300 // configure new 'compile' directive by passing a directive
7301 // factory function. The factory function injects the '$compile'
7302 $compileProvider.directive('compile', function($compile) {
7303 // directive factory creates a link function
7304 return function(scope, element, attrs) {
7307 // watch the 'compile' expression for changes
7308 return scope.$eval(attrs.compile);
7311 // when the 'compile' expression changes
7312 // assign it into the current DOM
7313 element.html(value);
7315 // compile the new DOM and link it to the current
7317 // NOTE: we only compile .childNodes so that
7318 // we don't get into infinite loop compiling ourselves
7319 $compile(element.contents())(scope);
7325 .controller('GreeterController', ['$scope', function($scope) {
7326 $scope.name = 'Angular';
7327 $scope.html = 'Hello {{name}}';
7330 <div ng-controller="GreeterController">
7331 <input ng-model="name"> <br/>
7332 <textarea ng-model="html"></textarea> <br/>
7333 <div compile="html"></div>
7336 <file name="protractor.js" type="protractor">
7337 it('should auto compile', function() {
7338 var textarea = $('textarea');
7339 var output = $('div[compile]');
7340 // The initial state reads 'Hello Angular'.
7341 expect(output.getText()).toBe('Hello Angular');
7343 textarea.sendKeys('{{name}}!');
7344 expect(output.getText()).toBe('Angular!');
7351 * @param {string|DOMElement} element Element or HTML string to compile into a template function.
7352 * @param {function(angular.Scope, cloneAttachFn=)} transclude function available to directives - DEPRECATED.
7354 * <div class="alert alert-danger">
7355 * **Note:** Passing a `transclude` function to the $compile function is deprecated, as it
7356 * e.g. will not use the right outer scope. Please pass the transclude function as a
7357 * `parentBoundTranscludeFn` to the link function instead.
7360 * @param {number} maxPriority only apply directives lower than given priority (Only effects the
7361 * root element(s), not their children)
7362 * @returns {function(scope, cloneAttachFn=, options=)} a link function which is used to bind template
7363 * (a DOM element/tree) to a scope. Where:
7365 * * `scope` - A {@link ng.$rootScope.Scope Scope} to bind to.
7366 * * `cloneAttachFn` - If `cloneAttachFn` is provided, then the link function will clone the
7367 * `template` and call the `cloneAttachFn` function allowing the caller to attach the
7368 * cloned elements to the DOM document at the appropriate place. The `cloneAttachFn` is
7369 * called as: <br/> `cloneAttachFn(clonedElement, scope)` where:
7371 * * `clonedElement` - is a clone of the original `element` passed into the compiler.
7372 * * `scope` - is the current scope with which the linking function is working with.
7374 * * `options` - An optional object hash with linking options. If `options` is provided, then the following
7375 * keys may be used to control linking behavior:
7377 * * `parentBoundTranscludeFn` - the transclude function made available to
7378 * directives; if given, it will be passed through to the link functions of
7379 * directives found in `element` during compilation.
7380 * * `transcludeControllers` - an object hash with keys that map controller names
7381 * to a hash with the key `instance`, which maps to the controller instance;
7382 * if given, it will make the controllers available to directives on the compileNode:
7386 * instance: parentControllerInstance
7390 * * `futureParentElement` - defines the parent to which the `cloneAttachFn` will add
7391 * the cloned elements; only needed for transcludes that are allowed to contain non html
7392 * elements (e.g. SVG elements). See also the directive.controller property.
7394 * Calling the linking function returns the element of the template. It is either the original
7395 * element passed in, or the clone of the element if the `cloneAttachFn` is provided.
7397 * After linking the view is not updated until after a call to $digest which typically is done by
7398 * Angular automatically.
7400 * If you need access to the bound view, there are two ways to do it:
7402 * - If you are not asking the linking function to clone the template, create the DOM element(s)
7403 * before you send them to the compiler and keep this reference around.
7405 * var element = $compile('<p>{{total}}</p>')(scope);
7408 * - if on the other hand, you need the element to be cloned, the view reference from the original
7409 * example would not point to the clone, but rather to the original template that was cloned. In
7410 * this case, you can access the clone via the cloneAttachFn:
7412 * var templateElement = angular.element('<p>{{total}}</p>'),
7415 * var clonedElement = $compile(templateElement)(scope, function(clonedElement, scope) {
7416 * //attach the clone to DOM document at the right place
7419 * //now we have reference to the cloned DOM via `clonedElement`
7423 * For information on how the compiler works, see the
7424 * {@link guide/compiler Angular HTML Compiler} section of the Developer Guide.
7427 var $compileMinErr = minErr('$compile');
7429 function UNINITIALIZED_VALUE() {}
7430 var _UNINITIALIZED_VALUE = new UNINITIALIZED_VALUE();
7434 * @name $compileProvider
7438 $CompileProvider.$inject = ['$provide', '$$sanitizeUriProvider'];
7439 function $CompileProvider($provide, $$sanitizeUriProvider) {
7440 var hasDirectives = {},
7441 Suffix = 'Directive',
7442 COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\w\-]+)\s+(.*)$/,
7443 CLASS_DIRECTIVE_REGEXP = /(([\w\-]+)(?:\:([^;]+))?;?)/,
7444 ALL_OR_NOTHING_ATTRS = makeMap('ngSrc,ngSrcset,src,srcset'),
7445 REQUIRE_PREFIX_REGEXP = /^(?:(\^\^?)?(\?)?(\^\^?)?)?/;
7447 // Ref: http://developers.whatwg.org/webappapis.html#event-handler-idl-attributes
7448 // The assumption is that future DOM event attribute names will begin with
7449 // 'on' and be composed of only English letters.
7450 var EVENT_HANDLER_ATTR_REGEXP = /^(on[a-z]+|formaction)$/;
7451 var bindingCache = createMap();
7453 function parseIsolateBindings(scope, directiveName, isController) {
7454 var LOCAL_REGEXP = /^\s*([@&<]|=(\*?))(\??)\s*(\w*)\s*$/;
7456 var bindings = createMap();
7458 forEach(scope, function(definition, scopeName) {
7459 if (definition in bindingCache) {
7460 bindings[scopeName] = bindingCache[definition];
7463 var match = definition.match(LOCAL_REGEXP);
7466 throw $compileMinErr('iscp',
7467 "Invalid {3} for directive '{0}'." +
7468 " Definition: {... {1}: '{2}' ...}",
7469 directiveName, scopeName, definition,
7470 (isController ? "controller bindings definition" :
7471 "isolate scope definition"));
7474 bindings[scopeName] = {
7476 collection: match[2] === '*',
7477 optional: match[3] === '?',
7478 attrName: match[4] || scopeName
7481 bindingCache[definition] = bindings[scopeName];
7488 function parseDirectiveBindings(directive, directiveName) {
7491 bindToController: null
7493 if (isObject(directive.scope)) {
7494 if (directive.bindToController === true) {
7495 bindings.bindToController = parseIsolateBindings(directive.scope,
7496 directiveName, true);
7497 bindings.isolateScope = {};
7499 bindings.isolateScope = parseIsolateBindings(directive.scope,
7500 directiveName, false);
7503 if (isObject(directive.bindToController)) {
7504 bindings.bindToController =
7505 parseIsolateBindings(directive.bindToController, directiveName, true);
7507 if (isObject(bindings.bindToController)) {
7508 var controller = directive.controller;
7509 var controllerAs = directive.controllerAs;
7511 // There is no controller, there may or may not be a controllerAs property
7512 throw $compileMinErr('noctrl',
7513 "Cannot bind to controller without directive '{0}'s controller.",
7515 } else if (!identifierForController(controller, controllerAs)) {
7516 // There is a controller, but no identifier or controllerAs property
7517 throw $compileMinErr('noident',
7518 "Cannot bind to controller without identifier for directive '{0}'.",
7525 function assertValidDirectiveName(name) {
7526 var letter = name.charAt(0);
7527 if (!letter || letter !== lowercase(letter)) {
7528 throw $compileMinErr('baddir', "Directive/Component name '{0}' is invalid. The first character must be a lowercase letter", name);
7530 if (name !== name.trim()) {
7531 throw $compileMinErr('baddir',
7532 "Directive/Component name '{0}' is invalid. The name should not contain leading or trailing whitespaces",
7539 * @name $compileProvider#directive
7543 * Register a new directive with the compiler.
7545 * @param {string|Object} name Name of the directive in camel-case (i.e. <code>ngBind</code> which
7546 * will match as <code>ng-bind</code>), or an object map of directives where the keys are the
7547 * names and the values are the factories.
7548 * @param {Function|Array} directiveFactory An injectable directive factory function. See the
7549 * {@link guide/directive directive guide} and the {@link $compile compile API} for more info.
7550 * @returns {ng.$compileProvider} Self for chaining.
7552 this.directive = function registerDirective(name, directiveFactory) {
7553 assertNotHasOwnProperty(name, 'directive');
7554 if (isString(name)) {
7555 assertValidDirectiveName(name);
7556 assertArg(directiveFactory, 'directiveFactory');
7557 if (!hasDirectives.hasOwnProperty(name)) {
7558 hasDirectives[name] = [];
7559 $provide.factory(name + Suffix, ['$injector', '$exceptionHandler',
7560 function($injector, $exceptionHandler) {
7561 var directives = [];
7562 forEach(hasDirectives[name], function(directiveFactory, index) {
7564 var directive = $injector.invoke(directiveFactory);
7565 if (isFunction(directive)) {
7566 directive = { compile: valueFn(directive) };
7567 } else if (!directive.compile && directive.link) {
7568 directive.compile = valueFn(directive.link);
7570 directive.priority = directive.priority || 0;
7571 directive.index = index;
7572 directive.name = directive.name || name;
7573 directive.require = directive.require || (directive.controller && directive.name);
7574 directive.restrict = directive.restrict || 'EA';
7575 directive.$$moduleName = directiveFactory.$$moduleName;
7576 directives.push(directive);
7578 $exceptionHandler(e);
7584 hasDirectives[name].push(directiveFactory);
7586 forEach(name, reverseParams(registerDirective));
7593 * @name $compileProvider#component
7595 * @param {string} name Name of the component in camelCase (i.e. `myComp` which will match `<my-comp>`)
7596 * @param {Object} options Component definition object (a simplified
7597 * {@link ng.$compile#directive-definition-object directive definition object}),
7598 * with the following properties (all optional):
7600 * - `controller` – `{(string|function()=}` – controller constructor function that should be
7601 * associated with newly created scope or the name of a {@link ng.$compile#-controller-
7602 * registered controller} if passed as a string. An empty `noop` function by default.
7603 * - `controllerAs` – `{string=}` – identifier name for to reference the controller in the component's scope.
7604 * If present, the controller will be published to scope under the `controllerAs` name.
7605 * If not present, this will default to be `$ctrl`.
7606 * - `template` – `{string=|function()=}` – html template as a string or a function that
7607 * returns an html template as a string which should be used as the contents of this component.
7608 * Empty string by default.
7610 * If `template` is a function, then it is {@link auto.$injector#invoke injected} with
7611 * the following locals:
7613 * - `$element` - Current element
7614 * - `$attrs` - Current attributes object for the element
7616 * - `templateUrl` – `{string=|function()=}` – path or function that returns a path to an html
7617 * template that should be used as the contents of this component.
7619 * If `templateUrl` is a function, then it is {@link auto.$injector#invoke injected} with
7620 * the following locals:
7622 * - `$element` - Current element
7623 * - `$attrs` - Current attributes object for the element
7625 * - `bindings` – `{object=}` – defines bindings between DOM attributes and component properties.
7626 * Component properties are always bound to the component controller and not to the scope.
7627 * See {@link ng.$compile#-bindtocontroller- `bindToController`}.
7628 * - `transclude` – `{boolean=}` – whether {@link $compile#transclusion content transclusion} is enabled.
7629 * Disabled by default.
7630 * - `require` - `{Object<string, string>=}` - requires the controllers of other directives and binds them to
7631 * this component's controller. The object keys specify the property names under which the required
7632 * controllers (object values) will be bound. See {@link ng.$compile#-require- `require`}.
7633 * - `$...` – additional properties to attach to the directive factory function and the controller
7634 * constructor function. (This is used by the component router to annotate)
7636 * @returns {ng.$compileProvider} the compile provider itself, for chaining of function calls.
7638 * Register a **component definition** with the compiler. This is a shorthand for registering a special
7639 * type of directive, which represents a self-contained UI component in your application. Such components
7640 * are always isolated (i.e. `scope: {}`) and are always restricted to elements (i.e. `restrict: 'E'`).
7642 * Component definitions are very simple and do not require as much configuration as defining general
7643 * directives. Component definitions usually consist only of a template and a controller backing it.
7645 * In order to make the definition easier, components enforce best practices like use of `controllerAs`,
7646 * `bindToController`. They always have **isolate scope** and are restricted to elements.
7648 * Here are a few examples of how you would usually define components:
7651 * var myMod = angular.module(...);
7652 * myMod.component('myComp', {
7653 * template: '<div>My name is {{$ctrl.name}}</div>',
7654 * controller: function() {
7655 * this.name = 'shahar';
7659 * myMod.component('myComp', {
7660 * template: '<div>My name is {{$ctrl.name}}</div>',
7661 * bindings: {name: '@'}
7664 * myMod.component('myComp', {
7665 * templateUrl: 'views/my-comp.html',
7666 * controller: 'MyCtrl',
7667 * controllerAs: 'ctrl',
7668 * bindings: {name: '@'}
7672 * For more examples, and an in-depth guide, see the {@link guide/component component guide}.
7675 * See also {@link ng.$compileProvider#directive $compileProvider.directive()}.
7677 this.component = function registerComponent(name, options) {
7678 var controller = options.controller || function() {};
7680 function factory($injector) {
7681 function makeInjectable(fn) {
7682 if (isFunction(fn) || isArray(fn)) {
7683 return function(tElement, tAttrs) {
7684 return $injector.invoke(fn, this, {$element: tElement, $attrs: tAttrs});
7691 var template = (!options.template && !options.templateUrl ? '' : options.template);
7693 controller: controller,
7694 controllerAs: identifierForController(options.controller) || options.controllerAs || '$ctrl',
7695 template: makeInjectable(template),
7696 templateUrl: makeInjectable(options.templateUrl),
7697 transclude: options.transclude,
7699 bindToController: options.bindings || {},
7701 require: options.require
7704 // Copy annotations (starting with $) over to the DDO
7705 forEach(options, function(val, key) {
7706 if (key.charAt(0) === '$') ddo[key] = val;
7712 // TODO(pete) remove the following `forEach` before we release 1.6.0
7713 // The component-router@0.2.0 looks for the annotations on the controller constructor
7714 // Nothing in Angular looks for annotations on the factory function but we can't remove
7715 // it from 1.5.x yet.
7717 // Copy any annotation properties (starting with $) over to the factory and controller constructor functions
7718 // These could be used by libraries such as the new component router
7719 forEach(options, function(val, key) {
7720 if (key.charAt(0) === '$') {
7722 // Don't try to copy over annotations to named controller
7723 if (isFunction(controller)) controller[key] = val;
7727 factory.$inject = ['$injector'];
7729 return this.directive(name, factory);
7735 * @name $compileProvider#aHrefSanitizationWhitelist
7739 * Retrieves or overrides the default regular expression that is used for whitelisting of safe
7740 * urls during a[href] sanitization.
7742 * The sanitization is a security measure aimed at preventing XSS attacks via html links.
7744 * Any url about to be assigned to a[href] via data-binding is first normalized and turned into
7745 * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist`
7746 * regular expression. If a match is found, the original url is written into the dom. Otherwise,
7747 * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
7749 * @param {RegExp=} regexp New regexp to whitelist urls with.
7750 * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
7751 * chaining otherwise.
7753 this.aHrefSanitizationWhitelist = function(regexp) {
7754 if (isDefined(regexp)) {
7755 $$sanitizeUriProvider.aHrefSanitizationWhitelist(regexp);
7758 return $$sanitizeUriProvider.aHrefSanitizationWhitelist();
7765 * @name $compileProvider#imgSrcSanitizationWhitelist
7769 * Retrieves or overrides the default regular expression that is used for whitelisting of safe
7770 * urls during img[src] sanitization.
7772 * The sanitization is a security measure aimed at prevent XSS attacks via html links.
7774 * Any url about to be assigned to img[src] via data-binding is first normalized and turned into
7775 * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist`
7776 * regular expression. If a match is found, the original url is written into the dom. Otherwise,
7777 * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
7779 * @param {RegExp=} regexp New regexp to whitelist urls with.
7780 * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
7781 * chaining otherwise.
7783 this.imgSrcSanitizationWhitelist = function(regexp) {
7784 if (isDefined(regexp)) {
7785 $$sanitizeUriProvider.imgSrcSanitizationWhitelist(regexp);
7788 return $$sanitizeUriProvider.imgSrcSanitizationWhitelist();
7794 * @name $compileProvider#debugInfoEnabled
7796 * @param {boolean=} enabled update the debugInfoEnabled state if provided, otherwise just return the
7797 * current debugInfoEnabled state
7798 * @returns {*} current value if used as getter or itself (chaining) if used as setter
7803 * Call this method to enable/disable various debug runtime information in the compiler such as adding
7804 * binding information and a reference to the current scope on to DOM elements.
7805 * If enabled, the compiler will add the following to DOM elements that have been bound to the scope
7806 * * `ng-binding` CSS class
7807 * * `$binding` data property containing an array of the binding expressions
7809 * You may want to disable this in production for a significant performance boost. See
7810 * {@link guide/production#disabling-debug-data Disabling Debug Data} for more.
7812 * The default value is true.
7814 var debugInfoEnabled = true;
7815 this.debugInfoEnabled = function(enabled) {
7816 if (isDefined(enabled)) {
7817 debugInfoEnabled = enabled;
7820 return debugInfoEnabled;
7827 * @name $compileProvider#onChangesTtl
7830 * Sets the number of times `$onChanges` hooks can trigger new changes before giving up and
7831 * assuming that the model is unstable.
7833 * The current default is 10 iterations.
7835 * In complex applications it's possible that dependencies between `$onChanges` hooks and bindings will result
7836 * in several iterations of calls to these hooks. However if an application needs more than the default 10
7837 * iterations to stabilize then you should investigate what is causing the model to continuously change during
7838 * the `$onChanges` hook execution.
7840 * Increasing the TTL could have performance implications, so you should not change it without proper justification.
7842 * @param {number} limit The number of `$onChanges` hook iterations.
7843 * @returns {number|object} the current limit (or `this` if called as a setter for chaining)
7845 this.onChangesTtl = function(value) {
7846 if (arguments.length) {
7854 '$injector', '$interpolate', '$exceptionHandler', '$templateRequest', '$parse',
7855 '$controller', '$rootScope', '$sce', '$animate', '$$sanitizeUri',
7856 function($injector, $interpolate, $exceptionHandler, $templateRequest, $parse,
7857 $controller, $rootScope, $sce, $animate, $$sanitizeUri) {
7859 var SIMPLE_ATTR_NAME = /^\w/;
7860 var specialAttrHolder = window.document.createElement('div');
7864 var onChangesTtl = TTL;
7865 // The onChanges hooks should all be run together in a single digest
7866 // When changes occur, the call to trigger their hooks will be added to this queue
7869 // This function is called in a $$postDigest to trigger all the onChanges hooks in a single digest
7870 function flushOnChangesQueue() {
7872 if (!(--onChangesTtl)) {
7873 // We have hit the TTL limit so reset everything
7874 onChangesQueue = undefined;
7875 throw $compileMinErr('infchng', '{0} $onChanges() iterations reached. Aborting!\n', TTL);
7877 // We must run this hook in an apply since the $$postDigest runs outside apply
7878 $rootScope.$apply(function() {
7879 for (var i = 0, ii = onChangesQueue.length; i < ii; ++i) {
7880 onChangesQueue[i]();
7882 // Reset the queue to trigger a new schedule next time there is a change
7883 onChangesQueue = undefined;
7891 function Attributes(element, attributesToCopy) {
7892 if (attributesToCopy) {
7893 var keys = Object.keys(attributesToCopy);
7896 for (i = 0, l = keys.length; i < l; i++) {
7898 this[key] = attributesToCopy[key];
7904 this.$$element = element;
7907 Attributes.prototype = {
7910 * @name $compile.directive.Attributes#$normalize
7914 * Converts an attribute name (e.g. dash/colon/underscore-delimited string, optionally prefixed with `x-` or
7915 * `data-`) to its normalized, camelCase form.
7917 * Also there is special case for Moz prefix starting with upper case letter.
7919 * For further information check out the guide on {@link guide/directive#matching-directives Matching Directives}
7921 * @param {string} name Name to normalize
7923 $normalize: directiveNormalize,
7928 * @name $compile.directive.Attributes#$addClass
7932 * Adds the CSS class value specified by the classVal parameter to the element. If animations
7933 * are enabled then an animation will be triggered for the class addition.
7935 * @param {string} classVal The className value that will be added to the element
7937 $addClass: function(classVal) {
7938 if (classVal && classVal.length > 0) {
7939 $animate.addClass(this.$$element, classVal);
7945 * @name $compile.directive.Attributes#$removeClass
7949 * Removes the CSS class value specified by the classVal parameter from the element. If
7950 * animations are enabled then an animation will be triggered for the class removal.
7952 * @param {string} classVal The className value that will be removed from the element
7954 $removeClass: function(classVal) {
7955 if (classVal && classVal.length > 0) {
7956 $animate.removeClass(this.$$element, classVal);
7962 * @name $compile.directive.Attributes#$updateClass
7966 * Adds and removes the appropriate CSS class values to the element based on the difference
7967 * between the new and old CSS class values (specified as newClasses and oldClasses).
7969 * @param {string} newClasses The current CSS className value
7970 * @param {string} oldClasses The former CSS className value
7972 $updateClass: function(newClasses, oldClasses) {
7973 var toAdd = tokenDifference(newClasses, oldClasses);
7974 if (toAdd && toAdd.length) {
7975 $animate.addClass(this.$$element, toAdd);
7978 var toRemove = tokenDifference(oldClasses, newClasses);
7979 if (toRemove && toRemove.length) {
7980 $animate.removeClass(this.$$element, toRemove);
7985 * Set a normalized attribute on the element in a way such that all directives
7986 * can share the attribute. This function properly handles boolean attributes.
7987 * @param {string} key Normalized key. (ie ngAttribute)
7988 * @param {string|boolean} value The value to set. If `null` attribute will be deleted.
7989 * @param {boolean=} writeAttr If false, does not write the value to DOM element attribute.
7991 * @param {string=} attrName Optional none normalized name. Defaults to key.
7993 $set: function(key, value, writeAttr, attrName) {
7994 // TODO: decide whether or not to throw an error if "class"
7995 //is set through this function since it may cause $updateClass to
7998 var node = this.$$element[0],
7999 booleanKey = getBooleanAttrName(node, key),
8000 aliasedKey = getAliasedAttrName(key),
8005 this.$$element.prop(key, value);
8006 attrName = booleanKey;
8007 } else if (aliasedKey) {
8008 this[aliasedKey] = value;
8009 observer = aliasedKey;
8014 // translate normalized key to actual key
8016 this.$attr[key] = attrName;
8018 attrName = this.$attr[key];
8020 this.$attr[key] = attrName = snake_case(key, '-');
8024 nodeName = nodeName_(this.$$element);
8026 if ((nodeName === 'a' && (key === 'href' || key === 'xlinkHref')) ||
8027 (nodeName === 'img' && key === 'src')) {
8028 // sanitize a[href] and img[src] values
8029 this[key] = value = $$sanitizeUri(value, key === 'src');
8030 } else if (nodeName === 'img' && key === 'srcset') {
8031 // sanitize img[srcset] values
8034 // first check if there are spaces because it's not the same pattern
8035 var trimmedSrcset = trim(value);
8036 // ( 999x ,| 999w ,| ,|, )
8037 var srcPattern = /(\s+\d+x\s*,|\s+\d+w\s*,|\s+,|,\s+)/;
8038 var pattern = /\s/.test(trimmedSrcset) ? srcPattern : /(,)/;
8040 // split srcset into tuple of uri and descriptor except for the last item
8041 var rawUris = trimmedSrcset.split(pattern);
8044 var nbrUrisWith2parts = Math.floor(rawUris.length / 2);
8045 for (var i = 0; i < nbrUrisWith2parts; i++) {
8046 var innerIdx = i * 2;
8048 result += $$sanitizeUri(trim(rawUris[innerIdx]), true);
8049 // add the descriptor
8050 result += (" " + trim(rawUris[innerIdx + 1]));
8053 // split the last item into uri and descriptor
8054 var lastTuple = trim(rawUris[i * 2]).split(/\s/);
8056 // sanitize the last uri
8057 result += $$sanitizeUri(trim(lastTuple[0]), true);
8059 // and add the last descriptor if any
8060 if (lastTuple.length === 2) {
8061 result += (" " + trim(lastTuple[1]));
8063 this[key] = value = result;
8066 if (writeAttr !== false) {
8067 if (value === null || isUndefined(value)) {
8068 this.$$element.removeAttr(attrName);
8070 if (SIMPLE_ATTR_NAME.test(attrName)) {
8071 this.$$element.attr(attrName, value);
8073 setSpecialAttr(this.$$element[0], attrName, value);
8079 var $$observers = this.$$observers;
8080 $$observers && forEach($$observers[observer], function(fn) {
8084 $exceptionHandler(e);
8092 * @name $compile.directive.Attributes#$observe
8096 * Observes an interpolated attribute.
8098 * The observer function will be invoked once during the next `$digest` following
8099 * compilation. The observer is then invoked whenever the interpolated value
8102 * @param {string} key Normalized key. (ie ngAttribute) .
8103 * @param {function(interpolatedValue)} fn Function that will be called whenever
8104 the interpolated value of the attribute changes.
8105 * See the {@link guide/interpolation#how-text-and-attribute-bindings-work Interpolation
8106 * guide} for more info.
8107 * @returns {function()} Returns a deregistration function for this observer.
8109 $observe: function(key, fn) {
8111 $$observers = (attrs.$$observers || (attrs.$$observers = createMap())),
8112 listeners = ($$observers[key] || ($$observers[key] = []));
8115 $rootScope.$evalAsync(function() {
8116 if (!listeners.$$inter && attrs.hasOwnProperty(key) && !isUndefined(attrs[key])) {
8117 // no one registered attribute interpolation function, so lets call it manually
8123 arrayRemove(listeners, fn);
8128 function setSpecialAttr(element, attrName, value) {
8129 // Attributes names that do not start with letters (such as `(click)`) cannot be set using `setAttribute`
8130 // so we have to jump through some hoops to get such an attribute
8131 // https://github.com/angular/angular.js/pull/13318
8132 specialAttrHolder.innerHTML = "<span " + attrName + ">";
8133 var attributes = specialAttrHolder.firstChild.attributes;
8134 var attribute = attributes[0];
8135 // We have to remove the attribute from its container element before we can add it to the destination element
8136 attributes.removeNamedItem(attribute.name);
8137 attribute.value = value;
8138 element.attributes.setNamedItem(attribute);
8141 function safeAddClass($element, className) {
8143 $element.addClass(className);
8145 // ignore, since it means that we are trying to set class on
8146 // SVG element, where class name is read-only.
8151 var startSymbol = $interpolate.startSymbol(),
8152 endSymbol = $interpolate.endSymbol(),
8153 denormalizeTemplate = (startSymbol == '{{' && endSymbol == '}}')
8155 : function denormalizeTemplate(template) {
8156 return template.replace(/\{\{/g, startSymbol).replace(/}}/g, endSymbol);
8158 NG_ATTR_BINDING = /^ngAttr[A-Z]/;
8159 var MULTI_ELEMENT_DIR_RE = /^(.+)Start$/;
8161 compile.$$addBindingInfo = debugInfoEnabled ? function $$addBindingInfo($element, binding) {
8162 var bindings = $element.data('$binding') || [];
8164 if (isArray(binding)) {
8165 bindings = bindings.concat(binding);
8167 bindings.push(binding);
8170 $element.data('$binding', bindings);
8173 compile.$$addBindingClass = debugInfoEnabled ? function $$addBindingClass($element) {
8174 safeAddClass($element, 'ng-binding');
8177 compile.$$addScopeInfo = debugInfoEnabled ? function $$addScopeInfo($element, scope, isolated, noTemplate) {
8178 var dataName = isolated ? (noTemplate ? '$isolateScopeNoTemplate' : '$isolateScope') : '$scope';
8179 $element.data(dataName, scope);
8182 compile.$$addScopeClass = debugInfoEnabled ? function $$addScopeClass($element, isolated) {
8183 safeAddClass($element, isolated ? 'ng-isolate-scope' : 'ng-scope');
8186 compile.$$createComment = function(directiveName, comment) {
8188 if (debugInfoEnabled) {
8189 content = ' ' + (directiveName || '') + ': ' + (comment || '') + ' ';
8191 return window.document.createComment(content);
8196 //================================
8198 function compile($compileNodes, transcludeFn, maxPriority, ignoreDirective,
8199 previousCompileContext) {
8200 if (!($compileNodes instanceof jqLite)) {
8201 // jquery always rewraps, whereas we need to preserve the original selector so that we can
8203 $compileNodes = jqLite($compileNodes);
8206 var NOT_EMPTY = /\S+/;
8208 // We can not compile top level text elements since text nodes can be merged and we will
8209 // not be able to attach scope data to them, so we will wrap them in <span>
8210 for (var i = 0, len = $compileNodes.length; i < len; i++) {
8211 var domNode = $compileNodes[i];
8213 if (domNode.nodeType === NODE_TYPE_TEXT && domNode.nodeValue.match(NOT_EMPTY) /* non-empty */) {
8214 jqLiteWrapNode(domNode, $compileNodes[i] = window.document.createElement('span'));
8218 var compositeLinkFn =
8219 compileNodes($compileNodes, transcludeFn, $compileNodes,
8220 maxPriority, ignoreDirective, previousCompileContext);
8221 compile.$$addScopeClass($compileNodes);
8222 var namespace = null;
8223 return function publicLinkFn(scope, cloneConnectFn, options) {
8224 assertArg(scope, 'scope');
8226 if (previousCompileContext && previousCompileContext.needsNewScope) {
8227 // A parent directive did a replace and a directive on this element asked
8228 // for transclusion, which caused us to lose a layer of element on which
8229 // we could hold the new transclusion scope, so we will create it manually
8231 scope = scope.$parent.$new();
8234 options = options || {};
8235 var parentBoundTranscludeFn = options.parentBoundTranscludeFn,
8236 transcludeControllers = options.transcludeControllers,
8237 futureParentElement = options.futureParentElement;
8239 // When `parentBoundTranscludeFn` is passed, it is a
8240 // `controllersBoundTransclude` function (it was previously passed
8241 // as `transclude` to directive.link) so we must unwrap it to get
8242 // its `boundTranscludeFn`
8243 if (parentBoundTranscludeFn && parentBoundTranscludeFn.$$boundTransclude) {
8244 parentBoundTranscludeFn = parentBoundTranscludeFn.$$boundTransclude;
8248 namespace = detectNamespaceForChildElements(futureParentElement);
8251 if (namespace !== 'html') {
8252 // When using a directive with replace:true and templateUrl the $compileNodes
8253 // (or a child element inside of them)
8254 // might change, so we need to recreate the namespace adapted compileNodes
8255 // for call to the link function.
8256 // Note: This will already clone the nodes...
8258 wrapTemplate(namespace, jqLite('<div>').append($compileNodes).html())
8260 } else if (cloneConnectFn) {
8261 // important!!: we must call our jqLite.clone() since the jQuery one is trying to be smart
8262 // and sometimes changes the structure of the DOM.
8263 $linkNode = JQLitePrototype.clone.call($compileNodes);
8265 $linkNode = $compileNodes;
8268 if (transcludeControllers) {
8269 for (var controllerName in transcludeControllers) {
8270 $linkNode.data('$' + controllerName + 'Controller', transcludeControllers[controllerName].instance);
8274 compile.$$addScopeInfo($linkNode, scope);
8276 if (cloneConnectFn) cloneConnectFn($linkNode, scope);
8277 if (compositeLinkFn) compositeLinkFn(scope, $linkNode, $linkNode, parentBoundTranscludeFn);
8282 function detectNamespaceForChildElements(parentElement) {
8283 // TODO: Make this detect MathML as well...
8284 var node = parentElement && parentElement[0];
8288 return nodeName_(node) !== 'foreignobject' && toString.call(node).match(/SVG/) ? 'svg' : 'html';
8293 * Compile function matches each node in nodeList against the directives. Once all directives
8294 * for a particular node are collected their compile functions are executed. The compile
8295 * functions return values - the linking functions - are combined into a composite linking
8296 * function, which is the a linking function for the node.
8298 * @param {NodeList} nodeList an array of nodes or NodeList to compile
8299 * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the
8300 * scope argument is auto-generated to the new child of the transcluded parent scope.
8301 * @param {DOMElement=} $rootElement If the nodeList is the root of the compilation tree then
8302 * the rootElement must be set the jqLite collection of the compile root. This is
8303 * needed so that the jqLite collection items can be replaced with widgets.
8304 * @param {number=} maxPriority Max directive priority.
8305 * @returns {Function} A composite linking function of all of the matched directives or null.
8307 function compileNodes(nodeList, transcludeFn, $rootElement, maxPriority, ignoreDirective,
8308 previousCompileContext) {
8310 attrs, directives, nodeLinkFn, childNodes, childLinkFn, linkFnFound, nodeLinkFnFound;
8312 for (var i = 0; i < nodeList.length; i++) {
8313 attrs = new Attributes();
8315 // we must always refer to nodeList[i] since the nodes can be replaced underneath us.
8316 directives = collectDirectives(nodeList[i], [], attrs, i === 0 ? maxPriority : undefined,
8319 nodeLinkFn = (directives.length)
8320 ? applyDirectivesToNode(directives, nodeList[i], attrs, transcludeFn, $rootElement,
8321 null, [], [], previousCompileContext)
8324 if (nodeLinkFn && nodeLinkFn.scope) {
8325 compile.$$addScopeClass(attrs.$$element);
8328 childLinkFn = (nodeLinkFn && nodeLinkFn.terminal ||
8329 !(childNodes = nodeList[i].childNodes) ||
8332 : compileNodes(childNodes,
8334 (nodeLinkFn.transcludeOnThisElement || !nodeLinkFn.templateOnThisElement)
8335 && nodeLinkFn.transclude) : transcludeFn);
8337 if (nodeLinkFn || childLinkFn) {
8338 linkFns.push(i, nodeLinkFn, childLinkFn);
8340 nodeLinkFnFound = nodeLinkFnFound || nodeLinkFn;
8343 //use the previous context only for the first element in the virtual group
8344 previousCompileContext = null;
8347 // return a linking function if we have found anything, null otherwise
8348 return linkFnFound ? compositeLinkFn : null;
8350 function compositeLinkFn(scope, nodeList, $rootElement, parentBoundTranscludeFn) {
8351 var nodeLinkFn, childLinkFn, node, childScope, i, ii, idx, childBoundTranscludeFn;
8355 if (nodeLinkFnFound) {
8356 // copy nodeList so that if a nodeLinkFn removes or adds an element at this DOM level our
8357 // offsets don't get screwed up
8358 var nodeListLength = nodeList.length;
8359 stableNodeList = new Array(nodeListLength);
8361 // create a sparse array by only copying the elements which have a linkFn
8362 for (i = 0; i < linkFns.length; i+=3) {
8364 stableNodeList[idx] = nodeList[idx];
8367 stableNodeList = nodeList;
8370 for (i = 0, ii = linkFns.length; i < ii;) {
8371 node = stableNodeList[linkFns[i++]];
8372 nodeLinkFn = linkFns[i++];
8373 childLinkFn = linkFns[i++];
8376 if (nodeLinkFn.scope) {
8377 childScope = scope.$new();
8378 compile.$$addScopeInfo(jqLite(node), childScope);
8383 if (nodeLinkFn.transcludeOnThisElement) {
8384 childBoundTranscludeFn = createBoundTranscludeFn(
8385 scope, nodeLinkFn.transclude, parentBoundTranscludeFn);
8387 } else if (!nodeLinkFn.templateOnThisElement && parentBoundTranscludeFn) {
8388 childBoundTranscludeFn = parentBoundTranscludeFn;
8390 } else if (!parentBoundTranscludeFn && transcludeFn) {
8391 childBoundTranscludeFn = createBoundTranscludeFn(scope, transcludeFn);
8394 childBoundTranscludeFn = null;
8397 nodeLinkFn(childLinkFn, childScope, node, $rootElement, childBoundTranscludeFn);
8399 } else if (childLinkFn) {
8400 childLinkFn(scope, node.childNodes, undefined, parentBoundTranscludeFn);
8406 function createBoundTranscludeFn(scope, transcludeFn, previousBoundTranscludeFn) {
8407 function boundTranscludeFn(transcludedScope, cloneFn, controllers, futureParentElement, containingScope) {
8409 if (!transcludedScope) {
8410 transcludedScope = scope.$new(false, containingScope);
8411 transcludedScope.$$transcluded = true;
8414 return transcludeFn(transcludedScope, cloneFn, {
8415 parentBoundTranscludeFn: previousBoundTranscludeFn,
8416 transcludeControllers: controllers,
8417 futureParentElement: futureParentElement
8421 // We need to attach the transclusion slots onto the `boundTranscludeFn`
8422 // so that they are available inside the `controllersBoundTransclude` function
8423 var boundSlots = boundTranscludeFn.$$slots = createMap();
8424 for (var slotName in transcludeFn.$$slots) {
8425 if (transcludeFn.$$slots[slotName]) {
8426 boundSlots[slotName] = createBoundTranscludeFn(scope, transcludeFn.$$slots[slotName], previousBoundTranscludeFn);
8428 boundSlots[slotName] = null;
8432 return boundTranscludeFn;
8436 * Looks for directives on the given node and adds them to the directive collection which is
8439 * @param node Node to search.
8440 * @param directives An array to which the directives are added to. This array is sorted before
8441 * the function returns.
8442 * @param attrs The shared attrs object which is used to populate the normalized attributes.
8443 * @param {number=} maxPriority Max directive priority.
8445 function collectDirectives(node, directives, attrs, maxPriority, ignoreDirective) {
8446 var nodeType = node.nodeType,
8447 attrsMap = attrs.$attr,
8452 case NODE_TYPE_ELEMENT: /* Element */
8453 // use the node name: <directive>
8454 addDirective(directives,
8455 directiveNormalize(nodeName_(node)), 'E', maxPriority, ignoreDirective);
8457 // iterate over the attributes
8458 for (var attr, name, nName, ngAttrName, value, isNgAttr, nAttrs = node.attributes,
8459 j = 0, jj = nAttrs && nAttrs.length; j < jj; j++) {
8460 var attrStartName = false;
8461 var attrEndName = false;
8465 value = trim(attr.value);
8467 // support ngAttr attribute binding
8468 ngAttrName = directiveNormalize(name);
8469 if (isNgAttr = NG_ATTR_BINDING.test(ngAttrName)) {
8470 name = name.replace(PREFIX_REGEXP, '')
8471 .substr(8).replace(/_(.)/g, function(match, letter) {
8472 return letter.toUpperCase();
8476 var multiElementMatch = ngAttrName.match(MULTI_ELEMENT_DIR_RE);
8477 if (multiElementMatch && directiveIsMultiElement(multiElementMatch[1])) {
8478 attrStartName = name;
8479 attrEndName = name.substr(0, name.length - 5) + 'end';
8480 name = name.substr(0, name.length - 6);
8483 nName = directiveNormalize(name.toLowerCase());
8484 attrsMap[nName] = name;
8485 if (isNgAttr || !attrs.hasOwnProperty(nName)) {
8486 attrs[nName] = value;
8487 if (getBooleanAttrName(node, nName)) {
8488 attrs[nName] = true; // presence means true
8491 addAttrInterpolateDirective(node, directives, value, nName, isNgAttr);
8492 addDirective(directives, nName, 'A', maxPriority, ignoreDirective, attrStartName,
8496 // use class as directive
8497 className = node.className;
8498 if (isObject(className)) {
8499 // Maybe SVGAnimatedString
8500 className = className.animVal;
8502 if (isString(className) && className !== '') {
8503 while (match = CLASS_DIRECTIVE_REGEXP.exec(className)) {
8504 nName = directiveNormalize(match[2]);
8505 if (addDirective(directives, nName, 'C', maxPriority, ignoreDirective)) {
8506 attrs[nName] = trim(match[3]);
8508 className = className.substr(match.index + match[0].length);
8512 case NODE_TYPE_TEXT: /* Text Node */
8514 // Workaround for #11781
8515 while (node.parentNode && node.nextSibling && node.nextSibling.nodeType === NODE_TYPE_TEXT) {
8516 node.nodeValue = node.nodeValue + node.nextSibling.nodeValue;
8517 node.parentNode.removeChild(node.nextSibling);
8520 addTextInterpolateDirective(directives, node.nodeValue);
8522 case NODE_TYPE_COMMENT: /* Comment */
8524 match = COMMENT_DIRECTIVE_REGEXP.exec(node.nodeValue);
8526 nName = directiveNormalize(match[1]);
8527 if (addDirective(directives, nName, 'M', maxPriority, ignoreDirective)) {
8528 attrs[nName] = trim(match[2]);
8532 // turns out that under some circumstances IE9 throws errors when one attempts to read
8533 // comment's node value.
8534 // Just ignore it and continue. (Can't seem to reproduce in test case.)
8539 directives.sort(byPriority);
8544 * Given a node with an directive-start it collects all of the siblings until it finds
8551 function groupScan(node, attrStart, attrEnd) {
8554 if (attrStart && node.hasAttribute && node.hasAttribute(attrStart)) {
8557 throw $compileMinErr('uterdir',
8558 "Unterminated attribute, found '{0}' but no matching '{1}' found.",
8559 attrStart, attrEnd);
8561 if (node.nodeType == NODE_TYPE_ELEMENT) {
8562 if (node.hasAttribute(attrStart)) depth++;
8563 if (node.hasAttribute(attrEnd)) depth--;
8566 node = node.nextSibling;
8567 } while (depth > 0);
8572 return jqLite(nodes);
8576 * Wrapper for linking function which converts normal linking function into a grouped
8581 * @returns {Function}
8583 function groupElementsLinkFnWrapper(linkFn, attrStart, attrEnd) {
8584 return function groupedElementsLink(scope, element, attrs, controllers, transcludeFn) {
8585 element = groupScan(element[0], attrStart, attrEnd);
8586 return linkFn(scope, element, attrs, controllers, transcludeFn);
8591 * A function generator that is used to support both eager and lazy compilation
8594 * @param $compileNodes
8595 * @param transcludeFn
8596 * @param maxPriority
8597 * @param ignoreDirective
8598 * @param previousCompileContext
8599 * @returns {Function}
8601 function compilationGenerator(eager, $compileNodes, transcludeFn, maxPriority, ignoreDirective, previousCompileContext) {
8605 return compile($compileNodes, transcludeFn, maxPriority, ignoreDirective, previousCompileContext);
8607 return function lazyCompilation() {
8609 compiled = compile($compileNodes, transcludeFn, maxPriority, ignoreDirective, previousCompileContext);
8611 // Null out all of these references in order to make them eligible for garbage collection
8612 // since this is a potentially long lived closure
8613 $compileNodes = transcludeFn = previousCompileContext = null;
8615 return compiled.apply(this, arguments);
8620 * Once the directives have been collected, their compile functions are executed. This method
8621 * is responsible for inlining directive templates as well as terminating the application
8622 * of the directives if the terminal directive has been reached.
8624 * @param {Array} directives Array of collected directives to execute their compile function.
8625 * this needs to be pre-sorted by priority order.
8626 * @param {Node} compileNode The raw DOM node to apply the compile functions to
8627 * @param {Object} templateAttrs The shared attribute function
8628 * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the
8629 * scope argument is auto-generated to the new
8630 * child of the transcluded parent scope.
8631 * @param {JQLite} jqCollection If we are working on the root of the compile tree then this
8632 * argument has the root jqLite array so that we can replace nodes
8634 * @param {Object=} originalReplaceDirective An optional directive that will be ignored when
8635 * compiling the transclusion.
8636 * @param {Array.<Function>} preLinkFns
8637 * @param {Array.<Function>} postLinkFns
8638 * @param {Object} previousCompileContext Context used for previous compilation of the current
8640 * @returns {Function} linkFn
8642 function applyDirectivesToNode(directives, compileNode, templateAttrs, transcludeFn,
8643 jqCollection, originalReplaceDirective, preLinkFns, postLinkFns,
8644 previousCompileContext) {
8645 previousCompileContext = previousCompileContext || {};
8647 var terminalPriority = -Number.MAX_VALUE,
8648 newScopeDirective = previousCompileContext.newScopeDirective,
8649 controllerDirectives = previousCompileContext.controllerDirectives,
8650 newIsolateScopeDirective = previousCompileContext.newIsolateScopeDirective,
8651 templateDirective = previousCompileContext.templateDirective,
8652 nonTlbTranscludeDirective = previousCompileContext.nonTlbTranscludeDirective,
8653 hasTranscludeDirective = false,
8654 hasTemplate = false,
8655 hasElementTranscludeDirective = previousCompileContext.hasElementTranscludeDirective,
8656 $compileNode = templateAttrs.$$element = jqLite(compileNode),
8660 replaceDirective = originalReplaceDirective,
8661 childTranscludeFn = transcludeFn,
8663 didScanForMultipleTransclusion = false,
8664 mightHaveMultipleTransclusionError = false,
8667 // executes all directives on the current element
8668 for (var i = 0, ii = directives.length; i < ii; i++) {
8669 directive = directives[i];
8670 var attrStart = directive.$$start;
8671 var attrEnd = directive.$$end;
8673 // collect multiblock sections
8675 $compileNode = groupScan(compileNode, attrStart, attrEnd);
8677 $template = undefined;
8679 if (terminalPriority > directive.priority) {
8680 break; // prevent further processing of directives
8683 if (directiveValue = directive.scope) {
8685 // skip the check for directives with async templates, we'll check the derived sync
8686 // directive when the template arrives
8687 if (!directive.templateUrl) {
8688 if (isObject(directiveValue)) {
8689 // This directive is trying to add an isolated scope.
8690 // Check that there is no scope of any kind already
8691 assertNoDuplicate('new/isolated scope', newIsolateScopeDirective || newScopeDirective,
8692 directive, $compileNode);
8693 newIsolateScopeDirective = directive;
8695 // This directive is trying to add a child scope.
8696 // Check that there is no isolated scope already
8697 assertNoDuplicate('new/isolated scope', newIsolateScopeDirective, directive,
8702 newScopeDirective = newScopeDirective || directive;
8705 directiveName = directive.name;
8707 // If we encounter a condition that can result in transclusion on the directive,
8708 // then scan ahead in the remaining directives for others that may cause a multiple
8709 // transclusion error to be thrown during the compilation process. If a matching directive
8710 // is found, then we know that when we encounter a transcluded directive, we need to eagerly
8711 // compile the `transclude` function rather than doing it lazily in order to throw
8712 // exceptions at the correct time
8713 if (!didScanForMultipleTransclusion && ((directive.replace && (directive.templateUrl || directive.template))
8714 || (directive.transclude && !directive.$$tlb))) {
8715 var candidateDirective;
8717 for (var scanningIndex = i + 1; candidateDirective = directives[scanningIndex++];) {
8718 if ((candidateDirective.transclude && !candidateDirective.$$tlb)
8719 || (candidateDirective.replace && (candidateDirective.templateUrl || candidateDirective.template))) {
8720 mightHaveMultipleTransclusionError = true;
8725 didScanForMultipleTransclusion = true;
8728 if (!directive.templateUrl && directive.controller) {
8729 directiveValue = directive.controller;
8730 controllerDirectives = controllerDirectives || createMap();
8731 assertNoDuplicate("'" + directiveName + "' controller",
8732 controllerDirectives[directiveName], directive, $compileNode);
8733 controllerDirectives[directiveName] = directive;
8736 if (directiveValue = directive.transclude) {
8737 hasTranscludeDirective = true;
8739 // Special case ngIf and ngRepeat so that we don't complain about duplicate transclusion.
8740 // This option should only be used by directives that know how to safely handle element transclusion,
8741 // where the transcluded nodes are added or replaced after linking.
8742 if (!directive.$$tlb) {
8743 assertNoDuplicate('transclusion', nonTlbTranscludeDirective, directive, $compileNode);
8744 nonTlbTranscludeDirective = directive;
8747 if (directiveValue == 'element') {
8748 hasElementTranscludeDirective = true;
8749 terminalPriority = directive.priority;
8750 $template = $compileNode;
8751 $compileNode = templateAttrs.$$element =
8752 jqLite(compile.$$createComment(directiveName, templateAttrs[directiveName]));
8753 compileNode = $compileNode[0];
8754 replaceWith(jqCollection, sliceArgs($template), compileNode);
8756 // Support: Chrome < 50
8757 // https://github.com/angular/angular.js/issues/14041
8759 // In the versions of V8 prior to Chrome 50, the document fragment that is created
8760 // in the `replaceWith` function is improperly garbage collected despite still
8761 // being referenced by the `parentNode` property of all of the child nodes. By adding
8762 // a reference to the fragment via a different property, we can avoid that incorrect
8764 // TODO: remove this line after Chrome 50 has been released
8765 $template[0].$$parentNode = $template[0].parentNode;
8767 childTranscludeFn = compilationGenerator(mightHaveMultipleTransclusionError, $template, transcludeFn, terminalPriority,
8768 replaceDirective && replaceDirective.name, {
8770 // - controllerDirectives - otherwise we'll create duplicates controllers
8771 // - newIsolateScopeDirective or templateDirective - combining templates with
8772 // element transclusion doesn't make sense.
8774 // We need only nonTlbTranscludeDirective so that we prevent putting transclusion
8775 // on the same element more than once.
8776 nonTlbTranscludeDirective: nonTlbTranscludeDirective
8780 var slots = createMap();
8782 $template = jqLite(jqLiteClone(compileNode)).contents();
8784 if (isObject(directiveValue)) {
8786 // We have transclusion slots,
8787 // collect them up, compile them and store their transclusion functions
8790 var slotMap = createMap();
8791 var filledSlots = createMap();
8793 // Parse the element selectors
8794 forEach(directiveValue, function(elementSelector, slotName) {
8795 // If an element selector starts with a ? then it is optional
8796 var optional = (elementSelector.charAt(0) === '?');
8797 elementSelector = optional ? elementSelector.substring(1) : elementSelector;
8799 slotMap[elementSelector] = slotName;
8801 // We explicitly assign `null` since this implies that a slot was defined but not filled.
8802 // Later when calling boundTransclusion functions with a slot name we only error if the
8803 // slot is `undefined`
8804 slots[slotName] = null;
8806 // filledSlots contains `true` for all slots that are either optional or have been
8807 // filled. This is used to check that we have not missed any required slots
8808 filledSlots[slotName] = optional;
8811 // Add the matching elements into their slot
8812 forEach($compileNode.contents(), function(node) {
8813 var slotName = slotMap[directiveNormalize(nodeName_(node))];
8815 filledSlots[slotName] = true;
8816 slots[slotName] = slots[slotName] || [];
8817 slots[slotName].push(node);
8819 $template.push(node);
8823 // Check for required slots that were not filled
8824 forEach(filledSlots, function(filled, slotName) {
8826 throw $compileMinErr('reqslot', 'Required transclusion slot `{0}` was not filled.', slotName);
8830 for (var slotName in slots) {
8831 if (slots[slotName]) {
8832 // Only define a transclusion function if the slot was filled
8833 slots[slotName] = compilationGenerator(mightHaveMultipleTransclusionError, slots[slotName], transcludeFn);
8838 $compileNode.empty(); // clear contents
8839 childTranscludeFn = compilationGenerator(mightHaveMultipleTransclusionError, $template, transcludeFn, undefined,
8840 undefined, { needsNewScope: directive.$$isolateScope || directive.$$newScope});
8841 childTranscludeFn.$$slots = slots;
8845 if (directive.template) {
8847 assertNoDuplicate('template', templateDirective, directive, $compileNode);
8848 templateDirective = directive;
8850 directiveValue = (isFunction(directive.template))
8851 ? directive.template($compileNode, templateAttrs)
8852 : directive.template;
8854 directiveValue = denormalizeTemplate(directiveValue);
8856 if (directive.replace) {
8857 replaceDirective = directive;
8858 if (jqLiteIsTextNode(directiveValue)) {
8861 $template = removeComments(wrapTemplate(directive.templateNamespace, trim(directiveValue)));
8863 compileNode = $template[0];
8865 if ($template.length != 1 || compileNode.nodeType !== NODE_TYPE_ELEMENT) {
8866 throw $compileMinErr('tplrt',
8867 "Template for directive '{0}' must have exactly one root element. {1}",
8871 replaceWith(jqCollection, $compileNode, compileNode);
8873 var newTemplateAttrs = {$attr: {}};
8875 // combine directives from the original node and from the template:
8876 // - take the array of directives for this element
8877 // - split it into two parts, those that already applied (processed) and those that weren't (unprocessed)
8878 // - collect directives from the template and sort them by priority
8879 // - combine directives as: processed + template + unprocessed
8880 var templateDirectives = collectDirectives(compileNode, [], newTemplateAttrs);
8881 var unprocessedDirectives = directives.splice(i + 1, directives.length - (i + 1));
8883 if (newIsolateScopeDirective || newScopeDirective) {
8884 // The original directive caused the current element to be replaced but this element
8885 // also needs to have a new scope, so we need to tell the template directives
8886 // that they would need to get their scope from further up, if they require transclusion
8887 markDirectiveScope(templateDirectives, newIsolateScopeDirective, newScopeDirective);
8889 directives = directives.concat(templateDirectives).concat(unprocessedDirectives);
8890 mergeTemplateAttributes(templateAttrs, newTemplateAttrs);
8892 ii = directives.length;
8894 $compileNode.html(directiveValue);
8898 if (directive.templateUrl) {
8900 assertNoDuplicate('template', templateDirective, directive, $compileNode);
8901 templateDirective = directive;
8903 if (directive.replace) {
8904 replaceDirective = directive;
8908 nodeLinkFn = compileTemplateUrl(directives.splice(i, directives.length - i), $compileNode,
8910 templateAttrs, jqCollection, hasTranscludeDirective && childTranscludeFn, preLinkFns, postLinkFns, {
8911 controllerDirectives: controllerDirectives,
8912 newScopeDirective: (newScopeDirective !== directive) && newScopeDirective,
8913 newIsolateScopeDirective: newIsolateScopeDirective,
8914 templateDirective: templateDirective,
8915 nonTlbTranscludeDirective: nonTlbTranscludeDirective
8917 ii = directives.length;
8918 } else if (directive.compile) {
8920 linkFn = directive.compile($compileNode, templateAttrs, childTranscludeFn);
8921 if (isFunction(linkFn)) {
8922 addLinkFns(null, linkFn, attrStart, attrEnd);
8923 } else if (linkFn) {
8924 addLinkFns(linkFn.pre, linkFn.post, attrStart, attrEnd);
8927 $exceptionHandler(e, startingTag($compileNode));
8931 if (directive.terminal) {
8932 nodeLinkFn.terminal = true;
8933 terminalPriority = Math.max(terminalPriority, directive.priority);
8938 nodeLinkFn.scope = newScopeDirective && newScopeDirective.scope === true;
8939 nodeLinkFn.transcludeOnThisElement = hasTranscludeDirective;
8940 nodeLinkFn.templateOnThisElement = hasTemplate;
8941 nodeLinkFn.transclude = childTranscludeFn;
8943 previousCompileContext.hasElementTranscludeDirective = hasElementTranscludeDirective;
8945 // might be normal or delayed nodeLinkFn depending on if templateUrl is present
8948 ////////////////////
8950 function addLinkFns(pre, post, attrStart, attrEnd) {
8952 if (attrStart) pre = groupElementsLinkFnWrapper(pre, attrStart, attrEnd);
8953 pre.require = directive.require;
8954 pre.directiveName = directiveName;
8955 if (newIsolateScopeDirective === directive || directive.$$isolateScope) {
8956 pre = cloneAndAnnotateFn(pre, {isolateScope: true});
8958 preLinkFns.push(pre);
8961 if (attrStart) post = groupElementsLinkFnWrapper(post, attrStart, attrEnd);
8962 post.require = directive.require;
8963 post.directiveName = directiveName;
8964 if (newIsolateScopeDirective === directive || directive.$$isolateScope) {
8965 post = cloneAndAnnotateFn(post, {isolateScope: true});
8967 postLinkFns.push(post);
8971 function nodeLinkFn(childLinkFn, scope, linkNode, $rootElement, boundTranscludeFn) {
8972 var i, ii, linkFn, isolateScope, controllerScope, elementControllers, transcludeFn, $element,
8973 attrs, scopeBindingInfo;
8975 if (compileNode === linkNode) {
8976 attrs = templateAttrs;
8977 $element = templateAttrs.$$element;
8979 $element = jqLite(linkNode);
8980 attrs = new Attributes($element, templateAttrs);
8983 controllerScope = scope;
8984 if (newIsolateScopeDirective) {
8985 isolateScope = scope.$new(true);
8986 } else if (newScopeDirective) {
8987 controllerScope = scope.$parent;
8990 if (boundTranscludeFn) {
8991 // track `boundTranscludeFn` so it can be unwrapped if `transcludeFn`
8992 // is later passed as `parentBoundTranscludeFn` to `publicLinkFn`
8993 transcludeFn = controllersBoundTransclude;
8994 transcludeFn.$$boundTransclude = boundTranscludeFn;
8995 // expose the slots on the `$transclude` function
8996 transcludeFn.isSlotFilled = function(slotName) {
8997 return !!boundTranscludeFn.$$slots[slotName];
9001 if (controllerDirectives) {
9002 elementControllers = setupControllers($element, attrs, transcludeFn, controllerDirectives, isolateScope, scope, newIsolateScopeDirective);
9005 if (newIsolateScopeDirective) {
9006 // Initialize isolate scope bindings for new isolate scope directive.
9007 compile.$$addScopeInfo($element, isolateScope, true, !(templateDirective && (templateDirective === newIsolateScopeDirective ||
9008 templateDirective === newIsolateScopeDirective.$$originalDirective)));
9009 compile.$$addScopeClass($element, true);
9010 isolateScope.$$isolateBindings =
9011 newIsolateScopeDirective.$$isolateBindings;
9012 scopeBindingInfo = initializeDirectiveBindings(scope, attrs, isolateScope,
9013 isolateScope.$$isolateBindings,
9014 newIsolateScopeDirective);
9015 if (scopeBindingInfo.removeWatches) {
9016 isolateScope.$on('$destroy', scopeBindingInfo.removeWatches);
9020 // Initialize bindToController bindings
9021 for (var name in elementControllers) {
9022 var controllerDirective = controllerDirectives[name];
9023 var controller = elementControllers[name];
9024 var bindings = controllerDirective.$$bindings.bindToController;
9026 if (controller.identifier && bindings) {
9027 controller.bindingInfo =
9028 initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective);
9030 controller.bindingInfo = {};
9033 var controllerResult = controller();
9034 if (controllerResult !== controller.instance) {
9035 // If the controller constructor has a return value, overwrite the instance
9036 // from setupControllers
9037 controller.instance = controllerResult;
9038 $element.data('$' + controllerDirective.name + 'Controller', controllerResult);
9039 controller.bindingInfo.removeWatches && controller.bindingInfo.removeWatches();
9040 controller.bindingInfo =
9041 initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective);
9045 // Bind the required controllers to the controller, if `require` is an object and `bindToController` is truthy
9046 forEach(controllerDirectives, function(controllerDirective, name) {
9047 var require = controllerDirective.require;
9048 if (controllerDirective.bindToController && !isArray(require) && isObject(require)) {
9049 extend(elementControllers[name].instance, getControllers(name, require, $element, elementControllers));
9053 // Handle the init and destroy lifecycle hooks on all controllers that have them
9054 forEach(elementControllers, function(controller) {
9055 var controllerInstance = controller.instance;
9056 if (isFunction(controllerInstance.$onChanges)) {
9057 controllerInstance.$onChanges(controller.bindingInfo.initialChanges);
9059 if (isFunction(controllerInstance.$onInit)) {
9060 controllerInstance.$onInit();
9062 if (isFunction(controllerInstance.$onDestroy)) {
9063 controllerScope.$on('$destroy', function callOnDestroyHook() {
9064 controllerInstance.$onDestroy();
9070 for (i = 0, ii = preLinkFns.length; i < ii; i++) {
9071 linkFn = preLinkFns[i];
9072 invokeLinkFn(linkFn,
9073 linkFn.isolateScope ? isolateScope : scope,
9076 linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers),
9082 // We only pass the isolate scope, if the isolate directive has a template,
9083 // otherwise the child elements do not belong to the isolate directive.
9084 var scopeToChild = scope;
9085 if (newIsolateScopeDirective && (newIsolateScopeDirective.template || newIsolateScopeDirective.templateUrl === null)) {
9086 scopeToChild = isolateScope;
9088 childLinkFn && childLinkFn(scopeToChild, linkNode.childNodes, undefined, boundTranscludeFn);
9091 for (i = postLinkFns.length - 1; i >= 0; i--) {
9092 linkFn = postLinkFns[i];
9093 invokeLinkFn(linkFn,
9094 linkFn.isolateScope ? isolateScope : scope,
9097 linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers),
9102 // Trigger $postLink lifecycle hooks
9103 forEach(elementControllers, function(controller) {
9104 var controllerInstance = controller.instance;
9105 if (isFunction(controllerInstance.$postLink)) {
9106 controllerInstance.$postLink();
9110 // This is the function that is injected as `$transclude`.
9111 // Note: all arguments are optional!
9112 function controllersBoundTransclude(scope, cloneAttachFn, futureParentElement, slotName) {
9113 var transcludeControllers;
9114 // No scope passed in:
9115 if (!isScope(scope)) {
9116 slotName = futureParentElement;
9117 futureParentElement = cloneAttachFn;
9118 cloneAttachFn = scope;
9122 if (hasElementTranscludeDirective) {
9123 transcludeControllers = elementControllers;
9125 if (!futureParentElement) {
9126 futureParentElement = hasElementTranscludeDirective ? $element.parent() : $element;
9129 // slotTranscludeFn can be one of three things:
9130 // * a transclude function - a filled slot
9131 // * `null` - an optional slot that was not filled
9132 // * `undefined` - a slot that was not declared (i.e. invalid)
9133 var slotTranscludeFn = boundTranscludeFn.$$slots[slotName];
9134 if (slotTranscludeFn) {
9135 return slotTranscludeFn(scope, cloneAttachFn, transcludeControllers, futureParentElement, scopeToChild);
9136 } else if (isUndefined(slotTranscludeFn)) {
9137 throw $compileMinErr('noslot',
9138 'No parent directive that requires a transclusion with slot name "{0}". ' +
9140 slotName, startingTag($element));
9143 return boundTranscludeFn(scope, cloneAttachFn, transcludeControllers, futureParentElement, scopeToChild);
9149 function getControllers(directiveName, require, $element, elementControllers) {
9152 if (isString(require)) {
9153 var match = require.match(REQUIRE_PREFIX_REGEXP);
9154 var name = require.substring(match[0].length);
9155 var inheritType = match[1] || match[3];
9156 var optional = match[2] === '?';
9158 //If only parents then start at the parent element
9159 if (inheritType === '^^') {
9160 $element = $element.parent();
9161 //Otherwise attempt getting the controller from elementControllers in case
9162 //the element is transcluded (and has no data) and to avoid .data if possible
9164 value = elementControllers && elementControllers[name];
9165 value = value && value.instance;
9169 var dataName = '$' + name + 'Controller';
9170 value = inheritType ? $element.inheritedData(dataName) : $element.data(dataName);
9173 if (!value && !optional) {
9174 throw $compileMinErr('ctreq',
9175 "Controller '{0}', required by directive '{1}', can't be found!",
9176 name, directiveName);
9178 } else if (isArray(require)) {
9180 for (var i = 0, ii = require.length; i < ii; i++) {
9181 value[i] = getControllers(directiveName, require[i], $element, elementControllers);
9183 } else if (isObject(require)) {
9185 forEach(require, function(controller, property) {
9186 value[property] = getControllers(directiveName, controller, $element, elementControllers);
9190 return value || null;
9193 function setupControllers($element, attrs, transcludeFn, controllerDirectives, isolateScope, scope, newIsolateScopeDirective) {
9194 var elementControllers = createMap();
9195 for (var controllerKey in controllerDirectives) {
9196 var directive = controllerDirectives[controllerKey];
9198 $scope: directive === newIsolateScopeDirective || directive.$$isolateScope ? isolateScope : scope,
9201 $transclude: transcludeFn
9204 var controller = directive.controller;
9205 if (controller == '@') {
9206 controller = attrs[directive.name];
9209 var controllerInstance = $controller(controller, locals, true, directive.controllerAs);
9211 // For directives with element transclusion the element is a comment.
9212 // In this case .data will not attach any data.
9213 // Instead, we save the controllers for the element in a local hash and attach to .data
9214 // later, once we have the actual element.
9215 elementControllers[directive.name] = controllerInstance;
9216 $element.data('$' + directive.name + 'Controller', controllerInstance.instance);
9218 return elementControllers;
9221 // Depending upon the context in which a directive finds itself it might need to have a new isolated
9222 // or child scope created. For instance:
9223 // * if the directive has been pulled into a template because another directive with a higher priority
9224 // asked for element transclusion
9225 // * if the directive itself asks for transclusion but it is at the root of a template and the original
9226 // element was replaced. See https://github.com/angular/angular.js/issues/12936
9227 function markDirectiveScope(directives, isolateScope, newScope) {
9228 for (var j = 0, jj = directives.length; j < jj; j++) {
9229 directives[j] = inherit(directives[j], {$$isolateScope: isolateScope, $$newScope: newScope});
9234 * looks up the directive and decorates it with exception handling and proper parameters. We
9235 * call this the boundDirective.
9237 * @param {string} name name of the directive to look up.
9238 * @param {string} location The directive must be found in specific format.
9239 * String containing any of theses characters:
9241 * * `E`: element name
9245 * @returns {boolean} true if directive was added.
9247 function addDirective(tDirectives, name, location, maxPriority, ignoreDirective, startAttrName,
9249 if (name === ignoreDirective) return null;
9251 if (hasDirectives.hasOwnProperty(name)) {
9252 for (var directive, directives = $injector.get(name + Suffix),
9253 i = 0, ii = directives.length; i < ii; i++) {
9255 directive = directives[i];
9256 if ((isUndefined(maxPriority) || maxPriority > directive.priority) &&
9257 directive.restrict.indexOf(location) != -1) {
9258 if (startAttrName) {
9259 directive = inherit(directive, {$$start: startAttrName, $$end: endAttrName});
9261 if (!directive.$$bindings) {
9262 var bindings = directive.$$bindings =
9263 parseDirectiveBindings(directive, directive.name);
9264 if (isObject(bindings.isolateScope)) {
9265 directive.$$isolateBindings = bindings.isolateScope;
9268 tDirectives.push(directive);
9271 } catch (e) { $exceptionHandler(e); }
9279 * looks up the directive and returns true if it is a multi-element directive,
9280 * and therefore requires DOM nodes between -start and -end markers to be grouped
9283 * @param {string} name name of the directive to look up.
9284 * @returns true if directive was registered as multi-element.
9286 function directiveIsMultiElement(name) {
9287 if (hasDirectives.hasOwnProperty(name)) {
9288 for (var directive, directives = $injector.get(name + Suffix),
9289 i = 0, ii = directives.length; i < ii; i++) {
9290 directive = directives[i];
9291 if (directive.multiElement) {
9300 * When the element is replaced with HTML template then the new attributes
9301 * on the template need to be merged with the existing attributes in the DOM.
9302 * The desired effect is to have both of the attributes present.
9304 * @param {object} dst destination attributes (original DOM)
9305 * @param {object} src source attributes (from the directive template)
9307 function mergeTemplateAttributes(dst, src) {
9308 var srcAttr = src.$attr,
9309 dstAttr = dst.$attr,
9310 $element = dst.$$element;
9312 // reapply the old attributes to the new element
9313 forEach(dst, function(value, key) {
9314 if (key.charAt(0) != '$') {
9315 if (src[key] && src[key] !== value) {
9316 value += (key === 'style' ? ';' : ' ') + src[key];
9318 dst.$set(key, value, true, srcAttr[key]);
9322 // copy the new attributes on the old attrs object
9323 forEach(src, function(value, key) {
9324 if (key == 'class') {
9325 safeAddClass($element, value);
9326 dst['class'] = (dst['class'] ? dst['class'] + ' ' : '') + value;
9327 } else if (key == 'style') {
9328 $element.attr('style', $element.attr('style') + ';' + value);
9329 dst['style'] = (dst['style'] ? dst['style'] + ';' : '') + value;
9330 // `dst` will never contain hasOwnProperty as DOM parser won't let it.
9331 // You will get an "InvalidCharacterError: DOM Exception 5" error if you
9332 // have an attribute like "has-own-property" or "data-has-own-property", etc.
9333 } else if (key.charAt(0) != '$' && !dst.hasOwnProperty(key)) {
9335 dstAttr[key] = srcAttr[key];
9341 function compileTemplateUrl(directives, $compileNode, tAttrs,
9342 $rootElement, childTranscludeFn, preLinkFns, postLinkFns, previousCompileContext) {
9344 afterTemplateNodeLinkFn,
9345 afterTemplateChildLinkFn,
9346 beforeTemplateCompileNode = $compileNode[0],
9347 origAsyncDirective = directives.shift(),
9348 derivedSyncDirective = inherit(origAsyncDirective, {
9349 templateUrl: null, transclude: null, replace: null, $$originalDirective: origAsyncDirective
9351 templateUrl = (isFunction(origAsyncDirective.templateUrl))
9352 ? origAsyncDirective.templateUrl($compileNode, tAttrs)
9353 : origAsyncDirective.templateUrl,
9354 templateNamespace = origAsyncDirective.templateNamespace;
9356 $compileNode.empty();
9358 $templateRequest(templateUrl)
9359 .then(function(content) {
9360 var compileNode, tempTemplateAttrs, $template, childBoundTranscludeFn;
9362 content = denormalizeTemplate(content);
9364 if (origAsyncDirective.replace) {
9365 if (jqLiteIsTextNode(content)) {
9368 $template = removeComments(wrapTemplate(templateNamespace, trim(content)));
9370 compileNode = $template[0];
9372 if ($template.length != 1 || compileNode.nodeType !== NODE_TYPE_ELEMENT) {
9373 throw $compileMinErr('tplrt',
9374 "Template for directive '{0}' must have exactly one root element. {1}",
9375 origAsyncDirective.name, templateUrl);
9378 tempTemplateAttrs = {$attr: {}};
9379 replaceWith($rootElement, $compileNode, compileNode);
9380 var templateDirectives = collectDirectives(compileNode, [], tempTemplateAttrs);
9382 if (isObject(origAsyncDirective.scope)) {
9383 // the original directive that caused the template to be loaded async required
9385 markDirectiveScope(templateDirectives, true);
9387 directives = templateDirectives.concat(directives);
9388 mergeTemplateAttributes(tAttrs, tempTemplateAttrs);
9390 compileNode = beforeTemplateCompileNode;
9391 $compileNode.html(content);
9394 directives.unshift(derivedSyncDirective);
9396 afterTemplateNodeLinkFn = applyDirectivesToNode(directives, compileNode, tAttrs,
9397 childTranscludeFn, $compileNode, origAsyncDirective, preLinkFns, postLinkFns,
9398 previousCompileContext);
9399 forEach($rootElement, function(node, i) {
9400 if (node == compileNode) {
9401 $rootElement[i] = $compileNode[0];
9404 afterTemplateChildLinkFn = compileNodes($compileNode[0].childNodes, childTranscludeFn);
9406 while (linkQueue.length) {
9407 var scope = linkQueue.shift(),
9408 beforeTemplateLinkNode = linkQueue.shift(),
9409 linkRootElement = linkQueue.shift(),
9410 boundTranscludeFn = linkQueue.shift(),
9411 linkNode = $compileNode[0];
9413 if (scope.$$destroyed) continue;
9415 if (beforeTemplateLinkNode !== beforeTemplateCompileNode) {
9416 var oldClasses = beforeTemplateLinkNode.className;
9418 if (!(previousCompileContext.hasElementTranscludeDirective &&
9419 origAsyncDirective.replace)) {
9420 // it was cloned therefore we have to clone as well.
9421 linkNode = jqLiteClone(compileNode);
9423 replaceWith(linkRootElement, jqLite(beforeTemplateLinkNode), linkNode);
9425 // Copy in CSS classes from original node
9426 safeAddClass(jqLite(linkNode), oldClasses);
9428 if (afterTemplateNodeLinkFn.transcludeOnThisElement) {
9429 childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn);
9431 childBoundTranscludeFn = boundTranscludeFn;
9433 afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, linkNode, $rootElement,
9434 childBoundTranscludeFn);
9439 return function delayedNodeLinkFn(ignoreChildLinkFn, scope, node, rootElement, boundTranscludeFn) {
9440 var childBoundTranscludeFn = boundTranscludeFn;
9441 if (scope.$$destroyed) return;
9443 linkQueue.push(scope,
9446 childBoundTranscludeFn);
9448 if (afterTemplateNodeLinkFn.transcludeOnThisElement) {
9449 childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn);
9451 afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, node, rootElement, childBoundTranscludeFn);
9458 * Sorting function for bound directives.
9460 function byPriority(a, b) {
9461 var diff = b.priority - a.priority;
9462 if (diff !== 0) return diff;
9463 if (a.name !== b.name) return (a.name < b.name) ? -1 : 1;
9464 return a.index - b.index;
9467 function assertNoDuplicate(what, previousDirective, directive, element) {
9469 function wrapModuleNameIfDefined(moduleName) {
9471 (' (module: ' + moduleName + ')') :
9475 if (previousDirective) {
9476 throw $compileMinErr('multidir', 'Multiple directives [{0}{1}, {2}{3}] asking for {4} on: {5}',
9477 previousDirective.name, wrapModuleNameIfDefined(previousDirective.$$moduleName),
9478 directive.name, wrapModuleNameIfDefined(directive.$$moduleName), what, startingTag(element));
9483 function addTextInterpolateDirective(directives, text) {
9484 var interpolateFn = $interpolate(text, true);
9485 if (interpolateFn) {
9488 compile: function textInterpolateCompileFn(templateNode) {
9489 var templateNodeParent = templateNode.parent(),
9490 hasCompileParent = !!templateNodeParent.length;
9492 // When transcluding a template that has bindings in the root
9493 // we don't have a parent and thus need to add the class during linking fn.
9494 if (hasCompileParent) compile.$$addBindingClass(templateNodeParent);
9496 return function textInterpolateLinkFn(scope, node) {
9497 var parent = node.parent();
9498 if (!hasCompileParent) compile.$$addBindingClass(parent);
9499 compile.$$addBindingInfo(parent, interpolateFn.expressions);
9500 scope.$watch(interpolateFn, function interpolateFnWatchAction(value) {
9501 node[0].nodeValue = value;
9510 function wrapTemplate(type, template) {
9511 type = lowercase(type || 'html');
9515 var wrapper = window.document.createElement('div');
9516 wrapper.innerHTML = '<' + type + '>' + template + '</' + type + '>';
9517 return wrapper.childNodes[0].childNodes;
9524 function getTrustedContext(node, attrNormalizedName) {
9525 if (attrNormalizedName == "srcdoc") {
9528 var tag = nodeName_(node);
9529 // maction[xlink:href] can source SVG. It's not limited to <maction>.
9530 if (attrNormalizedName == "xlinkHref" ||
9531 (tag == "form" && attrNormalizedName == "action") ||
9532 (tag != "img" && (attrNormalizedName == "src" ||
9533 attrNormalizedName == "ngSrc"))) {
9534 return $sce.RESOURCE_URL;
9539 function addAttrInterpolateDirective(node, directives, value, name, allOrNothing) {
9540 var trustedContext = getTrustedContext(node, name);
9541 allOrNothing = ALL_OR_NOTHING_ATTRS[name] || allOrNothing;
9543 var interpolateFn = $interpolate(value, true, trustedContext, allOrNothing);
9545 // no interpolation found -> ignore
9546 if (!interpolateFn) return;
9549 if (name === "multiple" && nodeName_(node) === "select") {
9550 throw $compileMinErr("selmulti",
9551 "Binding to the 'multiple' attribute is not supported. Element: {0}",
9557 compile: function() {
9559 pre: function attrInterpolatePreLinkFn(scope, element, attr) {
9560 var $$observers = (attr.$$observers || (attr.$$observers = createMap()));
9562 if (EVENT_HANDLER_ATTR_REGEXP.test(name)) {
9563 throw $compileMinErr('nodomevents',
9564 "Interpolations for HTML DOM event attributes are disallowed. Please use the " +
9565 "ng- versions (such as ng-click instead of onclick) instead.");
9568 // If the attribute has changed since last $interpolate()ed
9569 var newValue = attr[name];
9570 if (newValue !== value) {
9571 // we need to interpolate again since the attribute value has been updated
9572 // (e.g. by another directive's compile function)
9573 // ensure unset/empty values make interpolateFn falsy
9574 interpolateFn = newValue && $interpolate(newValue, true, trustedContext, allOrNothing);
9578 // if attribute was updated so that there is no interpolation going on we don't want to
9579 // register any observers
9580 if (!interpolateFn) return;
9582 // initialize attr object so that it's ready in case we need the value for isolate
9583 // scope initialization, otherwise the value would not be available from isolate
9584 // directive's linking fn during linking phase
9585 attr[name] = interpolateFn(scope);
9587 ($$observers[name] || ($$observers[name] = [])).$$inter = true;
9588 (attr.$$observers && attr.$$observers[name].$$scope || scope).
9589 $watch(interpolateFn, function interpolateFnWatchAction(newValue, oldValue) {
9590 //special case for class attribute addition + removal
9591 //so that class changes can tap into the animation
9592 //hooks provided by the $animate service. Be sure to
9593 //skip animations when the first digest occurs (when
9594 //both the new and the old values are the same) since
9595 //the CSS classes are the non-interpolated values
9596 if (name === 'class' && newValue != oldValue) {
9597 attr.$updateClass(newValue, oldValue);
9599 attr.$set(name, newValue);
9610 * This is a special jqLite.replaceWith, which can replace items which
9611 * have no parents, provided that the containing jqLite collection is provided.
9613 * @param {JqLite=} $rootElement The root of the compile tree. Used so that we can replace nodes
9614 * in the root of the tree.
9615 * @param {JqLite} elementsToRemove The jqLite element which we are going to replace. We keep
9616 * the shell, but replace its DOM node reference.
9617 * @param {Node} newNode The new DOM node.
9619 function replaceWith($rootElement, elementsToRemove, newNode) {
9620 var firstElementToRemove = elementsToRemove[0],
9621 removeCount = elementsToRemove.length,
9622 parent = firstElementToRemove.parentNode,
9626 for (i = 0, ii = $rootElement.length; i < ii; i++) {
9627 if ($rootElement[i] == firstElementToRemove) {
9628 $rootElement[i++] = newNode;
9629 for (var j = i, j2 = j + removeCount - 1,
9630 jj = $rootElement.length;
9631 j < jj; j++, j2++) {
9633 $rootElement[j] = $rootElement[j2];
9635 delete $rootElement[j];
9638 $rootElement.length -= removeCount - 1;
9640 // If the replaced element is also the jQuery .context then replace it
9641 // .context is a deprecated jQuery api, so we should set it only when jQuery set it
9642 // http://api.jquery.com/context/
9643 if ($rootElement.context === firstElementToRemove) {
9644 $rootElement.context = newNode;
9652 parent.replaceChild(newNode, firstElementToRemove);
9655 // Append all the `elementsToRemove` to a fragment. This will...
9656 // - remove them from the DOM
9657 // - allow them to still be traversed with .nextSibling
9658 // - allow a single fragment.qSA to fetch all elements being removed
9659 var fragment = window.document.createDocumentFragment();
9660 for (i = 0; i < removeCount; i++) {
9661 fragment.appendChild(elementsToRemove[i]);
9664 if (jqLite.hasData(firstElementToRemove)) {
9665 // Copy over user data (that includes Angular's $scope etc.). Don't copy private
9666 // data here because there's no public interface in jQuery to do that and copying over
9667 // event listeners (which is the main use of private data) wouldn't work anyway.
9668 jqLite.data(newNode, jqLite.data(firstElementToRemove));
9670 // Remove $destroy event listeners from `firstElementToRemove`
9671 jqLite(firstElementToRemove).off('$destroy');
9674 // Cleanup any data/listeners on the elements and children.
9675 // This includes invoking the $destroy event on any elements with listeners.
9676 jqLite.cleanData(fragment.querySelectorAll('*'));
9678 // Update the jqLite collection to only contain the `newNode`
9679 for (i = 1; i < removeCount; i++) {
9680 delete elementsToRemove[i];
9682 elementsToRemove[0] = newNode;
9683 elementsToRemove.length = 1;
9687 function cloneAndAnnotateFn(fn, annotation) {
9688 return extend(function() { return fn.apply(null, arguments); }, fn, annotation);
9692 function invokeLinkFn(linkFn, scope, $element, attrs, controllers, transcludeFn) {
9694 linkFn(scope, $element, attrs, controllers, transcludeFn);
9696 $exceptionHandler(e, startingTag($element));
9701 // Set up $watches for isolate scope and controller bindings. This process
9702 // only occurs for isolate scopes and new scopes with controllerAs.
9703 function initializeDirectiveBindings(scope, attrs, destination, bindings, directive) {
9704 var removeWatchCollection = [];
9705 var initialChanges = {};
9707 forEach(bindings, function initializeBinding(definition, scopeName) {
9708 var attrName = definition.attrName,
9709 optional = definition.optional,
9710 mode = definition.mode, // @, =, or &
9712 parentGet, parentSet, compare, removeWatch;
9717 if (!optional && !hasOwnProperty.call(attrs, attrName)) {
9718 destination[scopeName] = attrs[attrName] = void 0;
9720 attrs.$observe(attrName, function(value) {
9721 if (isString(value) || isBoolean(value)) {
9722 var oldValue = destination[scopeName];
9723 recordChanges(scopeName, value, oldValue);
9724 destination[scopeName] = value;
9727 attrs.$$observers[attrName].$$scope = scope;
9728 lastValue = attrs[attrName];
9729 if (isString(lastValue)) {
9730 // If the attribute has been provided then we trigger an interpolation to ensure
9731 // the value is there for use in the link fn
9732 destination[scopeName] = $interpolate(lastValue)(scope);
9733 } else if (isBoolean(lastValue)) {
9734 // If the attributes is one of the BOOLEAN_ATTR then Angular will have converted
9735 // the value to boolean rather than a string, so we special case this situation
9736 destination[scopeName] = lastValue;
9738 initialChanges[scopeName] = new SimpleChange(_UNINITIALIZED_VALUE, destination[scopeName]);
9742 if (!hasOwnProperty.call(attrs, attrName)) {
9743 if (optional) break;
9744 attrs[attrName] = void 0;
9746 if (optional && !attrs[attrName]) break;
9748 parentGet = $parse(attrs[attrName]);
9749 if (parentGet.literal) {
9752 compare = function simpleCompare(a, b) { return a === b || (a !== a && b !== b); };
9754 parentSet = parentGet.assign || function() {
9755 // reset the change, or we will throw this exception on every $digest
9756 lastValue = destination[scopeName] = parentGet(scope);
9757 throw $compileMinErr('nonassign',
9758 "Expression '{0}' in attribute '{1}' used with directive '{2}' is non-assignable!",
9759 attrs[attrName], attrName, directive.name);
9761 lastValue = destination[scopeName] = parentGet(scope);
9762 var parentValueWatch = function parentValueWatch(parentValue) {
9763 if (!compare(parentValue, destination[scopeName])) {
9764 // we are out of sync and need to copy
9765 if (!compare(parentValue, lastValue)) {
9766 // parent changed and it has precedence
9767 destination[scopeName] = parentValue;
9769 // if the parent can be assigned then do so
9770 parentSet(scope, parentValue = destination[scopeName]);
9773 return lastValue = parentValue;
9775 parentValueWatch.$stateful = true;
9776 if (definition.collection) {
9777 removeWatch = scope.$watchCollection(attrs[attrName], parentValueWatch);
9779 removeWatch = scope.$watch($parse(attrs[attrName], parentValueWatch), null, parentGet.literal);
9781 removeWatchCollection.push(removeWatch);
9785 if (!hasOwnProperty.call(attrs, attrName)) {
9786 if (optional) break;
9787 attrs[attrName] = void 0;
9789 if (optional && !attrs[attrName]) break;
9791 parentGet = $parse(attrs[attrName]);
9793 destination[scopeName] = parentGet(scope);
9794 initialChanges[scopeName] = new SimpleChange(_UNINITIALIZED_VALUE, destination[scopeName]);
9796 removeWatch = scope.$watch(parentGet, function parentValueWatchAction(newValue, oldValue) {
9797 if (newValue === oldValue) {
9798 // If the new and old values are identical then this is the first time the watch has been triggered
9799 // So instead we use the current value on the destination as the old value
9800 oldValue = destination[scopeName];
9802 recordChanges(scopeName, newValue, oldValue);
9803 destination[scopeName] = newValue;
9804 }, parentGet.literal);
9806 removeWatchCollection.push(removeWatch);
9810 // Don't assign Object.prototype method to scope
9811 parentGet = attrs.hasOwnProperty(attrName) ? $parse(attrs[attrName]) : noop;
9813 // Don't assign noop to destination if expression is not valid
9814 if (parentGet === noop && optional) break;
9816 destination[scopeName] = function(locals) {
9817 return parentGet(scope, locals);
9823 function recordChanges(key, currentValue, previousValue) {
9824 if (isFunction(destination.$onChanges) && currentValue !== previousValue) {
9825 // If we have not already scheduled the top level onChangesQueue handler then do so now
9826 if (!onChangesQueue) {
9827 scope.$$postDigest(flushOnChangesQueue);
9828 onChangesQueue = [];
9830 // If we have not already queued a trigger of onChanges for this controller then do so now
9833 onChangesQueue.push(triggerOnChangesHook);
9835 // If the has been a change on this property already then we need to reuse the previous value
9837 previousValue = changes[key].previousValue;
9839 // Store this change
9840 changes[key] = new SimpleChange(previousValue, currentValue);
9844 function triggerOnChangesHook() {
9845 destination.$onChanges(changes);
9846 // Now clear the changes so that we schedule onChanges when more changes arrive
9847 changes = undefined;
9851 initialChanges: initialChanges,
9852 removeWatches: removeWatchCollection.length && function removeWatches() {
9853 for (var i = 0, ii = removeWatchCollection.length; i < ii; ++i) {
9854 removeWatchCollection[i]();
9862 function SimpleChange(previous, current) {
9863 this.previousValue = previous;
9864 this.currentValue = current;
9866 SimpleChange.prototype.isFirstChange = function() { return this.previousValue === _UNINITIALIZED_VALUE; };
9869 var PREFIX_REGEXP = /^((?:x|data)[\:\-_])/i;
9871 * Converts all accepted directives format into proper directive name.
9872 * @param name Name to normalize
9874 function directiveNormalize(name) {
9875 return camelCase(name.replace(PREFIX_REGEXP, ''));
9880 * @name $compile.directive.Attributes
9883 * A shared object between directive compile / linking functions which contains normalized DOM
9884 * element attributes. The values reflect current binding state `{{ }}`. The normalization is
9885 * needed since all of these are treated as equivalent in Angular:
9888 * <span ng:bind="a" ng-bind="a" data-ng-bind="a" x-ng-bind="a">
9894 * @name $compile.directive.Attributes#$attr
9897 * A map of DOM element attribute names to the normalized name. This is
9898 * needed to do reverse lookup from normalized name back to actual name.
9904 * @name $compile.directive.Attributes#$set
9908 * Set DOM element attribute value.
9911 * @param {string} name Normalized element attribute name of the property to modify. The name is
9912 * reverse-translated using the {@link ng.$compile.directive.Attributes#$attr $attr}
9913 * property to the original name.
9914 * @param {string} value Value to set the attribute to. The value can be an interpolated string.
9920 * Closure compiler type information
9923 function nodesetLinkingFn(
9924 /* angular.Scope */ scope,
9925 /* NodeList */ nodeList,
9926 /* Element */ rootElement,
9927 /* function(Function) */ boundTranscludeFn
9930 function directiveLinkingFn(
9931 /* nodesetLinkingFn */ nodesetLinkingFn,
9932 /* angular.Scope */ scope,
9934 /* Element */ rootElement,
9935 /* function(Function) */ boundTranscludeFn
9938 function tokenDifference(str1, str2) {
9940 tokens1 = str1.split(/\s+/),
9941 tokens2 = str2.split(/\s+/);
9944 for (var i = 0; i < tokens1.length; i++) {
9945 var token = tokens1[i];
9946 for (var j = 0; j < tokens2.length; j++) {
9947 if (token == tokens2[j]) continue outer;
9949 values += (values.length > 0 ? ' ' : '') + token;
9954 function removeComments(jqNodes) {
9955 jqNodes = jqLite(jqNodes);
9956 var i = jqNodes.length;
9963 var node = jqNodes[i];
9964 if (node.nodeType === NODE_TYPE_COMMENT) {
9965 splice.call(jqNodes, i, 1);
9971 var $controllerMinErr = minErr('$controller');
9974 var CNTRL_REG = /^(\S+)(\s+as\s+([\w$]+))?$/;
9975 function identifierForController(controller, ident) {
9976 if (ident && isString(ident)) return ident;
9977 if (isString(controller)) {
9978 var match = CNTRL_REG.exec(controller);
9979 if (match) return match[3];
9986 * @name $controllerProvider
9988 * The {@link ng.$controller $controller service} is used by Angular to create new
9991 * This provider allows controller registration via the
9992 * {@link ng.$controllerProvider#register register} method.
9994 function $ControllerProvider() {
9995 var controllers = {},
10000 * @name $controllerProvider#has
10001 * @param {string} name Controller name to check.
10003 this.has = function(name) {
10004 return controllers.hasOwnProperty(name);
10009 * @name $controllerProvider#register
10010 * @param {string|Object} name Controller name, or an object map of controllers where the keys are
10011 * the names and the values are the constructors.
10012 * @param {Function|Array} constructor Controller constructor fn (optionally decorated with DI
10013 * annotations in the array notation).
10015 this.register = function(name, constructor) {
10016 assertNotHasOwnProperty(name, 'controller');
10017 if (isObject(name)) {
10018 extend(controllers, name);
10020 controllers[name] = constructor;
10026 * @name $controllerProvider#allowGlobals
10027 * @description If called, allows `$controller` to find controller constructors on `window`
10029 this.allowGlobals = function() {
10034 this.$get = ['$injector', '$window', function($injector, $window) {
10038 * @name $controller
10039 * @requires $injector
10041 * @param {Function|string} constructor If called with a function then it's considered to be the
10042 * controller constructor function. Otherwise it's considered to be a string which is used
10043 * to retrieve the controller constructor using the following steps:
10045 * * check if a controller with given name is registered via `$controllerProvider`
10046 * * check if evaluating the string on the current scope returns a constructor
10047 * * if $controllerProvider#allowGlobals, check `window[constructor]` on the global
10048 * `window` object (not recommended)
10050 * The string can use the `controller as property` syntax, where the controller instance is published
10051 * as the specified property on the `scope`; the `scope` must be injected into `locals` param for this
10052 * to work correctly.
10054 * @param {Object} locals Injection locals for Controller.
10055 * @return {Object} Instance of given controller.
10058 * `$controller` service is responsible for instantiating controllers.
10060 * It's just a simple call to {@link auto.$injector $injector}, but extracted into
10061 * a service, so that one can override this service with [BC version](https://gist.github.com/1649788).
10063 return function $controller(expression, locals, later, ident) {
10065 // param `later` --- indicates that the controller's constructor is invoked at a later time.
10066 // If true, $controller will allocate the object with the correct
10067 // prototype chain, but will not invoke the controller until a returned
10068 // callback is invoked.
10069 // param `ident` --- An optional label which overrides the label parsed from the controller
10070 // expression, if any.
10071 var instance, match, constructor, identifier;
10072 later = later === true;
10073 if (ident && isString(ident)) {
10074 identifier = ident;
10077 if (isString(expression)) {
10078 match = expression.match(CNTRL_REG);
10080 throw $controllerMinErr('ctrlfmt',
10081 "Badly formed controller string '{0}'. " +
10082 "Must match `__name__ as __id__` or `__name__`.", expression);
10084 constructor = match[1],
10085 identifier = identifier || match[3];
10086 expression = controllers.hasOwnProperty(constructor)
10087 ? controllers[constructor]
10088 : getter(locals.$scope, constructor, true) ||
10089 (globals ? getter($window, constructor, true) : undefined);
10091 assertArgFn(expression, constructor, true);
10095 // Instantiate controller later:
10096 // This machinery is used to create an instance of the object before calling the
10097 // controller's constructor itself.
10099 // This allows properties to be added to the controller before the constructor is
10100 // invoked. Primarily, this is used for isolate scope bindings in $compile.
10102 // This feature is not intended for use by applications, and is thus not documented
10104 // Object creation: http://jsperf.com/create-constructor/2
10105 var controllerPrototype = (isArray(expression) ?
10106 expression[expression.length - 1] : expression).prototype;
10107 instance = Object.create(controllerPrototype || null);
10110 addIdentifier(locals, identifier, instance, constructor || expression.name);
10114 return instantiate = extend(function $controllerInit() {
10115 var result = $injector.invoke(expression, instance, locals, constructor);
10116 if (result !== instance && (isObject(result) || isFunction(result))) {
10119 // If result changed, re-assign controllerAs value to scope.
10120 addIdentifier(locals, identifier, instance, constructor || expression.name);
10125 instance: instance,
10126 identifier: identifier
10130 instance = $injector.instantiate(expression, locals, constructor);
10133 addIdentifier(locals, identifier, instance, constructor || expression.name);
10139 function addIdentifier(locals, identifier, instance, name) {
10140 if (!(locals && isObject(locals.$scope))) {
10141 throw minErr('$controller')('noscp',
10142 "Cannot export controller '{0}' as '{1}'! No $scope object provided via `locals`.",
10146 locals.$scope[identifier] = instance;
10154 * @requires $window
10157 * A {@link angular.element jQuery or jqLite} wrapper for the browser's `window.document` object.
10160 <example module="documentExample">
10161 <file name="index.html">
10162 <div ng-controller="ExampleController">
10163 <p>$document title: <b ng-bind="title"></b></p>
10164 <p>window.document title: <b ng-bind="windowTitle"></b></p>
10167 <file name="script.js">
10168 angular.module('documentExample', [])
10169 .controller('ExampleController', ['$scope', '$document', function($scope, $document) {
10170 $scope.title = $document[0].title;
10171 $scope.windowTitle = angular.element(window.document)[0].title;
10176 function $DocumentProvider() {
10177 this.$get = ['$window', function(window) {
10178 return jqLite(window.document);
10184 * @name $exceptionHandler
10185 * @requires ng.$log
10188 * Any uncaught exception in angular expressions is delegated to this service.
10189 * The default implementation simply delegates to `$log.error` which logs it into
10190 * the browser console.
10192 * In unit tests, if `angular-mocks.js` is loaded, this service is overridden by
10193 * {@link ngMock.$exceptionHandler mock $exceptionHandler} which aids in testing.
10198 * angular.module('exceptionOverride', []).factory('$exceptionHandler', function() {
10199 * return function(exception, cause) {
10200 * exception.message += ' (caused by "' + cause + '")';
10206 * This example will override the normal action of `$exceptionHandler`, to make angular
10207 * exceptions fail hard when they happen, instead of just logging to the console.
10210 * Note, that code executed in event-listeners (even those registered using jqLite's `on`/`bind`
10211 * methods) does not delegate exceptions to the {@link ng.$exceptionHandler $exceptionHandler}
10212 * (unless executed during a digest).
10214 * If you wish, you can manually delegate exceptions, e.g.
10215 * `try { ... } catch(e) { $exceptionHandler(e); }`
10217 * @param {Error} exception Exception associated with the error.
10218 * @param {string=} cause optional information about the context in which
10219 * the error was thrown.
10222 function $ExceptionHandlerProvider() {
10223 this.$get = ['$log', function($log) {
10224 return function(exception, cause) {
10225 $log.error.apply($log, arguments);
10230 var $$ForceReflowProvider = function() {
10231 this.$get = ['$document', function($document) {
10232 return function(domNode) {
10233 //the line below will force the browser to perform a repaint so
10234 //that all the animated elements within the animation frame will
10235 //be properly updated and drawn on screen. This is required to
10236 //ensure that the preparation animation is properly flushed so that
10237 //the active state picks up from there. DO NOT REMOVE THIS LINE.
10238 //DO NOT OPTIMIZE THIS LINE. THE MINIFIER WILL REMOVE IT OTHERWISE WHICH
10239 //WILL RESULT IN AN UNPREDICTABLE BUG THAT IS VERY HARD TO TRACK DOWN AND
10240 //WILL TAKE YEARS AWAY FROM YOUR LIFE.
10242 if (!domNode.nodeType && domNode instanceof jqLite) {
10243 domNode = domNode[0];
10246 domNode = $document[0].body;
10248 return domNode.offsetWidth + 1;
10253 var APPLICATION_JSON = 'application/json';
10254 var CONTENT_TYPE_APPLICATION_JSON = {'Content-Type': APPLICATION_JSON + ';charset=utf-8'};
10255 var JSON_START = /^\[|^\{(?!\{)/;
10260 var JSON_PROTECTION_PREFIX = /^\)\]\}',?\n/;
10261 var $httpMinErr = minErr('$http');
10262 var $httpMinErrLegacyFn = function(method) {
10263 return function() {
10264 throw $httpMinErr('legacy', 'The method `{0}` on the promise returned from `$http` has been disabled.', method);
10268 function serializeValue(v) {
10270 return isDate(v) ? v.toISOString() : toJson(v);
10276 function $HttpParamSerializerProvider() {
10279 * @name $httpParamSerializer
10282 * Default {@link $http `$http`} params serializer that converts objects to strings
10283 * according to the following rules:
10285 * * `{'foo': 'bar'}` results in `foo=bar`
10286 * * `{'foo': Date.now()}` results in `foo=2015-04-01T09%3A50%3A49.262Z` (`toISOString()` and encoded representation of a Date object)
10287 * * `{'foo': ['bar', 'baz']}` results in `foo=bar&foo=baz` (repeated key for each array element)
10288 * * `{'foo': {'bar':'baz'}}` results in `foo=%7B%22bar%22%3A%22baz%22%7D"` (stringified and encoded representation of an object)
10290 * Note that serializer will sort the request parameters alphabetically.
10293 this.$get = function() {
10294 return function ngParamSerializer(params) {
10295 if (!params) return '';
10297 forEachSorted(params, function(value, key) {
10298 if (value === null || isUndefined(value)) return;
10299 if (isArray(value)) {
10300 forEach(value, function(v) {
10301 parts.push(encodeUriQuery(key) + '=' + encodeUriQuery(serializeValue(v)));
10304 parts.push(encodeUriQuery(key) + '=' + encodeUriQuery(serializeValue(value)));
10308 return parts.join('&');
10313 function $HttpParamSerializerJQLikeProvider() {
10316 * @name $httpParamSerializerJQLike
10319 * Alternative {@link $http `$http`} params serializer that follows
10320 * jQuery's [`param()`](http://api.jquery.com/jquery.param/) method logic.
10321 * The serializer will also sort the params alphabetically.
10323 * To use it for serializing `$http` request parameters, set it as the `paramSerializer` property:
10329 * params: myParams,
10330 * paramSerializer: '$httpParamSerializerJQLike'
10334 * It is also possible to set it as the default `paramSerializer` in the
10335 * {@link $httpProvider#defaults `$httpProvider`}.
10337 * Additionally, you can inject the serializer and use it explicitly, for example to serialize
10338 * form data for submission:
10341 * .controller(function($http, $httpParamSerializerJQLike) {
10347 * data: $httpParamSerializerJQLike(myData),
10349 * 'Content-Type': 'application/x-www-form-urlencoded'
10357 this.$get = function() {
10358 return function jQueryLikeParamSerializer(params) {
10359 if (!params) return '';
10361 serialize(params, '', true);
10362 return parts.join('&');
10364 function serialize(toSerialize, prefix, topLevel) {
10365 if (toSerialize === null || isUndefined(toSerialize)) return;
10366 if (isArray(toSerialize)) {
10367 forEach(toSerialize, function(value, index) {
10368 serialize(value, prefix + '[' + (isObject(value) ? index : '') + ']');
10370 } else if (isObject(toSerialize) && !isDate(toSerialize)) {
10371 forEachSorted(toSerialize, function(value, key) {
10372 serialize(value, prefix +
10373 (topLevel ? '' : '[') +
10375 (topLevel ? '' : ']'));
10378 parts.push(encodeUriQuery(prefix) + '=' + encodeUriQuery(serializeValue(toSerialize)));
10385 function defaultHttpResponseTransform(data, headers) {
10386 if (isString(data)) {
10387 // Strip json vulnerability protection prefix and trim whitespace
10388 var tempData = data.replace(JSON_PROTECTION_PREFIX, '').trim();
10391 var contentType = headers('Content-Type');
10392 if ((contentType && (contentType.indexOf(APPLICATION_JSON) === 0)) || isJsonLike(tempData)) {
10393 data = fromJson(tempData);
10401 function isJsonLike(str) {
10402 var jsonStart = str.match(JSON_START);
10403 return jsonStart && JSON_ENDS[jsonStart[0]].test(str);
10407 * Parse headers into key value object
10409 * @param {string} headers Raw headers as a string
10410 * @returns {Object} Parsed headers as key value object
10412 function parseHeaders(headers) {
10413 var parsed = createMap(), i;
10415 function fillInParsed(key, val) {
10417 parsed[key] = parsed[key] ? parsed[key] + ', ' + val : val;
10421 if (isString(headers)) {
10422 forEach(headers.split('\n'), function(line) {
10423 i = line.indexOf(':');
10424 fillInParsed(lowercase(trim(line.substr(0, i))), trim(line.substr(i + 1)));
10426 } else if (isObject(headers)) {
10427 forEach(headers, function(headerVal, headerKey) {
10428 fillInParsed(lowercase(headerKey), trim(headerVal));
10437 * Returns a function that provides access to parsed headers.
10439 * Headers are lazy parsed when first requested.
10440 * @see parseHeaders
10442 * @param {(string|Object)} headers Headers to provide access to.
10443 * @returns {function(string=)} Returns a getter function which if called with:
10445 * - if called with single an argument returns a single header value or null
10446 * - if called with no arguments returns an object containing all headers.
10448 function headersGetter(headers) {
10451 return function(name) {
10452 if (!headersObj) headersObj = parseHeaders(headers);
10455 var value = headersObj[lowercase(name)];
10456 if (value === void 0) {
10468 * Chain all given functions
10470 * This function is used for both request and response transforming
10472 * @param {*} data Data to transform.
10473 * @param {function(string=)} headers HTTP headers getter fn.
10474 * @param {number} status HTTP status code of the response.
10475 * @param {(Function|Array.<Function>)} fns Function or an array of functions.
10476 * @returns {*} Transformed data.
10478 function transformData(data, headers, status, fns) {
10479 if (isFunction(fns)) {
10480 return fns(data, headers, status);
10483 forEach(fns, function(fn) {
10484 data = fn(data, headers, status);
10491 function isSuccess(status) {
10492 return 200 <= status && status < 300;
10498 * @name $httpProvider
10500 * Use `$httpProvider` to change the default behavior of the {@link ng.$http $http} service.
10502 function $HttpProvider() {
10505 * @name $httpProvider#defaults
10508 * Object containing default values for all {@link ng.$http $http} requests.
10510 * - **`defaults.cache`** - {boolean|Object} - A boolean value or object created with
10511 * {@link ng.$cacheFactory `$cacheFactory`} to enable or disable caching of HTTP responses
10512 * by default. See {@link $http#caching $http Caching} for more information.
10514 * - **`defaults.xsrfCookieName`** - {string} - Name of cookie containing the XSRF token.
10515 * Defaults value is `'XSRF-TOKEN'`.
10517 * - **`defaults.xsrfHeaderName`** - {string} - Name of HTTP header to populate with the
10518 * XSRF token. Defaults value is `'X-XSRF-TOKEN'`.
10520 * - **`defaults.headers`** - {Object} - Default headers for all $http requests.
10521 * Refer to {@link ng.$http#setting-http-headers $http} for documentation on
10522 * setting default headers.
10523 * - **`defaults.headers.common`**
10524 * - **`defaults.headers.post`**
10525 * - **`defaults.headers.put`**
10526 * - **`defaults.headers.patch`**
10529 * - **`defaults.paramSerializer`** - `{string|function(Object<string,string>):string}` - A function
10530 * used to the prepare string representation of request parameters (specified as an object).
10531 * If specified as string, it is interpreted as a function registered with the {@link auto.$injector $injector}.
10532 * Defaults to {@link ng.$httpParamSerializer $httpParamSerializer}.
10535 var defaults = this.defaults = {
10536 // transform incoming response data
10537 transformResponse: [defaultHttpResponseTransform],
10539 // transform outgoing request data
10540 transformRequest: [function(d) {
10541 return isObject(d) && !isFile(d) && !isBlob(d) && !isFormData(d) ? toJson(d) : d;
10547 'Accept': 'application/json, text/plain, */*'
10549 post: shallowCopy(CONTENT_TYPE_APPLICATION_JSON),
10550 put: shallowCopy(CONTENT_TYPE_APPLICATION_JSON),
10551 patch: shallowCopy(CONTENT_TYPE_APPLICATION_JSON)
10554 xsrfCookieName: 'XSRF-TOKEN',
10555 xsrfHeaderName: 'X-XSRF-TOKEN',
10557 paramSerializer: '$httpParamSerializer'
10560 var useApplyAsync = false;
10563 * @name $httpProvider#useApplyAsync
10566 * Configure $http service to combine processing of multiple http responses received at around
10567 * the same time via {@link ng.$rootScope.Scope#$applyAsync $rootScope.$applyAsync}. This can result in
10568 * significant performance improvement for bigger applications that make many HTTP requests
10569 * concurrently (common during application bootstrap).
10571 * Defaults to false. If no value is specified, returns the current configured value.
10573 * @param {boolean=} value If true, when requests are loaded, they will schedule a deferred
10574 * "apply" on the next tick, giving time for subsequent requests in a roughly ~10ms window
10575 * to load and share the same digest cycle.
10577 * @returns {boolean|Object} If a value is specified, returns the $httpProvider for chaining.
10578 * otherwise, returns the current configured value.
10580 this.useApplyAsync = function(value) {
10581 if (isDefined(value)) {
10582 useApplyAsync = !!value;
10585 return useApplyAsync;
10588 var useLegacyPromise = true;
10591 * @name $httpProvider#useLegacyPromiseExtensions
10594 * Configure `$http` service to return promises without the shorthand methods `success` and `error`.
10595 * This should be used to make sure that applications work without these methods.
10597 * Defaults to true. If no value is specified, returns the current configured value.
10599 * @param {boolean=} value If true, `$http` will return a promise with the deprecated legacy `success` and `error` methods.
10601 * @returns {boolean|Object} If a value is specified, returns the $httpProvider for chaining.
10602 * otherwise, returns the current configured value.
10604 this.useLegacyPromiseExtensions = function(value) {
10605 if (isDefined(value)) {
10606 useLegacyPromise = !!value;
10609 return useLegacyPromise;
10614 * @name $httpProvider#interceptors
10617 * Array containing service factories for all synchronous or asynchronous {@link ng.$http $http}
10618 * pre-processing of request or postprocessing of responses.
10620 * These service factories are ordered by request, i.e. they are applied in the same order as the
10621 * array, on request, but reverse order, on response.
10623 * {@link ng.$http#interceptors Interceptors detailed info}
10625 var interceptorFactories = this.interceptors = [];
10627 this.$get = ['$httpBackend', '$$cookieReader', '$cacheFactory', '$rootScope', '$q', '$injector',
10628 function($httpBackend, $$cookieReader, $cacheFactory, $rootScope, $q, $injector) {
10630 var defaultCache = $cacheFactory('$http');
10633 * Make sure that default param serializer is exposed as a function
10635 defaults.paramSerializer = isString(defaults.paramSerializer) ?
10636 $injector.get(defaults.paramSerializer) : defaults.paramSerializer;
10639 * Interceptors stored in reverse order. Inner interceptors before outer interceptors.
10640 * The reversal is needed so that we can build up the interception chain around the
10643 var reversedInterceptors = [];
10645 forEach(interceptorFactories, function(interceptorFactory) {
10646 reversedInterceptors.unshift(isString(interceptorFactory)
10647 ? $injector.get(interceptorFactory) : $injector.invoke(interceptorFactory));
10654 * @requires ng.$httpBackend
10655 * @requires $cacheFactory
10656 * @requires $rootScope
10658 * @requires $injector
10661 * The `$http` service is a core Angular service that facilitates communication with the remote
10662 * HTTP servers via the browser's [XMLHttpRequest](https://developer.mozilla.org/en/xmlhttprequest)
10663 * object or via [JSONP](http://en.wikipedia.org/wiki/JSONP).
10665 * For unit testing applications that use `$http` service, see
10666 * {@link ngMock.$httpBackend $httpBackend mock}.
10668 * For a higher level of abstraction, please check out the {@link ngResource.$resource
10669 * $resource} service.
10671 * The $http API is based on the {@link ng.$q deferred/promise APIs} exposed by
10672 * the $q service. While for simple usage patterns this doesn't matter much, for advanced usage
10673 * it is important to familiarize yourself with these APIs and the guarantees they provide.
10677 * The `$http` service is a function which takes a single argument — a {@link $http#usage configuration object} —
10678 * that is used to generate an HTTP request and returns a {@link ng.$q promise}.
10681 * // Simple GET request example:
10685 * }).then(function successCallback(response) {
10686 * // this callback will be called asynchronously
10687 * // when the response is available
10688 * }, function errorCallback(response) {
10689 * // called asynchronously if an error occurs
10690 * // or server returns response with an error status.
10694 * The response object has these properties:
10696 * - **data** – `{string|Object}` – The response body transformed with the transform
10698 * - **status** – `{number}` – HTTP status code of the response.
10699 * - **headers** – `{function([headerName])}` – Header getter function.
10700 * - **config** – `{Object}` – The configuration object that was used to generate the request.
10701 * - **statusText** – `{string}` – HTTP status text of the response.
10703 * A response status code between 200 and 299 is considered a success status and
10704 * will result in the success callback being called. Note that if the response is a redirect,
10705 * XMLHttpRequest will transparently follow it, meaning that the error callback will not be
10706 * called for such responses.
10709 * ## Shortcut methods
10711 * Shortcut methods are also available. All shortcut methods require passing in the URL, and
10712 * request data must be passed in for POST/PUT requests. An optional config can be passed as the
10716 * $http.get('/someUrl', config).then(successCallback, errorCallback);
10717 * $http.post('/someUrl', data, config).then(successCallback, errorCallback);
10720 * Complete list of shortcut methods:
10722 * - {@link ng.$http#get $http.get}
10723 * - {@link ng.$http#head $http.head}
10724 * - {@link ng.$http#post $http.post}
10725 * - {@link ng.$http#put $http.put}
10726 * - {@link ng.$http#delete $http.delete}
10727 * - {@link ng.$http#jsonp $http.jsonp}
10728 * - {@link ng.$http#patch $http.patch}
10731 * ## Writing Unit Tests that use $http
10732 * When unit testing (using {@link ngMock ngMock}), it is necessary to call
10733 * {@link ngMock.$httpBackend#flush $httpBackend.flush()} to flush each pending
10734 * request using trained responses.
10737 * $httpBackend.expectGET(...);
10739 * $httpBackend.flush();
10742 * ## Deprecation Notice
10743 * <div class="alert alert-danger">
10744 * The `$http` legacy promise methods `success` and `error` have been deprecated.
10745 * Use the standard `then` method instead.
10746 * If {@link $httpProvider#useLegacyPromiseExtensions `$httpProvider.useLegacyPromiseExtensions`} is set to
10747 * `false` then these methods will throw {@link $http:legacy `$http/legacy`} error.
10750 * ## Setting HTTP Headers
10752 * The $http service will automatically add certain HTTP headers to all requests. These defaults
10753 * can be fully configured by accessing the `$httpProvider.defaults.headers` configuration
10754 * object, which currently contains this default configuration:
10756 * - `$httpProvider.defaults.headers.common` (headers that are common for all requests):
10757 * - `Accept: application/json, text/plain, * / *`
10758 * - `$httpProvider.defaults.headers.post`: (header defaults for POST requests)
10759 * - `Content-Type: application/json`
10760 * - `$httpProvider.defaults.headers.put` (header defaults for PUT requests)
10761 * - `Content-Type: application/json`
10763 * To add or overwrite these defaults, simply add or remove a property from these configuration
10764 * objects. To add headers for an HTTP method other than POST or PUT, simply add a new object
10765 * with the lowercased HTTP method name as the key, e.g.
10766 * `$httpProvider.defaults.headers.get = { 'My-Header' : 'value' }`.
10768 * The defaults can also be set at runtime via the `$http.defaults` object in the same
10769 * fashion. For example:
10772 * module.run(function($http) {
10773 * $http.defaults.headers.common.Authorization = 'Basic YmVlcDpib29w';
10777 * In addition, you can supply a `headers` property in the config object passed when
10778 * calling `$http(config)`, which overrides the defaults without changing them globally.
10780 * To explicitly remove a header automatically added via $httpProvider.defaults.headers on a per request basis,
10781 * Use the `headers` property, setting the desired header to `undefined`. For example:
10786 * url: 'http://example.com',
10788 * 'Content-Type': undefined
10790 * data: { test: 'test' }
10793 * $http(req).then(function(){...}, function(){...});
10796 * ## Transforming Requests and Responses
10798 * Both requests and responses can be transformed using transformation functions: `transformRequest`
10799 * and `transformResponse`. These properties can be a single function that returns
10800 * the transformed value (`function(data, headersGetter, status)`) or an array of such transformation functions,
10801 * which allows you to `push` or `unshift` a new transformation function into the transformation chain.
10803 * <div class="alert alert-warning">
10804 * **Note:** Angular does not make a copy of the `data` parameter before it is passed into the `transformRequest` pipeline.
10805 * That means changes to the properties of `data` are not local to the transform function (since Javascript passes objects by reference).
10806 * For example, when calling `$http.get(url, $scope.myObject)`, modifications to the object's properties in a transformRequest
10807 * function will be reflected on the scope and in any templates where the object is data-bound.
10808 * To prevent this, transform functions should have no side-effects.
10809 * If you need to modify properties, it is recommended to make a copy of the data, or create new object to return.
10812 * ### Default Transformations
10814 * The `$httpProvider` provider and `$http` service expose `defaults.transformRequest` and
10815 * `defaults.transformResponse` properties. If a request does not provide its own transformations
10816 * then these will be applied.
10818 * You can augment or replace the default transformations by modifying these properties by adding to or
10819 * replacing the array.
10821 * Angular provides the following default transformations:
10823 * Request transformations (`$httpProvider.defaults.transformRequest` and `$http.defaults.transformRequest`):
10825 * - If the `data` property of the request configuration object contains an object, serialize it
10826 * into JSON format.
10828 * Response transformations (`$httpProvider.defaults.transformResponse` and `$http.defaults.transformResponse`):
10830 * - If XSRF prefix is detected, strip it (see Security Considerations section below).
10831 * - If JSON response is detected, deserialize it using a JSON parser.
10834 * ### Overriding the Default Transformations Per Request
10836 * If you wish override the request/response transformations only for a single request then provide
10837 * `transformRequest` and/or `transformResponse` properties on the configuration object passed
10840 * Note that if you provide these properties on the config object the default transformations will be
10841 * overwritten. If you wish to augment the default transformations then you must include them in your
10842 * local transformation array.
10844 * The following code demonstrates adding a new response transformation to be run after the default response
10845 * transformations have been run.
10848 * function appendTransform(defaults, transform) {
10850 * // We can't guarantee that the default transformation is an array
10851 * defaults = angular.isArray(defaults) ? defaults : [defaults];
10853 * // Append the new transformation to the defaults
10854 * return defaults.concat(transform);
10860 * transformResponse: appendTransform($http.defaults.transformResponse, function(value) {
10861 * return doTransform(value);
10869 * {@link ng.$http `$http`} responses are not cached by default. To enable caching, you must
10870 * set the config.cache value or the default cache value to TRUE or to a cache object (created
10871 * with {@link ng.$cacheFactory `$cacheFactory`}). If defined, the value of config.cache takes
10872 * precedence over the default cache value.
10875 * * cache all responses - set the default cache value to TRUE or to a cache object
10876 * * cache a specific response - set config.cache value to TRUE or to a cache object
10878 * If caching is enabled, but neither the default cache nor config.cache are set to a cache object,
10879 * then the default `$cacheFactory($http)` object is used.
10881 * The default cache value can be set by updating the
10882 * {@link ng.$http#defaults `$http.defaults.cache`} property or the
10883 * {@link $httpProvider#defaults `$httpProvider.defaults.cache`} property.
10885 * When caching is enabled, {@link ng.$http `$http`} stores the response from the server using
10886 * the relevant cache object. The next time the same request is made, the response is returned
10887 * from the cache without sending a request to the server.
10891 * * Only GET and JSONP requests are cached.
10892 * * The cache key is the request URL including search parameters; headers are not considered.
10893 * * Cached responses are returned asynchronously, in the same way as responses from the server.
10894 * * If multiple identical requests are made using the same cache, which is not yet populated,
10895 * one request will be made to the server and remaining requests will return the same response.
10896 * * A cache-control header on the response does not affect if or how responses are cached.
10901 * Before you start creating interceptors, be sure to understand the
10902 * {@link ng.$q $q and deferred/promise APIs}.
10904 * For purposes of global error handling, authentication, or any kind of synchronous or
10905 * asynchronous pre-processing of request or postprocessing of responses, it is desirable to be
10906 * able to intercept requests before they are handed to the server and
10907 * responses before they are handed over to the application code that
10908 * initiated these requests. The interceptors leverage the {@link ng.$q
10909 * promise APIs} to fulfill this need for both synchronous and asynchronous pre-processing.
10911 * The interceptors are service factories that are registered with the `$httpProvider` by
10912 * adding them to the `$httpProvider.interceptors` array. The factory is called and
10913 * injected with dependencies (if specified) and returns the interceptor.
10915 * There are two kinds of interceptors (and two kinds of rejection interceptors):
10917 * * `request`: interceptors get called with a http {@link $http#usage config} object. The function is free to
10918 * modify the `config` object or create a new one. The function needs to return the `config`
10919 * object directly, or a promise containing the `config` or a new `config` object.
10920 * * `requestError`: interceptor gets called when a previous interceptor threw an error or
10921 * resolved with a rejection.
10922 * * `response`: interceptors get called with http `response` object. The function is free to
10923 * modify the `response` object or create a new one. The function needs to return the `response`
10924 * object directly, or as a promise containing the `response` or a new `response` object.
10925 * * `responseError`: interceptor gets called when a previous interceptor threw an error or
10926 * resolved with a rejection.
10930 * // register the interceptor as a service
10931 * $provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) {
10933 * // optional method
10934 * 'request': function(config) {
10935 * // do something on success
10939 * // optional method
10940 * 'requestError': function(rejection) {
10941 * // do something on error
10942 * if (canRecover(rejection)) {
10943 * return responseOrNewPromise
10945 * return $q.reject(rejection);
10950 * // optional method
10951 * 'response': function(response) {
10952 * // do something on success
10956 * // optional method
10957 * 'responseError': function(rejection) {
10958 * // do something on error
10959 * if (canRecover(rejection)) {
10960 * return responseOrNewPromise
10962 * return $q.reject(rejection);
10967 * $httpProvider.interceptors.push('myHttpInterceptor');
10970 * // alternatively, register the interceptor via an anonymous factory
10971 * $httpProvider.interceptors.push(function($q, dependency1, dependency2) {
10973 * 'request': function(config) {
10977 * 'response': function(response) {
10984 * ## Security Considerations
10986 * When designing web applications, consider security threats from:
10988 * - [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx)
10989 * - [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery)
10991 * Both server and the client must cooperate in order to eliminate these threats. Angular comes
10992 * pre-configured with strategies that address these issues, but for this to work backend server
10993 * cooperation is required.
10995 * ### JSON Vulnerability Protection
10997 * A [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx)
10998 * allows third party website to turn your JSON resource URL into
10999 * [JSONP](http://en.wikipedia.org/wiki/JSONP) request under some conditions. To
11000 * counter this your server can prefix all JSON requests with following string `")]}',\n"`.
11001 * Angular will automatically strip the prefix before processing it as JSON.
11003 * For example if your server needs to return:
11008 * which is vulnerable to attack, your server can return:
11014 * Angular will strip the prefix, before processing the JSON.
11017 * ### Cross Site Request Forgery (XSRF) Protection
11019 * [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery) is an attack technique by
11020 * which the attacker can trick an authenticated user into unknowingly executing actions on your
11021 * website. Angular provides a mechanism to counter XSRF. When performing XHR requests, the
11022 * $http service reads a token from a cookie (by default, `XSRF-TOKEN`) and sets it as an HTTP
11023 * header (`X-XSRF-TOKEN`). Since only JavaScript that runs on your domain could read the
11024 * cookie, your server can be assured that the XHR came from JavaScript running on your domain.
11025 * The header will not be set for cross-domain requests.
11027 * To take advantage of this, your server needs to set a token in a JavaScript readable session
11028 * cookie called `XSRF-TOKEN` on the first HTTP GET request. On subsequent XHR requests the
11029 * server can verify that the cookie matches `X-XSRF-TOKEN` HTTP header, and therefore be sure
11030 * that only JavaScript running on your domain could have sent the request. The token must be
11031 * unique for each user and must be verifiable by the server (to prevent the JavaScript from
11032 * making up its own tokens). We recommend that the token is a digest of your site's
11033 * authentication cookie with a [salt](https://en.wikipedia.org/wiki/Salt_(cryptography))
11034 * for added security.
11036 * The name of the headers can be specified using the xsrfHeaderName and xsrfCookieName
11037 * properties of either $httpProvider.defaults at config-time, $http.defaults at run-time,
11038 * or the per-request config object.
11040 * In order to prevent collisions in environments where multiple Angular apps share the
11041 * same domain or subdomain, we recommend that each application uses unique cookie name.
11043 * @param {object} config Object describing the request to be made and how it should be
11044 * processed. The object has following properties:
11046 * - **method** – `{string}` – HTTP method (e.g. 'GET', 'POST', etc)
11047 * - **url** – `{string}` – Absolute or relative URL of the resource that is being requested.
11048 * - **params** – `{Object.<string|Object>}` – Map of strings or objects which will be serialized
11049 * with the `paramSerializer` and appended as GET parameters.
11050 * - **data** – `{string|Object}` – Data to be sent as the request message data.
11051 * - **headers** – `{Object}` – Map of strings or functions which return strings representing
11052 * HTTP headers to send to the server. If the return value of a function is null, the
11053 * header will not be sent. Functions accept a config object as an argument.
11054 * - **eventHandlers** - `{Object}` - Event listeners to be bound to the XMLHttpRequest object.
11055 * To bind events to the XMLHttpRequest upload object, use `uploadEventHandlers`.
11056 * The handler will be called in the context of a `$apply` block.
11057 * - **uploadEventHandlers** - `{Object}` - Event listeners to be bound to the XMLHttpRequest upload
11058 * object. To bind events to the XMLHttpRequest object, use `eventHandlers`.
11059 * The handler will be called in the context of a `$apply` block.
11060 * - **xsrfHeaderName** – `{string}` – Name of HTTP header to populate with the XSRF token.
11061 * - **xsrfCookieName** – `{string}` – Name of cookie containing the XSRF token.
11062 * - **transformRequest** –
11063 * `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` –
11064 * transform function or an array of such functions. The transform function takes the http
11065 * request body and headers and returns its transformed (typically serialized) version.
11066 * See {@link ng.$http#overriding-the-default-transformations-per-request
11067 * Overriding the Default Transformations}
11068 * - **transformResponse** –
11069 * `{function(data, headersGetter, status)|Array.<function(data, headersGetter, status)>}` –
11070 * transform function or an array of such functions. The transform function takes the http
11071 * response body, headers and status and returns its transformed (typically deserialized) version.
11072 * See {@link ng.$http#overriding-the-default-transformations-per-request
11073 * Overriding the Default Transformations}
11074 * - **paramSerializer** - `{string|function(Object<string,string>):string}` - A function used to
11075 * prepare the string representation of request parameters (specified as an object).
11076 * If specified as string, it is interpreted as function registered with the
11077 * {@link $injector $injector}, which means you can create your own serializer
11078 * by registering it as a {@link auto.$provide#service service}.
11079 * The default serializer is the {@link $httpParamSerializer $httpParamSerializer};
11080 * alternatively, you can use the {@link $httpParamSerializerJQLike $httpParamSerializerJQLike}
11081 * - **cache** – `{boolean|Object}` – A boolean value or object created with
11082 * {@link ng.$cacheFactory `$cacheFactory`} to enable or disable caching of the HTTP response.
11083 * See {@link $http#caching $http Caching} for more information.
11084 * - **timeout** – `{number|Promise}` – timeout in milliseconds, or {@link ng.$q promise}
11085 * that should abort the request when resolved.
11086 * - **withCredentials** - `{boolean}` - whether to set the `withCredentials` flag on the
11087 * XHR object. See [requests with credentials](https://developer.mozilla.org/docs/Web/HTTP/Access_control_CORS#Requests_with_credentials)
11088 * for more information.
11089 * - **responseType** - `{string}` - see
11090 * [XMLHttpRequest.responseType](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest#xmlhttprequest-responsetype).
11092 * @returns {HttpPromise} Returns a {@link ng.$q `Promise}` that will be resolved to a response object
11093 * when the request succeeds or fails.
11096 * @property {Array.<Object>} pendingRequests Array of config objects for currently pending
11097 * requests. This is primarily meant to be used for debugging purposes.
11101 <example module="httpExample">
11102 <file name="index.html">
11103 <div ng-controller="FetchController">
11104 <select ng-model="method" aria-label="Request method">
11105 <option>GET</option>
11106 <option>JSONP</option>
11108 <input type="text" ng-model="url" size="80" aria-label="URL" />
11109 <button id="fetchbtn" ng-click="fetch()">fetch</button><br>
11110 <button id="samplegetbtn" ng-click="updateModel('GET', 'http-hello.html')">Sample GET</button>
11111 <button id="samplejsonpbtn"
11112 ng-click="updateModel('JSONP',
11113 'https://angularjs.org/greet.php?callback=JSON_CALLBACK&name=Super%20Hero')">
11116 <button id="invalidjsonpbtn"
11117 ng-click="updateModel('JSONP', 'https://angularjs.org/doesntexist&callback=JSON_CALLBACK')">
11120 <pre>http status code: {{status}}</pre>
11121 <pre>http response data: {{data}}</pre>
11124 <file name="script.js">
11125 angular.module('httpExample', [])
11126 .controller('FetchController', ['$scope', '$http', '$templateCache',
11127 function($scope, $http, $templateCache) {
11128 $scope.method = 'GET';
11129 $scope.url = 'http-hello.html';
11131 $scope.fetch = function() {
11132 $scope.code = null;
11133 $scope.response = null;
11135 $http({method: $scope.method, url: $scope.url, cache: $templateCache}).
11136 then(function(response) {
11137 $scope.status = response.status;
11138 $scope.data = response.data;
11139 }, function(response) {
11140 $scope.data = response.data || "Request failed";
11141 $scope.status = response.status;
11145 $scope.updateModel = function(method, url) {
11146 $scope.method = method;
11151 <file name="http-hello.html">
11154 <file name="protractor.js" type="protractor">
11155 var status = element(by.binding('status'));
11156 var data = element(by.binding('data'));
11157 var fetchBtn = element(by.id('fetchbtn'));
11158 var sampleGetBtn = element(by.id('samplegetbtn'));
11159 var sampleJsonpBtn = element(by.id('samplejsonpbtn'));
11160 var invalidJsonpBtn = element(by.id('invalidjsonpbtn'));
11162 it('should make an xhr GET request', function() {
11163 sampleGetBtn.click();
11165 expect(status.getText()).toMatch('200');
11166 expect(data.getText()).toMatch(/Hello, \$http!/);
11169 // Commented out due to flakes. See https://github.com/angular/angular.js/issues/9185
11170 // it('should make a JSONP request to angularjs.org', function() {
11171 // sampleJsonpBtn.click();
11172 // fetchBtn.click();
11173 // expect(status.getText()).toMatch('200');
11174 // expect(data.getText()).toMatch(/Super Hero!/);
11177 it('should make JSONP request to invalid URL and invoke the error handler',
11179 invalidJsonpBtn.click();
11181 expect(status.getText()).toMatch('0');
11182 expect(data.getText()).toMatch('Request failed');
11187 function $http(requestConfig) {
11189 if (!isObject(requestConfig)) {
11190 throw minErr('$http')('badreq', 'Http request configuration must be an object. Received: {0}', requestConfig);
11193 if (!isString(requestConfig.url)) {
11194 throw minErr('$http')('badreq', 'Http request configuration url must be a string. Received: {0}', requestConfig.url);
11197 var config = extend({
11199 transformRequest: defaults.transformRequest,
11200 transformResponse: defaults.transformResponse,
11201 paramSerializer: defaults.paramSerializer
11204 config.headers = mergeHeaders(requestConfig);
11205 config.method = uppercase(config.method);
11206 config.paramSerializer = isString(config.paramSerializer) ?
11207 $injector.get(config.paramSerializer) : config.paramSerializer;
11209 var serverRequest = function(config) {
11210 var headers = config.headers;
11211 var reqData = transformData(config.data, headersGetter(headers), undefined, config.transformRequest);
11213 // strip content-type if data is undefined
11214 if (isUndefined(reqData)) {
11215 forEach(headers, function(value, header) {
11216 if (lowercase(header) === 'content-type') {
11217 delete headers[header];
11222 if (isUndefined(config.withCredentials) && !isUndefined(defaults.withCredentials)) {
11223 config.withCredentials = defaults.withCredentials;
11227 return sendReq(config, reqData).then(transformResponse, transformResponse);
11230 var chain = [serverRequest, undefined];
11231 var promise = $q.when(config);
11233 // apply interceptors
11234 forEach(reversedInterceptors, function(interceptor) {
11235 if (interceptor.request || interceptor.requestError) {
11236 chain.unshift(interceptor.request, interceptor.requestError);
11238 if (interceptor.response || interceptor.responseError) {
11239 chain.push(interceptor.response, interceptor.responseError);
11243 while (chain.length) {
11244 var thenFn = chain.shift();
11245 var rejectFn = chain.shift();
11247 promise = promise.then(thenFn, rejectFn);
11250 if (useLegacyPromise) {
11251 promise.success = function(fn) {
11252 assertArgFn(fn, 'fn');
11254 promise.then(function(response) {
11255 fn(response.data, response.status, response.headers, config);
11260 promise.error = function(fn) {
11261 assertArgFn(fn, 'fn');
11263 promise.then(null, function(response) {
11264 fn(response.data, response.status, response.headers, config);
11269 promise.success = $httpMinErrLegacyFn('success');
11270 promise.error = $httpMinErrLegacyFn('error');
11275 function transformResponse(response) {
11276 // make a copy since the response must be cacheable
11277 var resp = extend({}, response);
11278 resp.data = transformData(response.data, response.headers, response.status,
11279 config.transformResponse);
11280 return (isSuccess(response.status))
11285 function executeHeaderFns(headers, config) {
11286 var headerContent, processedHeaders = {};
11288 forEach(headers, function(headerFn, header) {
11289 if (isFunction(headerFn)) {
11290 headerContent = headerFn(config);
11291 if (headerContent != null) {
11292 processedHeaders[header] = headerContent;
11295 processedHeaders[header] = headerFn;
11299 return processedHeaders;
11302 function mergeHeaders(config) {
11303 var defHeaders = defaults.headers,
11304 reqHeaders = extend({}, config.headers),
11305 defHeaderName, lowercaseDefHeaderName, reqHeaderName;
11307 defHeaders = extend({}, defHeaders.common, defHeaders[lowercase(config.method)]);
11309 // using for-in instead of forEach to avoid unnecessary iteration after header has been found
11310 defaultHeadersIteration:
11311 for (defHeaderName in defHeaders) {
11312 lowercaseDefHeaderName = lowercase(defHeaderName);
11314 for (reqHeaderName in reqHeaders) {
11315 if (lowercase(reqHeaderName) === lowercaseDefHeaderName) {
11316 continue defaultHeadersIteration;
11320 reqHeaders[defHeaderName] = defHeaders[defHeaderName];
11323 // execute if header value is a function for merged headers
11324 return executeHeaderFns(reqHeaders, shallowCopy(config));
11328 $http.pendingRequests = [];
11335 * Shortcut method to perform `GET` request.
11337 * @param {string} url Relative or absolute URL specifying the destination of the request
11338 * @param {Object=} config Optional configuration object
11339 * @returns {HttpPromise} Future object
11344 * @name $http#delete
11347 * Shortcut method to perform `DELETE` request.
11349 * @param {string} url Relative or absolute URL specifying the destination of the request
11350 * @param {Object=} config Optional configuration object
11351 * @returns {HttpPromise} Future object
11359 * Shortcut method to perform `HEAD` request.
11361 * @param {string} url Relative or absolute URL specifying the destination of the request
11362 * @param {Object=} config Optional configuration object
11363 * @returns {HttpPromise} Future object
11368 * @name $http#jsonp
11371 * Shortcut method to perform `JSONP` request.
11373 * @param {string} url Relative or absolute URL specifying the destination of the request.
11374 * The name of the callback should be the string `JSON_CALLBACK`.
11375 * @param {Object=} config Optional configuration object
11376 * @returns {HttpPromise} Future object
11378 createShortMethods('get', 'delete', 'head', 'jsonp');
11385 * Shortcut method to perform `POST` request.
11387 * @param {string} url Relative or absolute URL specifying the destination of the request
11388 * @param {*} data Request content
11389 * @param {Object=} config Optional configuration object
11390 * @returns {HttpPromise} Future object
11398 * Shortcut method to perform `PUT` request.
11400 * @param {string} url Relative or absolute URL specifying the destination of the request
11401 * @param {*} data Request content
11402 * @param {Object=} config Optional configuration object
11403 * @returns {HttpPromise} Future object
11408 * @name $http#patch
11411 * Shortcut method to perform `PATCH` request.
11413 * @param {string} url Relative or absolute URL specifying the destination of the request
11414 * @param {*} data Request content
11415 * @param {Object=} config Optional configuration object
11416 * @returns {HttpPromise} Future object
11418 createShortMethodsWithData('post', 'put', 'patch');
11422 * @name $http#defaults
11425 * Runtime equivalent of the `$httpProvider.defaults` property. Allows configuration of
11426 * default headers, withCredentials as well as request and response transformations.
11428 * See "Setting HTTP Headers" and "Transforming Requests and Responses" sections above.
11430 $http.defaults = defaults;
11436 function createShortMethods(names) {
11437 forEach(arguments, function(name) {
11438 $http[name] = function(url, config) {
11439 return $http(extend({}, config || {}, {
11448 function createShortMethodsWithData(name) {
11449 forEach(arguments, function(name) {
11450 $http[name] = function(url, data, config) {
11451 return $http(extend({}, config || {}, {
11462 * Makes the request.
11464 * !!! ACCESSES CLOSURE VARS:
11465 * $httpBackend, defaults, $log, $rootScope, defaultCache, $http.pendingRequests
11467 function sendReq(config, reqData) {
11468 var deferred = $q.defer(),
11469 promise = deferred.promise,
11472 reqHeaders = config.headers,
11473 url = buildUrl(config.url, config.paramSerializer(config.params));
11475 $http.pendingRequests.push(config);
11476 promise.then(removePendingReq, removePendingReq);
11479 if ((config.cache || defaults.cache) && config.cache !== false &&
11480 (config.method === 'GET' || config.method === 'JSONP')) {
11481 cache = isObject(config.cache) ? config.cache
11482 : isObject(defaults.cache) ? defaults.cache
11487 cachedResp = cache.get(url);
11488 if (isDefined(cachedResp)) {
11489 if (isPromiseLike(cachedResp)) {
11490 // cached request has already been sent, but there is no response yet
11491 cachedResp.then(resolvePromiseWithResult, resolvePromiseWithResult);
11493 // serving from cache
11494 if (isArray(cachedResp)) {
11495 resolvePromise(cachedResp[1], cachedResp[0], shallowCopy(cachedResp[2]), cachedResp[3]);
11497 resolvePromise(cachedResp, 200, {}, 'OK');
11501 // put the promise for the non-transformed response into cache as a placeholder
11502 cache.put(url, promise);
11507 // if we won't have the response in cache, set the xsrf headers and
11508 // send the request to the backend
11509 if (isUndefined(cachedResp)) {
11510 var xsrfValue = urlIsSameOrigin(config.url)
11511 ? $$cookieReader()[config.xsrfCookieName || defaults.xsrfCookieName]
11514 reqHeaders[(config.xsrfHeaderName || defaults.xsrfHeaderName)] = xsrfValue;
11517 $httpBackend(config.method, url, reqData, done, reqHeaders, config.timeout,
11518 config.withCredentials, config.responseType,
11519 createApplyHandlers(config.eventHandlers),
11520 createApplyHandlers(config.uploadEventHandlers));
11525 function createApplyHandlers(eventHandlers) {
11526 if (eventHandlers) {
11527 var applyHandlers = {};
11528 forEach(eventHandlers, function(eventHandler, key) {
11529 applyHandlers[key] = function(event) {
11530 if (useApplyAsync) {
11531 $rootScope.$applyAsync(callEventHandler);
11532 } else if ($rootScope.$$phase) {
11533 callEventHandler();
11535 $rootScope.$apply(callEventHandler);
11538 function callEventHandler() {
11539 eventHandler(event);
11543 return applyHandlers;
11549 * Callback registered to $httpBackend():
11550 * - caches the response if desired
11551 * - resolves the raw $http promise
11554 function done(status, response, headersString, statusText) {
11556 if (isSuccess(status)) {
11557 cache.put(url, [status, response, parseHeaders(headersString), statusText]);
11559 // remove promise from the cache
11564 function resolveHttpPromise() {
11565 resolvePromise(response, status, headersString, statusText);
11568 if (useApplyAsync) {
11569 $rootScope.$applyAsync(resolveHttpPromise);
11571 resolveHttpPromise();
11572 if (!$rootScope.$$phase) $rootScope.$apply();
11578 * Resolves the raw $http promise.
11580 function resolvePromise(response, status, headers, statusText) {
11581 //status: HTTP response status code, 0, -1 (aborted by timeout / promise)
11582 status = status >= -1 ? status : 0;
11584 (isSuccess(status) ? deferred.resolve : deferred.reject)({
11587 headers: headersGetter(headers),
11589 statusText: statusText
11593 function resolvePromiseWithResult(result) {
11594 resolvePromise(result.data, result.status, shallowCopy(result.headers()), result.statusText);
11597 function removePendingReq() {
11598 var idx = $http.pendingRequests.indexOf(config);
11599 if (idx !== -1) $http.pendingRequests.splice(idx, 1);
11604 function buildUrl(url, serializedParams) {
11605 if (serializedParams.length > 0) {
11606 url += ((url.indexOf('?') == -1) ? '?' : '&') + serializedParams;
11615 * @name $xhrFactory
11618 * Factory function used to create XMLHttpRequest objects.
11620 * Replace or decorate this service to create your own custom XMLHttpRequest objects.
11623 * angular.module('myApp', [])
11624 * .factory('$xhrFactory', function() {
11625 * return function createXhr(method, url) {
11626 * return new window.XMLHttpRequest({mozSystem: true});
11631 * @param {string} method HTTP method of the request (GET, POST, PUT, ..)
11632 * @param {string} url URL of the request.
11634 function $xhrFactoryProvider() {
11635 this.$get = function() {
11636 return function createXhr() {
11637 return new window.XMLHttpRequest();
11644 * @name $httpBackend
11645 * @requires $window
11646 * @requires $document
11647 * @requires $xhrFactory
11650 * HTTP backend used by the {@link ng.$http service} that delegates to
11651 * XMLHttpRequest object or JSONP and deals with browser incompatibilities.
11653 * You should never need to use this service directly, instead use the higher-level abstractions:
11654 * {@link ng.$http $http} or {@link ngResource.$resource $resource}.
11656 * During testing this implementation is swapped with {@link ngMock.$httpBackend mock
11657 * $httpBackend} which can be trained with responses.
11659 function $HttpBackendProvider() {
11660 this.$get = ['$browser', '$window', '$document', '$xhrFactory', function($browser, $window, $document, $xhrFactory) {
11661 return createHttpBackend($browser, $xhrFactory, $browser.defer, $window.angular.callbacks, $document[0]);
11665 function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDocument) {
11666 // TODO(vojta): fix the signature
11667 return function(method, url, post, callback, headers, timeout, withCredentials, responseType, eventHandlers, uploadEventHandlers) {
11668 $browser.$$incOutstandingRequestCount();
11669 url = url || $browser.url();
11671 if (lowercase(method) == 'jsonp') {
11672 var callbackId = '_' + (callbacks.counter++).toString(36);
11673 callbacks[callbackId] = function(data) {
11674 callbacks[callbackId].data = data;
11675 callbacks[callbackId].called = true;
11678 var jsonpDone = jsonpReq(url.replace('JSON_CALLBACK', 'angular.callbacks.' + callbackId),
11679 callbackId, function(status, text) {
11680 completeRequest(callback, status, callbacks[callbackId].data, "", text);
11681 callbacks[callbackId] = noop;
11685 var xhr = createXhr(method, url);
11687 xhr.open(method, url, true);
11688 forEach(headers, function(value, key) {
11689 if (isDefined(value)) {
11690 xhr.setRequestHeader(key, value);
11694 xhr.onload = function requestLoaded() {
11695 var statusText = xhr.statusText || '';
11697 // responseText is the old-school way of retrieving response (supported by IE9)
11698 // response/responseType properties were introduced in XHR Level2 spec (supported by IE10)
11699 var response = ('response' in xhr) ? xhr.response : xhr.responseText;
11701 // normalize IE9 bug (http://bugs.jquery.com/ticket/1450)
11702 var status = xhr.status === 1223 ? 204 : xhr.status;
11704 // fix status code when it is 0 (0 status is undocumented).
11705 // Occurs when accessing file resources or on Android 4.1 stock browser
11706 // while retrieving files from application cache.
11707 if (status === 0) {
11708 status = response ? 200 : urlResolve(url).protocol == 'file' ? 404 : 0;
11711 completeRequest(callback,
11714 xhr.getAllResponseHeaders(),
11718 var requestError = function() {
11719 // The response is always empty
11720 // See https://xhr.spec.whatwg.org/#request-error-steps and https://fetch.spec.whatwg.org/#concept-network-error
11721 completeRequest(callback, -1, null, null, '');
11724 xhr.onerror = requestError;
11725 xhr.onabort = requestError;
11727 forEach(eventHandlers, function(value, key) {
11728 xhr.addEventListener(key, value);
11731 forEach(uploadEventHandlers, function(value, key) {
11732 xhr.upload.addEventListener(key, value);
11735 if (withCredentials) {
11736 xhr.withCredentials = true;
11739 if (responseType) {
11741 xhr.responseType = responseType;
11743 // WebKit added support for the json responseType value on 09/03/2013
11744 // https://bugs.webkit.org/show_bug.cgi?id=73648. Versions of Safari prior to 7 are
11745 // known to throw when setting the value "json" as the response type. Other older
11746 // browsers implementing the responseType
11748 // The json response type can be ignored if not supported, because JSON payloads are
11749 // parsed on the client-side regardless.
11750 if (responseType !== 'json') {
11756 xhr.send(isUndefined(post) ? null : post);
11760 var timeoutId = $browserDefer(timeoutRequest, timeout);
11761 } else if (isPromiseLike(timeout)) {
11762 timeout.then(timeoutRequest);
11766 function timeoutRequest() {
11767 jsonpDone && jsonpDone();
11768 xhr && xhr.abort();
11771 function completeRequest(callback, status, response, headersString, statusText) {
11772 // cancel timeout and subsequent timeout promise resolution
11773 if (isDefined(timeoutId)) {
11774 $browserDefer.cancel(timeoutId);
11776 jsonpDone = xhr = null;
11778 callback(status, response, headersString, statusText);
11779 $browser.$$completeOutstandingRequest(noop);
11783 function jsonpReq(url, callbackId, done) {
11784 // we can't use jQuery/jqLite here because jQuery does crazy stuff with script elements, e.g.:
11785 // - fetches local scripts via XHR and evals them
11786 // - adds and immediately removes script elements from the document
11787 var script = rawDocument.createElement('script'), callback = null;
11788 script.type = "text/javascript";
11790 script.async = true;
11792 callback = function(event) {
11793 removeEventListenerFn(script, "load", callback);
11794 removeEventListenerFn(script, "error", callback);
11795 rawDocument.body.removeChild(script);
11798 var text = "unknown";
11801 if (event.type === "load" && !callbacks[callbackId].called) {
11802 event = { type: "error" };
11805 status = event.type === "error" ? 404 : 200;
11809 done(status, text);
11813 addEventListenerFn(script, "load", callback);
11814 addEventListenerFn(script, "error", callback);
11815 rawDocument.body.appendChild(script);
11820 var $interpolateMinErr = angular.$interpolateMinErr = minErr('$interpolate');
11821 $interpolateMinErr.throwNoconcat = function(text) {
11822 throw $interpolateMinErr('noconcat',
11823 "Error while interpolating: {0}\nStrict Contextual Escaping disallows " +
11824 "interpolations that concatenate multiple expressions when a trusted value is " +
11825 "required. See http://docs.angularjs.org/api/ng.$sce", text);
11828 $interpolateMinErr.interr = function(text, err) {
11829 return $interpolateMinErr('interr', "Can't interpolate: {0}\n{1}", text, err.toString());
11834 * @name $interpolateProvider
11838 * Used for configuring the interpolation markup. Defaults to `{{` and `}}`.
11840 * <div class="alert alert-danger">
11841 * This feature is sometimes used to mix different markup languages, e.g. to wrap an Angular
11842 * template within a Python Jinja template (or any other template language). Mixing templating
11843 * languages is **very dangerous**. The embedding template language will not safely escape Angular
11844 * expressions, so any user-controlled values in the template will cause Cross Site Scripting (XSS)
11849 <example name="custom-interpolation-markup" module="customInterpolationApp">
11850 <file name="index.html">
11852 var customInterpolationApp = angular.module('customInterpolationApp', []);
11854 customInterpolationApp.config(function($interpolateProvider) {
11855 $interpolateProvider.startSymbol('//');
11856 $interpolateProvider.endSymbol('//');
11860 customInterpolationApp.controller('DemoController', function() {
11861 this.label = "This binding is brought you by // interpolation symbols.";
11864 <div ng-controller="DemoController as demo">
11868 <file name="protractor.js" type="protractor">
11869 it('should interpolate binding with custom symbols', function() {
11870 expect(element(by.binding('demo.label')).getText()).toBe('This binding is brought you by // interpolation symbols.');
11875 function $InterpolateProvider() {
11876 var startSymbol = '{{';
11877 var endSymbol = '}}';
11881 * @name $interpolateProvider#startSymbol
11883 * Symbol to denote start of expression in the interpolated string. Defaults to `{{`.
11885 * @param {string=} value new value to set the starting symbol to.
11886 * @returns {string|self} Returns the symbol when used as getter and self if used as setter.
11888 this.startSymbol = function(value) {
11890 startSymbol = value;
11893 return startSymbol;
11899 * @name $interpolateProvider#endSymbol
11901 * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
11903 * @param {string=} value new value to set the ending symbol to.
11904 * @returns {string|self} Returns the symbol when used as getter and self if used as setter.
11906 this.endSymbol = function(value) {
11916 this.$get = ['$parse', '$exceptionHandler', '$sce', function($parse, $exceptionHandler, $sce) {
11917 var startSymbolLength = startSymbol.length,
11918 endSymbolLength = endSymbol.length,
11919 escapedStartRegexp = new RegExp(startSymbol.replace(/./g, escape), 'g'),
11920 escapedEndRegexp = new RegExp(endSymbol.replace(/./g, escape), 'g');
11922 function escape(ch) {
11923 return '\\\\\\' + ch;
11926 function unescapeText(text) {
11927 return text.replace(escapedStartRegexp, startSymbol).
11928 replace(escapedEndRegexp, endSymbol);
11931 function stringify(value) {
11932 if (value == null) { // null || undefined
11935 switch (typeof value) {
11939 value = '' + value;
11942 value = toJson(value);
11948 //TODO: this is the same as the constantWatchDelegate in parse.js
11949 function constantWatchDelegate(scope, listener, objectEquality, constantInterp) {
11951 return unwatch = scope.$watch(function constantInterpolateWatch(scope) {
11953 return constantInterp(scope);
11954 }, listener, objectEquality);
11959 * @name $interpolate
11967 * Compiles a string with markup into an interpolation function. This service is used by the
11968 * HTML {@link ng.$compile $compile} service for data binding. See
11969 * {@link ng.$interpolateProvider $interpolateProvider} for configuring the
11970 * interpolation markup.
11974 * var $interpolate = ...; // injected
11975 * var exp = $interpolate('Hello {{name | uppercase}}!');
11976 * expect(exp({name:'Angular'})).toEqual('Hello ANGULAR!');
11979 * `$interpolate` takes an optional fourth argument, `allOrNothing`. If `allOrNothing` is
11980 * `true`, the interpolation function will return `undefined` unless all embedded expressions
11981 * evaluate to a value other than `undefined`.
11984 * var $interpolate = ...; // injected
11985 * var context = {greeting: 'Hello', name: undefined };
11987 * // default "forgiving" mode
11988 * var exp = $interpolate('{{greeting}} {{name}}!');
11989 * expect(exp(context)).toEqual('Hello !');
11991 * // "allOrNothing" mode
11992 * exp = $interpolate('{{greeting}} {{name}}!', false, null, true);
11993 * expect(exp(context)).toBeUndefined();
11994 * context.name = 'Angular';
11995 * expect(exp(context)).toEqual('Hello Angular!');
11998 * `allOrNothing` is useful for interpolating URLs. `ngSrc` and `ngSrcset` use this behavior.
12000 * ####Escaped Interpolation
12001 * $interpolate provides a mechanism for escaping interpolation markers. Start and end markers
12002 * can be escaped by preceding each of their characters with a REVERSE SOLIDUS U+005C (backslash).
12003 * It will be rendered as a regular start/end marker, and will not be interpreted as an expression
12006 * This enables web-servers to prevent script injection attacks and defacing attacks, to some
12007 * degree, while also enabling code examples to work without relying on the
12008 * {@link ng.directive:ngNonBindable ngNonBindable} directive.
12010 * **For security purposes, it is strongly encouraged that web servers escape user-supplied data,
12011 * replacing angle brackets (<, >) with &lt; and &gt; respectively, and replacing all
12012 * interpolation start/end markers with their escaped counterparts.**
12014 * Escaped interpolation markers are only replaced with the actual interpolation markers in rendered
12015 * output when the $interpolate service processes the text. So, for HTML elements interpolated
12016 * by {@link ng.$compile $compile}, or otherwise interpolated with the `mustHaveExpression` parameter
12017 * set to `true`, the interpolated text must contain an unescaped interpolation expression. As such,
12018 * this is typically useful only when user-data is used in rendering a template from the server, or
12019 * when otherwise untrusted data is used by a directive.
12022 * <file name="index.html">
12023 * <div ng-init="username='A user'">
12024 * <p ng-init="apptitle='Escaping demo'">{{apptitle}}: \{\{ username = "defaced value"; \}\}
12026 * <p><strong>{{username}}</strong> attempts to inject code which will deface the
12027 * application, but fails to accomplish their task, because the server has correctly
12028 * escaped the interpolation start/end markers with REVERSE SOLIDUS U+005C (backslash)
12030 * <p>Instead, the result of the attempted script injection is visible, and can be removed
12031 * from the database by an administrator.</p>
12036 * @param {string} text The text with markup to interpolate.
12037 * @param {boolean=} mustHaveExpression if set to true then the interpolation string must have
12038 * embedded expression in order to return an interpolation function. Strings with no
12039 * embedded expression will return null for the interpolation function.
12040 * @param {string=} trustedContext when provided, the returned function passes the interpolated
12041 * result through {@link ng.$sce#getTrusted $sce.getTrusted(interpolatedResult,
12042 * trustedContext)} before returning it. Refer to the {@link ng.$sce $sce} service that
12043 * provides Strict Contextual Escaping for details.
12044 * @param {boolean=} allOrNothing if `true`, then the returned function returns undefined
12045 * unless all embedded expressions evaluate to a value other than `undefined`.
12046 * @returns {function(context)} an interpolation function which is used to compute the
12047 * interpolated string. The function has these parameters:
12049 * - `context`: evaluation context for all expressions embedded in the interpolated text
12051 function $interpolate(text, mustHaveExpression, trustedContext, allOrNothing) {
12052 // Provide a quick exit and simplified result function for text with no interpolation
12053 if (!text.length || text.indexOf(startSymbol) === -1) {
12054 var constantInterp;
12055 if (!mustHaveExpression) {
12056 var unescapedText = unescapeText(text);
12057 constantInterp = valueFn(unescapedText);
12058 constantInterp.exp = text;
12059 constantInterp.expressions = [];
12060 constantInterp.$$watchDelegate = constantWatchDelegate;
12062 return constantInterp;
12065 allOrNothing = !!allOrNothing;
12071 textLength = text.length,
12074 expressionPositions = [];
12076 while (index < textLength) {
12077 if (((startIndex = text.indexOf(startSymbol, index)) != -1) &&
12078 ((endIndex = text.indexOf(endSymbol, startIndex + startSymbolLength)) != -1)) {
12079 if (index !== startIndex) {
12080 concat.push(unescapeText(text.substring(index, startIndex)));
12082 exp = text.substring(startIndex + startSymbolLength, endIndex);
12083 expressions.push(exp);
12084 parseFns.push($parse(exp, parseStringifyInterceptor));
12085 index = endIndex + endSymbolLength;
12086 expressionPositions.push(concat.length);
12089 // we did not find an interpolation, so we have to add the remainder to the separators array
12090 if (index !== textLength) {
12091 concat.push(unescapeText(text.substring(index)));
12097 // Concatenating expressions makes it hard to reason about whether some combination of
12098 // concatenated values are unsafe to use and could easily lead to XSS. By requiring that a
12099 // single expression be used for iframe[src], object[src], etc., we ensure that the value
12100 // that's used is assigned or constructed by some JS code somewhere that is more testable or
12101 // make it obvious that you bound the value to some user controlled value. This helps reduce
12102 // the load when auditing for XSS issues.
12103 if (trustedContext && concat.length > 1) {
12104 $interpolateMinErr.throwNoconcat(text);
12107 if (!mustHaveExpression || expressions.length) {
12108 var compute = function(values) {
12109 for (var i = 0, ii = expressions.length; i < ii; i++) {
12110 if (allOrNothing && isUndefined(values[i])) return;
12111 concat[expressionPositions[i]] = values[i];
12113 return concat.join('');
12116 var getValue = function(value) {
12117 return trustedContext ?
12118 $sce.getTrusted(trustedContext, value) :
12119 $sce.valueOf(value);
12122 return extend(function interpolationFn(context) {
12124 var ii = expressions.length;
12125 var values = new Array(ii);
12128 for (; i < ii; i++) {
12129 values[i] = parseFns[i](context);
12132 return compute(values);
12134 $exceptionHandler($interpolateMinErr.interr(text, err));
12138 // all of these properties are undocumented for now
12139 exp: text, //just for compatibility with regular watchers created via $watch
12140 expressions: expressions,
12141 $$watchDelegate: function(scope, listener) {
12143 return scope.$watchGroup(parseFns, function interpolateFnWatcher(values, oldValues) {
12144 var currValue = compute(values);
12145 if (isFunction(listener)) {
12146 listener.call(this, currValue, values !== oldValues ? lastValue : currValue, scope);
12148 lastValue = currValue;
12154 function parseStringifyInterceptor(value) {
12156 value = getValue(value);
12157 return allOrNothing && !isDefined(value) ? value : stringify(value);
12159 $exceptionHandler($interpolateMinErr.interr(text, err));
12167 * @name $interpolate#startSymbol
12169 * Symbol to denote the start of expression in the interpolated string. Defaults to `{{`.
12171 * Use {@link ng.$interpolateProvider#startSymbol `$interpolateProvider.startSymbol`} to change
12174 * @returns {string} start symbol.
12176 $interpolate.startSymbol = function() {
12177 return startSymbol;
12183 * @name $interpolate#endSymbol
12185 * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
12187 * Use {@link ng.$interpolateProvider#endSymbol `$interpolateProvider.endSymbol`} to change
12190 * @returns {string} end symbol.
12192 $interpolate.endSymbol = function() {
12196 return $interpolate;
12200 function $IntervalProvider() {
12201 this.$get = ['$rootScope', '$window', '$q', '$$q', '$browser',
12202 function($rootScope, $window, $q, $$q, $browser) {
12203 var intervals = {};
12211 * Angular's wrapper for `window.setInterval`. The `fn` function is executed every `delay`
12214 * The return value of registering an interval function is a promise. This promise will be
12215 * notified upon each tick of the interval, and will be resolved after `count` iterations, or
12216 * run indefinitely if `count` is not defined. The value of the notification will be the
12217 * number of iterations that have run.
12218 * To cancel an interval, call `$interval.cancel(promise)`.
12220 * In tests you can use {@link ngMock.$interval#flush `$interval.flush(millis)`} to
12221 * move forward by `millis` milliseconds and trigger any functions scheduled to run in that
12224 * <div class="alert alert-warning">
12225 * **Note**: Intervals created by this service must be explicitly destroyed when you are finished
12226 * with them. In particular they are not automatically destroyed when a controller's scope or a
12227 * directive's element are destroyed.
12228 * You should take this into consideration and make sure to always cancel the interval at the
12229 * appropriate moment. See the example below for more details on how and when to do this.
12232 * @param {function()} fn A function that should be called repeatedly.
12233 * @param {number} delay Number of milliseconds between each function call.
12234 * @param {number=} [count=0] Number of times to repeat. If not set, or 0, will repeat
12236 * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
12237 * will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
12238 * @param {...*=} Pass additional parameters to the executed function.
12239 * @returns {promise} A promise which will be notified on each iteration.
12242 * <example module="intervalExample">
12243 * <file name="index.html">
12245 * angular.module('intervalExample', [])
12246 * .controller('ExampleController', ['$scope', '$interval',
12247 * function($scope, $interval) {
12248 * $scope.format = 'M/d/yy h:mm:ss a';
12249 * $scope.blood_1 = 100;
12250 * $scope.blood_2 = 120;
12253 * $scope.fight = function() {
12254 * // Don't start a new fight if we are already fighting
12255 * if ( angular.isDefined(stop) ) return;
12257 * stop = $interval(function() {
12258 * if ($scope.blood_1 > 0 && $scope.blood_2 > 0) {
12259 * $scope.blood_1 = $scope.blood_1 - 3;
12260 * $scope.blood_2 = $scope.blood_2 - 4;
12262 * $scope.stopFight();
12267 * $scope.stopFight = function() {
12268 * if (angular.isDefined(stop)) {
12269 * $interval.cancel(stop);
12270 * stop = undefined;
12274 * $scope.resetFight = function() {
12275 * $scope.blood_1 = 100;
12276 * $scope.blood_2 = 120;
12279 * $scope.$on('$destroy', function() {
12280 * // Make sure that the interval is destroyed too
12281 * $scope.stopFight();
12284 * // Register the 'myCurrentTime' directive factory method.
12285 * // We inject $interval and dateFilter service since the factory method is DI.
12286 * .directive('myCurrentTime', ['$interval', 'dateFilter',
12287 * function($interval, dateFilter) {
12288 * // return the directive link function. (compile function not needed)
12289 * return function(scope, element, attrs) {
12290 * var format, // date format
12291 * stopTime; // so that we can cancel the time updates
12293 * // used to update the UI
12294 * function updateTime() {
12295 * element.text(dateFilter(new Date(), format));
12298 * // watch the expression, and update the UI on change.
12299 * scope.$watch(attrs.myCurrentTime, function(value) {
12304 * stopTime = $interval(updateTime, 1000);
12306 * // listen on DOM destroy (removal) event, and cancel the next UI update
12307 * // to prevent updating time after the DOM element was removed.
12308 * element.on('$destroy', function() {
12309 * $interval.cancel(stopTime);
12316 * <div ng-controller="ExampleController">
12317 * <label>Date format: <input ng-model="format"></label> <hr/>
12318 * Current time is: <span my-current-time="format"></span>
12320 * Blood 1 : <font color='red'>{{blood_1}}</font>
12321 * Blood 2 : <font color='red'>{{blood_2}}</font>
12322 * <button type="button" data-ng-click="fight()">Fight</button>
12323 * <button type="button" data-ng-click="stopFight()">StopFight</button>
12324 * <button type="button" data-ng-click="resetFight()">resetFight</button>
12331 function interval(fn, delay, count, invokeApply) {
12332 var hasParams = arguments.length > 4,
12333 args = hasParams ? sliceArgs(arguments, 4) : [],
12334 setInterval = $window.setInterval,
12335 clearInterval = $window.clearInterval,
12337 skipApply = (isDefined(invokeApply) && !invokeApply),
12338 deferred = (skipApply ? $$q : $q).defer(),
12339 promise = deferred.promise;
12341 count = isDefined(count) ? count : 0;
12343 promise.$$intervalId = setInterval(function tick() {
12345 $browser.defer(callback);
12347 $rootScope.$evalAsync(callback);
12349 deferred.notify(iteration++);
12351 if (count > 0 && iteration >= count) {
12352 deferred.resolve(iteration);
12353 clearInterval(promise.$$intervalId);
12354 delete intervals[promise.$$intervalId];
12357 if (!skipApply) $rootScope.$apply();
12361 intervals[promise.$$intervalId] = deferred;
12365 function callback() {
12369 fn.apply(null, args);
12377 * @name $interval#cancel
12380 * Cancels a task associated with the `promise`.
12382 * @param {Promise=} promise returned by the `$interval` function.
12383 * @returns {boolean} Returns `true` if the task was successfully canceled.
12385 interval.cancel = function(promise) {
12386 if (promise && promise.$$intervalId in intervals) {
12387 intervals[promise.$$intervalId].reject('canceled');
12388 $window.clearInterval(promise.$$intervalId);
12389 delete intervals[promise.$$intervalId];
12404 * $locale service provides localization rules for various Angular components. As of right now the
12405 * only public api is:
12407 * * `id` – `{string}` – locale id formatted as `languageId-countryId` (e.g. `en-us`)
12410 var PATH_MATCH = /^([^\?#]*)(\?([^#]*))?(#(.*))?$/,
12411 DEFAULT_PORTS = {'http': 80, 'https': 443, 'ftp': 21};
12412 var $locationMinErr = minErr('$location');
12416 * Encode path using encodeUriSegment, ignoring forward slashes
12418 * @param {string} path Path to encode
12419 * @returns {string}
12421 function encodePath(path) {
12422 var segments = path.split('/'),
12423 i = segments.length;
12426 segments[i] = encodeUriSegment(segments[i]);
12429 return segments.join('/');
12432 function parseAbsoluteUrl(absoluteUrl, locationObj) {
12433 var parsedUrl = urlResolve(absoluteUrl);
12435 locationObj.$$protocol = parsedUrl.protocol;
12436 locationObj.$$host = parsedUrl.hostname;
12437 locationObj.$$port = toInt(parsedUrl.port) || DEFAULT_PORTS[parsedUrl.protocol] || null;
12441 function parseAppUrl(relativeUrl, locationObj) {
12442 var prefixed = (relativeUrl.charAt(0) !== '/');
12444 relativeUrl = '/' + relativeUrl;
12446 var match = urlResolve(relativeUrl);
12447 locationObj.$$path = decodeURIComponent(prefixed && match.pathname.charAt(0) === '/' ?
12448 match.pathname.substring(1) : match.pathname);
12449 locationObj.$$search = parseKeyValue(match.search);
12450 locationObj.$$hash = decodeURIComponent(match.hash);
12452 // make sure path starts with '/';
12453 if (locationObj.$$path && locationObj.$$path.charAt(0) != '/') {
12454 locationObj.$$path = '/' + locationObj.$$path;
12461 * @param {string} begin
12462 * @param {string} whole
12463 * @returns {string} returns text from whole after begin or undefined if it does not begin with
12466 function beginsWith(begin, whole) {
12467 if (whole.indexOf(begin) === 0) {
12468 return whole.substr(begin.length);
12473 function stripHash(url) {
12474 var index = url.indexOf('#');
12475 return index == -1 ? url : url.substr(0, index);
12478 function trimEmptyHash(url) {
12479 return url.replace(/(#.+)|#$/, '$1');
12483 function stripFile(url) {
12484 return url.substr(0, stripHash(url).lastIndexOf('/') + 1);
12487 /* return the server only (scheme://host:port) */
12488 function serverBase(url) {
12489 return url.substring(0, url.indexOf('/', url.indexOf('//') + 2));
12494 * LocationHtml5Url represents an url
12495 * This object is exposed as $location service when HTML5 mode is enabled and supported
12498 * @param {string} appBase application base URL
12499 * @param {string} appBaseNoFile application base URL stripped of any filename
12500 * @param {string} basePrefix url path prefix
12502 function LocationHtml5Url(appBase, appBaseNoFile, basePrefix) {
12503 this.$$html5 = true;
12504 basePrefix = basePrefix || '';
12505 parseAbsoluteUrl(appBase, this);
12509 * Parse given html5 (regular) url string into properties
12510 * @param {string} url HTML5 url
12513 this.$$parse = function(url) {
12514 var pathUrl = beginsWith(appBaseNoFile, url);
12515 if (!isString(pathUrl)) {
12516 throw $locationMinErr('ipthprfx', 'Invalid url "{0}", missing path prefix "{1}".', url,
12520 parseAppUrl(pathUrl, this);
12522 if (!this.$$path) {
12530 * Compose url and update `absUrl` property
12533 this.$$compose = function() {
12534 var search = toKeyValue(this.$$search),
12535 hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
12537 this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
12538 this.$$absUrl = appBaseNoFile + this.$$url.substr(1); // first char is always '/'
12541 this.$$parseLinkUrl = function(url, relHref) {
12542 if (relHref && relHref[0] === '#') {
12543 // special case for links to hash fragments:
12544 // keep the old url and only replace the hash fragment
12545 this.hash(relHref.slice(1));
12548 var appUrl, prevAppUrl;
12551 if (isDefined(appUrl = beginsWith(appBase, url))) {
12552 prevAppUrl = appUrl;
12553 if (isDefined(appUrl = beginsWith(basePrefix, appUrl))) {
12554 rewrittenUrl = appBaseNoFile + (beginsWith('/', appUrl) || appUrl);
12556 rewrittenUrl = appBase + prevAppUrl;
12558 } else if (isDefined(appUrl = beginsWith(appBaseNoFile, url))) {
12559 rewrittenUrl = appBaseNoFile + appUrl;
12560 } else if (appBaseNoFile == url + '/') {
12561 rewrittenUrl = appBaseNoFile;
12563 if (rewrittenUrl) {
12564 this.$$parse(rewrittenUrl);
12566 return !!rewrittenUrl;
12572 * LocationHashbangUrl represents url
12573 * This object is exposed as $location service when developer doesn't opt into html5 mode.
12574 * It also serves as the base class for html5 mode fallback on legacy browsers.
12577 * @param {string} appBase application base URL
12578 * @param {string} appBaseNoFile application base URL stripped of any filename
12579 * @param {string} hashPrefix hashbang prefix
12581 function LocationHashbangUrl(appBase, appBaseNoFile, hashPrefix) {
12583 parseAbsoluteUrl(appBase, this);
12587 * Parse given hashbang url into properties
12588 * @param {string} url Hashbang url
12591 this.$$parse = function(url) {
12592 var withoutBaseUrl = beginsWith(appBase, url) || beginsWith(appBaseNoFile, url);
12593 var withoutHashUrl;
12595 if (!isUndefined(withoutBaseUrl) && withoutBaseUrl.charAt(0) === '#') {
12597 // The rest of the url starts with a hash so we have
12598 // got either a hashbang path or a plain hash fragment
12599 withoutHashUrl = beginsWith(hashPrefix, withoutBaseUrl);
12600 if (isUndefined(withoutHashUrl)) {
12601 // There was no hashbang prefix so we just have a hash fragment
12602 withoutHashUrl = withoutBaseUrl;
12606 // There was no hashbang path nor hash fragment:
12607 // If we are in HTML5 mode we use what is left as the path;
12608 // Otherwise we ignore what is left
12609 if (this.$$html5) {
12610 withoutHashUrl = withoutBaseUrl;
12612 withoutHashUrl = '';
12613 if (isUndefined(withoutBaseUrl)) {
12620 parseAppUrl(withoutHashUrl, this);
12622 this.$$path = removeWindowsDriveName(this.$$path, withoutHashUrl, appBase);
12627 * In Windows, on an anchor node on documents loaded from
12628 * the filesystem, the browser will return a pathname
12629 * prefixed with the drive name ('/C:/path') when a
12630 * pathname without a drive is set:
12631 * * a.setAttribute('href', '/foo')
12632 * * a.pathname === '/C:/foo' //true
12634 * Inside of Angular, we're always using pathnames that
12635 * do not include drive names for routing.
12637 function removeWindowsDriveName(path, url, base) {
12639 Matches paths for file protocol on windows,
12640 such as /C:/foo/bar, and captures only /foo/bar.
12642 var windowsFilePathExp = /^\/[A-Z]:(\/.*)/;
12644 var firstPathSegmentMatch;
12646 //Get the relative path from the input URL.
12647 if (url.indexOf(base) === 0) {
12648 url = url.replace(base, '');
12651 // The input URL intentionally contains a first path segment that ends with a colon.
12652 if (windowsFilePathExp.exec(url)) {
12656 firstPathSegmentMatch = windowsFilePathExp.exec(path);
12657 return firstPathSegmentMatch ? firstPathSegmentMatch[1] : path;
12662 * Compose hashbang url and update `absUrl` property
12665 this.$$compose = function() {
12666 var search = toKeyValue(this.$$search),
12667 hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
12669 this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
12670 this.$$absUrl = appBase + (this.$$url ? hashPrefix + this.$$url : '');
12673 this.$$parseLinkUrl = function(url, relHref) {
12674 if (stripHash(appBase) == stripHash(url)) {
12684 * LocationHashbangUrl represents url
12685 * This object is exposed as $location service when html5 history api is enabled but the browser
12686 * does not support it.
12689 * @param {string} appBase application base URL
12690 * @param {string} appBaseNoFile application base URL stripped of any filename
12691 * @param {string} hashPrefix hashbang prefix
12693 function LocationHashbangInHtml5Url(appBase, appBaseNoFile, hashPrefix) {
12694 this.$$html5 = true;
12695 LocationHashbangUrl.apply(this, arguments);
12697 this.$$parseLinkUrl = function(url, relHref) {
12698 if (relHref && relHref[0] === '#') {
12699 // special case for links to hash fragments:
12700 // keep the old url and only replace the hash fragment
12701 this.hash(relHref.slice(1));
12708 if (appBase == stripHash(url)) {
12709 rewrittenUrl = url;
12710 } else if ((appUrl = beginsWith(appBaseNoFile, url))) {
12711 rewrittenUrl = appBase + hashPrefix + appUrl;
12712 } else if (appBaseNoFile === url + '/') {
12713 rewrittenUrl = appBaseNoFile;
12715 if (rewrittenUrl) {
12716 this.$$parse(rewrittenUrl);
12718 return !!rewrittenUrl;
12721 this.$$compose = function() {
12722 var search = toKeyValue(this.$$search),
12723 hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
12725 this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
12726 // include hashPrefix in $$absUrl when $$url is empty so IE9 does not reload page because of removal of '#'
12727 this.$$absUrl = appBase + hashPrefix + this.$$url;
12733 var locationPrototype = {
12736 * Are we in html5 mode?
12742 * Has any change been replacing?
12749 * @name $location#absUrl
12752 * This method is getter only.
12754 * Return full url representation with all segments encoded according to rules specified in
12755 * [RFC 3986](http://www.ietf.org/rfc/rfc3986.txt).
12759 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
12760 * var absUrl = $location.absUrl();
12761 * // => "http://example.com/#/some/path?foo=bar&baz=xoxo"
12764 * @return {string} full url
12766 absUrl: locationGetter('$$absUrl'),
12770 * @name $location#url
12773 * This method is getter / setter.
12775 * Return url (e.g. `/path?a=b#hash`) when called without any parameter.
12777 * Change path, search and hash, when called with parameter and return `$location`.
12781 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
12782 * var url = $location.url();
12783 * // => "/some/path?foo=bar&baz=xoxo"
12786 * @param {string=} url New url without base prefix (e.g. `/path?a=b#hash`)
12787 * @return {string} url
12789 url: function(url) {
12790 if (isUndefined(url)) {
12794 var match = PATH_MATCH.exec(url);
12795 if (match[1] || url === '') this.path(decodeURIComponent(match[1]));
12796 if (match[2] || match[1] || url === '') this.search(match[3] || '');
12797 this.hash(match[5] || '');
12804 * @name $location#protocol
12807 * This method is getter only.
12809 * Return protocol of current url.
12813 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
12814 * var protocol = $location.protocol();
12818 * @return {string} protocol of current url
12820 protocol: locationGetter('$$protocol'),
12824 * @name $location#host
12827 * This method is getter only.
12829 * Return host of current url.
12831 * Note: compared to the non-angular version `location.host` which returns `hostname:port`, this returns the `hostname` portion only.
12835 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
12836 * var host = $location.host();
12837 * // => "example.com"
12839 * // given url http://user:password@example.com:8080/#/some/path?foo=bar&baz=xoxo
12840 * host = $location.host();
12841 * // => "example.com"
12842 * host = location.host;
12843 * // => "example.com:8080"
12846 * @return {string} host of current url.
12848 host: locationGetter('$$host'),
12852 * @name $location#port
12855 * This method is getter only.
12857 * Return port of current url.
12861 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
12862 * var port = $location.port();
12866 * @return {Number} port
12868 port: locationGetter('$$port'),
12872 * @name $location#path
12875 * This method is getter / setter.
12877 * Return path of current url when called without any parameter.
12879 * Change path when called with parameter and return `$location`.
12881 * Note: Path should always begin with forward slash (/), this method will add the forward slash
12882 * if it is missing.
12886 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
12887 * var path = $location.path();
12888 * // => "/some/path"
12891 * @param {(string|number)=} path New path
12892 * @return {string} path
12894 path: locationGetterSetter('$$path', function(path) {
12895 path = path !== null ? path.toString() : '';
12896 return path.charAt(0) == '/' ? path : '/' + path;
12901 * @name $location#search
12904 * This method is getter / setter.
12906 * Return search part (as object) of current url when called without any parameter.
12908 * Change search part when called with parameter and return `$location`.
12912 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
12913 * var searchObject = $location.search();
12914 * // => {foo: 'bar', baz: 'xoxo'}
12916 * // set foo to 'yipee'
12917 * $location.search('foo', 'yipee');
12918 * // $location.search() => {foo: 'yipee', baz: 'xoxo'}
12921 * @param {string|Object.<string>|Object.<Array.<string>>} search New search params - string or
12924 * When called with a single argument the method acts as a setter, setting the `search` component
12925 * of `$location` to the specified value.
12927 * If the argument is a hash object containing an array of values, these values will be encoded
12928 * as duplicate search parameters in the url.
12930 * @param {(string|Number|Array<string>|boolean)=} paramValue If `search` is a string or number, then `paramValue`
12931 * will override only a single search property.
12933 * If `paramValue` is an array, it will override the property of the `search` component of
12934 * `$location` specified via the first argument.
12936 * If `paramValue` is `null`, the property specified via the first argument will be deleted.
12938 * If `paramValue` is `true`, the property specified via the first argument will be added with no
12939 * value nor trailing equal sign.
12941 * @return {Object} If called with no arguments returns the parsed `search` object. If called with
12942 * one or more arguments returns `$location` object itself.
12944 search: function(search, paramValue) {
12945 switch (arguments.length) {
12947 return this.$$search;
12949 if (isString(search) || isNumber(search)) {
12950 search = search.toString();
12951 this.$$search = parseKeyValue(search);
12952 } else if (isObject(search)) {
12953 search = copy(search, {});
12954 // remove object undefined or null properties
12955 forEach(search, function(value, key) {
12956 if (value == null) delete search[key];
12959 this.$$search = search;
12961 throw $locationMinErr('isrcharg',
12962 'The first argument of the `$location#search()` call must be a string or an object.');
12966 if (isUndefined(paramValue) || paramValue === null) {
12967 delete this.$$search[search];
12969 this.$$search[search] = paramValue;
12979 * @name $location#hash
12982 * This method is getter / setter.
12984 * Returns the hash fragment when called without any parameters.
12986 * Changes the hash fragment when called with a parameter and returns `$location`.
12990 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo#hashValue
12991 * var hash = $location.hash();
12992 * // => "hashValue"
12995 * @param {(string|number)=} hash New hash fragment
12996 * @return {string} hash
12998 hash: locationGetterSetter('$$hash', function(hash) {
12999 return hash !== null ? hash.toString() : '';
13004 * @name $location#replace
13007 * If called, all changes to $location during the current `$digest` will replace the current history
13008 * record, instead of adding a new one.
13010 replace: function() {
13011 this.$$replace = true;
13016 forEach([LocationHashbangInHtml5Url, LocationHashbangUrl, LocationHtml5Url], function(Location) {
13017 Location.prototype = Object.create(locationPrototype);
13021 * @name $location#state
13024 * This method is getter / setter.
13026 * Return the history state object when called without any parameter.
13028 * Change the history state object when called with one parameter and return `$location`.
13029 * The state object is later passed to `pushState` or `replaceState`.
13031 * NOTE: This method is supported only in HTML5 mode and only in browsers supporting
13032 * the HTML5 History API (i.e. methods `pushState` and `replaceState`). If you need to support
13033 * older browsers (like IE9 or Android < 4.0), don't use this method.
13035 * @param {object=} state State object for pushState or replaceState
13036 * @return {object} state
13038 Location.prototype.state = function(state) {
13039 if (!arguments.length) {
13040 return this.$$state;
13043 if (Location !== LocationHtml5Url || !this.$$html5) {
13044 throw $locationMinErr('nostate', 'History API state support is available only ' +
13045 'in HTML5 mode and only in browsers supporting HTML5 History API');
13047 // The user might modify `stateObject` after invoking `$location.state(stateObject)`
13048 // but we're changing the $$state reference to $browser.state() during the $digest
13049 // so the modification window is narrow.
13050 this.$$state = isUndefined(state) ? null : state;
13057 function locationGetter(property) {
13058 return function() {
13059 return this[property];
13064 function locationGetterSetter(property, preprocess) {
13065 return function(value) {
13066 if (isUndefined(value)) {
13067 return this[property];
13070 this[property] = preprocess(value);
13082 * @requires $rootElement
13085 * The $location service parses the URL in the browser address bar (based on the
13086 * [window.location](https://developer.mozilla.org/en/window.location)) and makes the URL
13087 * available to your application. Changes to the URL in the address bar are reflected into
13088 * $location service and changes to $location are reflected into the browser address bar.
13090 * **The $location service:**
13092 * - Exposes the current URL in the browser address bar, so you can
13093 * - Watch and observe the URL.
13094 * - Change the URL.
13095 * - Synchronizes the URL with the browser when the user
13096 * - Changes the address bar.
13097 * - Clicks the back or forward button (or clicks a History link).
13098 * - Clicks on a link.
13099 * - Represents the URL object as a set of methods (protocol, host, port, path, search, hash).
13101 * For more information see {@link guide/$location Developer Guide: Using $location}
13106 * @name $locationProvider
13108 * Use the `$locationProvider` to configure how the application deep linking paths are stored.
13110 function $LocationProvider() {
13111 var hashPrefix = '',
13120 * @name $locationProvider#hashPrefix
13122 * @param {string=} prefix Prefix for hash part (containing path and search)
13123 * @returns {*} current value if used as getter or itself (chaining) if used as setter
13125 this.hashPrefix = function(prefix) {
13126 if (isDefined(prefix)) {
13127 hashPrefix = prefix;
13136 * @name $locationProvider#html5Mode
13138 * @param {(boolean|Object)=} mode If boolean, sets `html5Mode.enabled` to value.
13139 * If object, sets `enabled`, `requireBase` and `rewriteLinks` to respective values. Supported
13141 * - **enabled** – `{boolean}` – (default: false) If true, will rely on `history.pushState` to
13142 * change urls where supported. Will fall back to hash-prefixed paths in browsers that do not
13143 * support `pushState`.
13144 * - **requireBase** - `{boolean}` - (default: `true`) When html5Mode is enabled, specifies
13145 * whether or not a <base> tag is required to be present. If `enabled` and `requireBase` are
13146 * true, and a base tag is not present, an error will be thrown when `$location` is injected.
13147 * See the {@link guide/$location $location guide for more information}
13148 * - **rewriteLinks** - `{boolean}` - (default: `true`) When html5Mode is enabled,
13149 * enables/disables url rewriting for relative links.
13151 * @returns {Object} html5Mode object if used as getter or itself (chaining) if used as setter
13153 this.html5Mode = function(mode) {
13154 if (isBoolean(mode)) {
13155 html5Mode.enabled = mode;
13157 } else if (isObject(mode)) {
13159 if (isBoolean(mode.enabled)) {
13160 html5Mode.enabled = mode.enabled;
13163 if (isBoolean(mode.requireBase)) {
13164 html5Mode.requireBase = mode.requireBase;
13167 if (isBoolean(mode.rewriteLinks)) {
13168 html5Mode.rewriteLinks = mode.rewriteLinks;
13179 * @name $location#$locationChangeStart
13180 * @eventType broadcast on root scope
13182 * Broadcasted before a URL will change.
13184 * This change can be prevented by calling
13185 * `preventDefault` method of the event. See {@link ng.$rootScope.Scope#$on} for more
13186 * details about event object. Upon successful change
13187 * {@link ng.$location#$locationChangeSuccess $locationChangeSuccess} is fired.
13189 * The `newState` and `oldState` parameters may be defined only in HTML5 mode and when
13190 * the browser supports the HTML5 History API.
13192 * @param {Object} angularEvent Synthetic event object.
13193 * @param {string} newUrl New URL
13194 * @param {string=} oldUrl URL that was before it was changed.
13195 * @param {string=} newState New history state object
13196 * @param {string=} oldState History state object that was before it was changed.
13201 * @name $location#$locationChangeSuccess
13202 * @eventType broadcast on root scope
13204 * Broadcasted after a URL was changed.
13206 * The `newState` and `oldState` parameters may be defined only in HTML5 mode and when
13207 * the browser supports the HTML5 History API.
13209 * @param {Object} angularEvent Synthetic event object.
13210 * @param {string} newUrl New URL
13211 * @param {string=} oldUrl URL that was before it was changed.
13212 * @param {string=} newState New history state object
13213 * @param {string=} oldState History state object that was before it was changed.
13216 this.$get = ['$rootScope', '$browser', '$sniffer', '$rootElement', '$window',
13217 function($rootScope, $browser, $sniffer, $rootElement, $window) {
13220 baseHref = $browser.baseHref(), // if base[href] is undefined, it defaults to ''
13221 initialUrl = $browser.url(),
13224 if (html5Mode.enabled) {
13225 if (!baseHref && html5Mode.requireBase) {
13226 throw $locationMinErr('nobase',
13227 "$location in HTML5 mode requires a <base> tag to be present!");
13229 appBase = serverBase(initialUrl) + (baseHref || '/');
13230 LocationMode = $sniffer.history ? LocationHtml5Url : LocationHashbangInHtml5Url;
13232 appBase = stripHash(initialUrl);
13233 LocationMode = LocationHashbangUrl;
13235 var appBaseNoFile = stripFile(appBase);
13237 $location = new LocationMode(appBase, appBaseNoFile, '#' + hashPrefix);
13238 $location.$$parseLinkUrl(initialUrl, initialUrl);
13240 $location.$$state = $browser.state();
13242 var IGNORE_URI_REGEXP = /^\s*(javascript|mailto):/i;
13244 function setBrowserUrlWithFallback(url, replace, state) {
13245 var oldUrl = $location.url();
13246 var oldState = $location.$$state;
13248 $browser.url(url, replace, state);
13250 // Make sure $location.state() returns referentially identical (not just deeply equal)
13251 // state object; this makes possible quick checking if the state changed in the digest
13252 // loop. Checking deep equality would be too expensive.
13253 $location.$$state = $browser.state();
13255 // Restore old values if pushState fails
13256 $location.url(oldUrl);
13257 $location.$$state = oldState;
13263 $rootElement.on('click', function(event) {
13264 // TODO(vojta): rewrite link when opening in new tab/window (in legacy browser)
13265 // currently we open nice url link and redirect then
13267 if (!html5Mode.rewriteLinks || event.ctrlKey || event.metaKey || event.shiftKey || event.which == 2 || event.button == 2) return;
13269 var elm = jqLite(event.target);
13271 // traverse the DOM up to find first A tag
13272 while (nodeName_(elm[0]) !== 'a') {
13273 // ignore rewriting if no A tag (reached root element, or no parent - removed from document)
13274 if (elm[0] === $rootElement[0] || !(elm = elm.parent())[0]) return;
13277 var absHref = elm.prop('href');
13278 // get the actual href attribute - see
13279 // http://msdn.microsoft.com/en-us/library/ie/dd347148(v=vs.85).aspx
13280 var relHref = elm.attr('href') || elm.attr('xlink:href');
13282 if (isObject(absHref) && absHref.toString() === '[object SVGAnimatedString]') {
13283 // SVGAnimatedString.animVal should be identical to SVGAnimatedString.baseVal, unless during
13285 absHref = urlResolve(absHref.animVal).href;
13288 // Ignore when url is started with javascript: or mailto:
13289 if (IGNORE_URI_REGEXP.test(absHref)) return;
13291 if (absHref && !elm.attr('target') && !event.isDefaultPrevented()) {
13292 if ($location.$$parseLinkUrl(absHref, relHref)) {
13293 // We do a preventDefault for all urls that are part of the angular application,
13294 // in html5mode and also without, so that we are able to abort navigation without
13295 // getting double entries in the location history.
13296 event.preventDefault();
13297 // update location manually
13298 if ($location.absUrl() != $browser.url()) {
13299 $rootScope.$apply();
13300 // hack to work around FF6 bug 684208 when scenario runner clicks on links
13301 $window.angular['ff-684208-preventDefault'] = true;
13308 // rewrite hashbang url <> html5 url
13309 if (trimEmptyHash($location.absUrl()) != trimEmptyHash(initialUrl)) {
13310 $browser.url($location.absUrl(), true);
13313 var initializing = true;
13315 // update $location when $browser url changes
13316 $browser.onUrlChange(function(newUrl, newState) {
13318 if (isUndefined(beginsWith(appBaseNoFile, newUrl))) {
13319 // If we are navigating outside of the app then force a reload
13320 $window.location.href = newUrl;
13324 $rootScope.$evalAsync(function() {
13325 var oldUrl = $location.absUrl();
13326 var oldState = $location.$$state;
13327 var defaultPrevented;
13328 newUrl = trimEmptyHash(newUrl);
13329 $location.$$parse(newUrl);
13330 $location.$$state = newState;
13332 defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
13333 newState, oldState).defaultPrevented;
13335 // if the location was changed by a `$locationChangeStart` handler then stop
13336 // processing this location change
13337 if ($location.absUrl() !== newUrl) return;
13339 if (defaultPrevented) {
13340 $location.$$parse(oldUrl);
13341 $location.$$state = oldState;
13342 setBrowserUrlWithFallback(oldUrl, false, oldState);
13344 initializing = false;
13345 afterLocationChange(oldUrl, oldState);
13348 if (!$rootScope.$$phase) $rootScope.$digest();
13352 $rootScope.$watch(function $locationWatch() {
13353 var oldUrl = trimEmptyHash($browser.url());
13354 var newUrl = trimEmptyHash($location.absUrl());
13355 var oldState = $browser.state();
13356 var currentReplace = $location.$$replace;
13357 var urlOrStateChanged = oldUrl !== newUrl ||
13358 ($location.$$html5 && $sniffer.history && oldState !== $location.$$state);
13360 if (initializing || urlOrStateChanged) {
13361 initializing = false;
13363 $rootScope.$evalAsync(function() {
13364 var newUrl = $location.absUrl();
13365 var defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
13366 $location.$$state, oldState).defaultPrevented;
13368 // if the location was changed by a `$locationChangeStart` handler then stop
13369 // processing this location change
13370 if ($location.absUrl() !== newUrl) return;
13372 if (defaultPrevented) {
13373 $location.$$parse(oldUrl);
13374 $location.$$state = oldState;
13376 if (urlOrStateChanged) {
13377 setBrowserUrlWithFallback(newUrl, currentReplace,
13378 oldState === $location.$$state ? null : $location.$$state);
13380 afterLocationChange(oldUrl, oldState);
13385 $location.$$replace = false;
13387 // we don't need to return anything because $evalAsync will make the digest loop dirty when
13388 // there is a change
13393 function afterLocationChange(oldUrl, oldState) {
13394 $rootScope.$broadcast('$locationChangeSuccess', $location.absUrl(), oldUrl,
13395 $location.$$state, oldState);
13403 * @requires $window
13406 * Simple service for logging. Default implementation safely writes the message
13407 * into the browser's console (if present).
13409 * The main purpose of this service is to simplify debugging and troubleshooting.
13411 * The default is to log `debug` messages. You can use
13412 * {@link ng.$logProvider ng.$logProvider#debugEnabled} to change this.
13415 <example module="logExample">
13416 <file name="script.js">
13417 angular.module('logExample', [])
13418 .controller('LogController', ['$scope', '$log', function($scope, $log) {
13419 $scope.$log = $log;
13420 $scope.message = 'Hello World!';
13423 <file name="index.html">
13424 <div ng-controller="LogController">
13425 <p>Reload this page with open console, enter text and hit the log button...</p>
13427 <input type="text" ng-model="message" /></label>
13428 <button ng-click="$log.log(message)">log</button>
13429 <button ng-click="$log.warn(message)">warn</button>
13430 <button ng-click="$log.info(message)">info</button>
13431 <button ng-click="$log.error(message)">error</button>
13432 <button ng-click="$log.debug(message)">debug</button>
13440 * @name $logProvider
13442 * Use the `$logProvider` to configure how the application logs messages
13444 function $LogProvider() {
13450 * @name $logProvider#debugEnabled
13452 * @param {boolean=} flag enable or disable debug level messages
13453 * @returns {*} current value if used as getter or itself (chaining) if used as setter
13455 this.debugEnabled = function(flag) {
13456 if (isDefined(flag)) {
13464 this.$get = ['$window', function($window) {
13471 * Write a log message
13473 log: consoleLog('log'),
13480 * Write an information message
13482 info: consoleLog('info'),
13489 * Write a warning message
13491 warn: consoleLog('warn'),
13498 * Write an error message
13500 error: consoleLog('error'),
13507 * Write a debug message
13509 debug: (function() {
13510 var fn = consoleLog('debug');
13512 return function() {
13514 fn.apply(self, arguments);
13520 function formatError(arg) {
13521 if (arg instanceof Error) {
13523 arg = (arg.message && arg.stack.indexOf(arg.message) === -1)
13524 ? 'Error: ' + arg.message + '\n' + arg.stack
13526 } else if (arg.sourceURL) {
13527 arg = arg.message + '\n' + arg.sourceURL + ':' + arg.line;
13533 function consoleLog(type) {
13534 var console = $window.console || {},
13535 logFn = console[type] || console.log || noop,
13538 // Note: reading logFn.apply throws an error in IE11 in IE8 document mode.
13539 // The reason behind this is that console.log has type "object" in IE8...
13541 hasApply = !!logFn.apply;
13545 return function() {
13547 forEach(arguments, function(arg) {
13548 args.push(formatError(arg));
13550 return logFn.apply(console, args);
13554 // we are IE which either doesn't have window.console => this is noop and we do nothing,
13555 // or we are IE where console.log doesn't have apply so we log at least first 2 args
13556 return function(arg1, arg2) {
13557 logFn(arg1, arg2 == null ? '' : arg2);
13563 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
13564 * Any commits to this file should be reviewed with security in mind. *
13565 * Changes to this file can potentially create security vulnerabilities. *
13566 * An approval from 2 Core members with history of modifying *
13567 * this file is required. *
13569 * Does the change somehow allow for arbitrary javascript to be executed? *
13570 * Or allows for someone to change the prototype of built-in objects? *
13571 * Or gives undesired access to variables likes document or window? *
13572 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
13574 var $parseMinErr = minErr('$parse');
13576 // Sandboxing Angular Expressions
13577 // ------------------------------
13578 // Angular expressions are generally considered safe because these expressions only have direct
13579 // access to `$scope` and locals. However, one can obtain the ability to execute arbitrary JS code by
13580 // obtaining a reference to native JS functions such as the Function constructor.
13582 // As an example, consider the following Angular expression:
13584 // {}.toString.constructor('alert("evil JS code")')
13586 // This sandboxing technique is not perfect and doesn't aim to be. The goal is to prevent exploits
13587 // against the expression language, but not to prevent exploits that were enabled by exposing
13588 // sensitive JavaScript or browser APIs on Scope. Exposing such objects on a Scope is never a good
13589 // practice and therefore we are not even trying to protect against interaction with an object
13590 // explicitly exposed in this way.
13592 // In general, it is not possible to access a Window object from an angular expression unless a
13593 // window or some DOM object that has a reference to window is published onto a Scope.
13594 // Similarly we prevent invocations of function known to be dangerous, as well as assignments to
13597 // See https://docs.angularjs.org/guide/security
13600 function ensureSafeMemberName(name, fullExpression) {
13601 if (name === "__defineGetter__" || name === "__defineSetter__"
13602 || name === "__lookupGetter__" || name === "__lookupSetter__"
13603 || name === "__proto__") {
13604 throw $parseMinErr('isecfld',
13605 'Attempting to access a disallowed field in Angular expressions! '
13606 + 'Expression: {0}', fullExpression);
13611 function getStringValue(name) {
13612 // Property names must be strings. This means that non-string objects cannot be used
13613 // as keys in an object. Any non-string object, including a number, is typecasted
13614 // into a string via the toString method.
13615 // -- MDN, https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Property_accessors#Property_names
13617 // So, to ensure that we are checking the same `name` that JavaScript would use, we cast it
13618 // to a string. It's not always possible. If `name` is an object and its `toString` method is
13619 // 'broken' (doesn't return a string, isn't a function, etc.), an error will be thrown:
13621 // TypeError: Cannot convert object to primitive value
13623 // For performance reasons, we don't catch this error here and allow it to propagate up the call
13624 // stack. Note that you'll get the same error in JavaScript if you try to access a property using
13625 // such a 'broken' object as a key.
13629 function ensureSafeObject(obj, fullExpression) {
13630 // nifty check if obj is Function that is fast and works across iframes and other contexts
13632 if (obj.constructor === obj) {
13633 throw $parseMinErr('isecfn',
13634 'Referencing Function in Angular expressions is disallowed! Expression: {0}',
13636 } else if (// isWindow(obj)
13637 obj.window === obj) {
13638 throw $parseMinErr('isecwindow',
13639 'Referencing the Window in Angular expressions is disallowed! Expression: {0}',
13641 } else if (// isElement(obj)
13642 obj.children && (obj.nodeName || (obj.prop && obj.attr && obj.find))) {
13643 throw $parseMinErr('isecdom',
13644 'Referencing DOM nodes in Angular expressions is disallowed! Expression: {0}',
13646 } else if (// block Object so that we can't get hold of dangerous Object.* methods
13648 throw $parseMinErr('isecobj',
13649 'Referencing Object in Angular expressions is disallowed! Expression: {0}',
13656 var CALL = Function.prototype.call;
13657 var APPLY = Function.prototype.apply;
13658 var BIND = Function.prototype.bind;
13660 function ensureSafeFunction(obj, fullExpression) {
13662 if (obj.constructor === obj) {
13663 throw $parseMinErr('isecfn',
13664 'Referencing Function in Angular expressions is disallowed! Expression: {0}',
13666 } else if (obj === CALL || obj === APPLY || obj === BIND) {
13667 throw $parseMinErr('isecff',
13668 'Referencing call, apply or bind in Angular expressions is disallowed! Expression: {0}',
13674 function ensureSafeAssignContext(obj, fullExpression) {
13676 if (obj === (0).constructor || obj === (false).constructor || obj === ''.constructor ||
13677 obj === {}.constructor || obj === [].constructor || obj === Function.constructor) {
13678 throw $parseMinErr('isecaf',
13679 'Assigning to a constructor is disallowed! Expression: {0}', fullExpression);
13684 var OPERATORS = createMap();
13685 forEach('+ - * / % === !== == != < > <= >= && || ! = |'.split(' '), function(operator) { OPERATORS[operator] = true; });
13686 var ESCAPE = {"n":"\n", "f":"\f", "r":"\r", "t":"\t", "v":"\v", "'":"'", '"':'"'};
13689 /////////////////////////////////////////
13695 var Lexer = function(options) {
13696 this.options = options;
13699 Lexer.prototype = {
13700 constructor: Lexer,
13702 lex: function(text) {
13707 while (this.index < this.text.length) {
13708 var ch = this.text.charAt(this.index);
13709 if (ch === '"' || ch === "'") {
13710 this.readString(ch);
13711 } else if (this.isNumber(ch) || ch === '.' && this.isNumber(this.peek())) {
13713 } else if (this.isIdentifierStart(this.peekMultichar())) {
13715 } else if (this.is(ch, '(){}[].,;:?')) {
13716 this.tokens.push({index: this.index, text: ch});
13718 } else if (this.isWhitespace(ch)) {
13721 var ch2 = ch + this.peek();
13722 var ch3 = ch2 + this.peek(2);
13723 var op1 = OPERATORS[ch];
13724 var op2 = OPERATORS[ch2];
13725 var op3 = OPERATORS[ch3];
13726 if (op1 || op2 || op3) {
13727 var token = op3 ? ch3 : (op2 ? ch2 : ch);
13728 this.tokens.push({index: this.index, text: token, operator: true});
13729 this.index += token.length;
13731 this.throwError('Unexpected next character ', this.index, this.index + 1);
13735 return this.tokens;
13738 is: function(ch, chars) {
13739 return chars.indexOf(ch) !== -1;
13742 peek: function(i) {
13744 return (this.index + num < this.text.length) ? this.text.charAt(this.index + num) : false;
13747 isNumber: function(ch) {
13748 return ('0' <= ch && ch <= '9') && typeof ch === "string";
13751 isWhitespace: function(ch) {
13752 // IE treats non-breaking space as \u00A0
13753 return (ch === ' ' || ch === '\r' || ch === '\t' ||
13754 ch === '\n' || ch === '\v' || ch === '\u00A0');
13757 isIdentifierStart: function(ch) {
13758 return this.options.isIdentifierStart ?
13759 this.options.isIdentifierStart(ch, this.codePointAt(ch)) :
13760 this.isValidIdentifierStart(ch);
13763 isValidIdentifierStart: function(ch) {
13764 return ('a' <= ch && ch <= 'z' ||
13765 'A' <= ch && ch <= 'Z' ||
13766 '_' === ch || ch === '$');
13769 isIdentifierContinue: function(ch) {
13770 return this.options.isIdentifierContinue ?
13771 this.options.isIdentifierContinue(ch, this.codePointAt(ch)) :
13772 this.isValidIdentifierContinue(ch);
13775 isValidIdentifierContinue: function(ch, cp) {
13776 return this.isValidIdentifierStart(ch, cp) || this.isNumber(ch);
13779 codePointAt: function(ch) {
13780 if (ch.length === 1) return ch.charCodeAt(0);
13781 /*jshint bitwise: false*/
13782 return (ch.charCodeAt(0) << 10) + ch.charCodeAt(1) - 0x35FDC00;
13783 /*jshint bitwise: true*/
13786 peekMultichar: function() {
13787 var ch = this.text.charAt(this.index);
13788 var peek = this.peek();
13792 var cp1 = ch.charCodeAt(0);
13793 var cp2 = peek.charCodeAt(0);
13794 if (cp1 >= 0xD800 && cp1 <= 0xDBFF && cp2 >= 0xDC00 && cp2 <= 0xDFFF) {
13800 isExpOperator: function(ch) {
13801 return (ch === '-' || ch === '+' || this.isNumber(ch));
13804 throwError: function(error, start, end) {
13805 end = end || this.index;
13806 var colStr = (isDefined(start)
13807 ? 's ' + start + '-' + this.index + ' [' + this.text.substring(start, end) + ']'
13809 throw $parseMinErr('lexerr', 'Lexer Error: {0} at column{1} in expression [{2}].',
13810 error, colStr, this.text);
13813 readNumber: function() {
13815 var start = this.index;
13816 while (this.index < this.text.length) {
13817 var ch = lowercase(this.text.charAt(this.index));
13818 if (ch == '.' || this.isNumber(ch)) {
13821 var peekCh = this.peek();
13822 if (ch == 'e' && this.isExpOperator(peekCh)) {
13824 } else if (this.isExpOperator(ch) &&
13825 peekCh && this.isNumber(peekCh) &&
13826 number.charAt(number.length - 1) == 'e') {
13828 } else if (this.isExpOperator(ch) &&
13829 (!peekCh || !this.isNumber(peekCh)) &&
13830 number.charAt(number.length - 1) == 'e') {
13831 this.throwError('Invalid exponent');
13842 value: Number(number)
13846 readIdent: function() {
13847 var start = this.index;
13848 this.index += this.peekMultichar().length;
13849 while (this.index < this.text.length) {
13850 var ch = this.peekMultichar();
13851 if (!this.isIdentifierContinue(ch)) {
13854 this.index += ch.length;
13858 text: this.text.slice(start, this.index),
13863 readString: function(quote) {
13864 var start = this.index;
13867 var rawString = quote;
13868 var escape = false;
13869 while (this.index < this.text.length) {
13870 var ch = this.text.charAt(this.index);
13874 var hex = this.text.substring(this.index + 1, this.index + 5);
13875 if (!hex.match(/[\da-f]{4}/i)) {
13876 this.throwError('Invalid unicode escape [\\u' + hex + ']');
13879 string += String.fromCharCode(parseInt(hex, 16));
13881 var rep = ESCAPE[ch];
13882 string = string + (rep || ch);
13885 } else if (ch === '\\') {
13887 } else if (ch === quote) {
13901 this.throwError('Unterminated quote', start);
13905 var AST = function(lexer, options) {
13906 this.lexer = lexer;
13907 this.options = options;
13910 AST.Program = 'Program';
13911 AST.ExpressionStatement = 'ExpressionStatement';
13912 AST.AssignmentExpression = 'AssignmentExpression';
13913 AST.ConditionalExpression = 'ConditionalExpression';
13914 AST.LogicalExpression = 'LogicalExpression';
13915 AST.BinaryExpression = 'BinaryExpression';
13916 AST.UnaryExpression = 'UnaryExpression';
13917 AST.CallExpression = 'CallExpression';
13918 AST.MemberExpression = 'MemberExpression';
13919 AST.Identifier = 'Identifier';
13920 AST.Literal = 'Literal';
13921 AST.ArrayExpression = 'ArrayExpression';
13922 AST.Property = 'Property';
13923 AST.ObjectExpression = 'ObjectExpression';
13924 AST.ThisExpression = 'ThisExpression';
13925 AST.LocalsExpression = 'LocalsExpression';
13927 // Internal use only
13928 AST.NGValueParameter = 'NGValueParameter';
13931 ast: function(text) {
13933 this.tokens = this.lexer.lex(text);
13935 var value = this.program();
13937 if (this.tokens.length !== 0) {
13938 this.throwError('is an unexpected token', this.tokens[0]);
13944 program: function() {
13947 if (this.tokens.length > 0 && !this.peek('}', ')', ';', ']'))
13948 body.push(this.expressionStatement());
13949 if (!this.expect(';')) {
13950 return { type: AST.Program, body: body};
13955 expressionStatement: function() {
13956 return { type: AST.ExpressionStatement, expression: this.filterChain() };
13959 filterChain: function() {
13960 var left = this.expression();
13962 while ((token = this.expect('|'))) {
13963 left = this.filter(left);
13968 expression: function() {
13969 return this.assignment();
13972 assignment: function() {
13973 var result = this.ternary();
13974 if (this.expect('=')) {
13975 result = { type: AST.AssignmentExpression, left: result, right: this.assignment(), operator: '='};
13980 ternary: function() {
13981 var test = this.logicalOR();
13984 if (this.expect('?')) {
13985 alternate = this.expression();
13986 if (this.consume(':')) {
13987 consequent = this.expression();
13988 return { type: AST.ConditionalExpression, test: test, alternate: alternate, consequent: consequent};
13994 logicalOR: function() {
13995 var left = this.logicalAND();
13996 while (this.expect('||')) {
13997 left = { type: AST.LogicalExpression, operator: '||', left: left, right: this.logicalAND() };
14002 logicalAND: function() {
14003 var left = this.equality();
14004 while (this.expect('&&')) {
14005 left = { type: AST.LogicalExpression, operator: '&&', left: left, right: this.equality()};
14010 equality: function() {
14011 var left = this.relational();
14013 while ((token = this.expect('==','!=','===','!=='))) {
14014 left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.relational() };
14019 relational: function() {
14020 var left = this.additive();
14022 while ((token = this.expect('<', '>', '<=', '>='))) {
14023 left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.additive() };
14028 additive: function() {
14029 var left = this.multiplicative();
14031 while ((token = this.expect('+','-'))) {
14032 left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.multiplicative() };
14037 multiplicative: function() {
14038 var left = this.unary();
14040 while ((token = this.expect('*','/','%'))) {
14041 left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.unary() };
14046 unary: function() {
14048 if ((token = this.expect('+', '-', '!'))) {
14049 return { type: AST.UnaryExpression, operator: token.text, prefix: true, argument: this.unary() };
14051 return this.primary();
14055 primary: function() {
14057 if (this.expect('(')) {
14058 primary = this.filterChain();
14060 } else if (this.expect('[')) {
14061 primary = this.arrayDeclaration();
14062 } else if (this.expect('{')) {
14063 primary = this.object();
14064 } else if (this.selfReferential.hasOwnProperty(this.peek().text)) {
14065 primary = copy(this.selfReferential[this.consume().text]);
14066 } else if (this.options.literals.hasOwnProperty(this.peek().text)) {
14067 primary = { type: AST.Literal, value: this.options.literals[this.consume().text]};
14068 } else if (this.peek().identifier) {
14069 primary = this.identifier();
14070 } else if (this.peek().constant) {
14071 primary = this.constant();
14073 this.throwError('not a primary expression', this.peek());
14077 while ((next = this.expect('(', '[', '.'))) {
14078 if (next.text === '(') {
14079 primary = {type: AST.CallExpression, callee: primary, arguments: this.parseArguments() };
14081 } else if (next.text === '[') {
14082 primary = { type: AST.MemberExpression, object: primary, property: this.expression(), computed: true };
14084 } else if (next.text === '.') {
14085 primary = { type: AST.MemberExpression, object: primary, property: this.identifier(), computed: false };
14087 this.throwError('IMPOSSIBLE');
14093 filter: function(baseExpression) {
14094 var args = [baseExpression];
14095 var result = {type: AST.CallExpression, callee: this.identifier(), arguments: args, filter: true};
14097 while (this.expect(':')) {
14098 args.push(this.expression());
14104 parseArguments: function() {
14106 if (this.peekToken().text !== ')') {
14108 args.push(this.expression());
14109 } while (this.expect(','));
14114 identifier: function() {
14115 var token = this.consume();
14116 if (!token.identifier) {
14117 this.throwError('is not a valid identifier', token);
14119 return { type: AST.Identifier, name: token.text };
14122 constant: function() {
14123 // TODO check that it is a constant
14124 return { type: AST.Literal, value: this.consume().value };
14127 arrayDeclaration: function() {
14129 if (this.peekToken().text !== ']') {
14131 if (this.peek(']')) {
14132 // Support trailing commas per ES5.1.
14135 elements.push(this.expression());
14136 } while (this.expect(','));
14140 return { type: AST.ArrayExpression, elements: elements };
14143 object: function() {
14144 var properties = [], property;
14145 if (this.peekToken().text !== '}') {
14147 if (this.peek('}')) {
14148 // Support trailing commas per ES5.1.
14151 property = {type: AST.Property, kind: 'init'};
14152 if (this.peek().constant) {
14153 property.key = this.constant();
14154 } else if (this.peek().identifier) {
14155 property.key = this.identifier();
14157 this.throwError("invalid key", this.peek());
14160 property.value = this.expression();
14161 properties.push(property);
14162 } while (this.expect(','));
14166 return {type: AST.ObjectExpression, properties: properties };
14169 throwError: function(msg, token) {
14170 throw $parseMinErr('syntax',
14171 'Syntax Error: Token \'{0}\' {1} at column {2} of the expression [{3}] starting at [{4}].',
14172 token.text, msg, (token.index + 1), this.text, this.text.substring(token.index));
14175 consume: function(e1) {
14176 if (this.tokens.length === 0) {
14177 throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text);
14180 var token = this.expect(e1);
14182 this.throwError('is unexpected, expecting [' + e1 + ']', this.peek());
14187 peekToken: function() {
14188 if (this.tokens.length === 0) {
14189 throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text);
14191 return this.tokens[0];
14194 peek: function(e1, e2, e3, e4) {
14195 return this.peekAhead(0, e1, e2, e3, e4);
14198 peekAhead: function(i, e1, e2, e3, e4) {
14199 if (this.tokens.length > i) {
14200 var token = this.tokens[i];
14201 var t = token.text;
14202 if (t === e1 || t === e2 || t === e3 || t === e4 ||
14203 (!e1 && !e2 && !e3 && !e4)) {
14210 expect: function(e1, e2, e3, e4) {
14211 var token = this.peek(e1, e2, e3, e4);
14213 this.tokens.shift();
14220 'this': {type: AST.ThisExpression },
14221 '$locals': {type: AST.LocalsExpression }
14225 function ifDefined(v, d) {
14226 return typeof v !== 'undefined' ? v : d;
14229 function plusFn(l, r) {
14230 if (typeof l === 'undefined') return r;
14231 if (typeof r === 'undefined') return l;
14235 function isStateless($filter, filterName) {
14236 var fn = $filter(filterName);
14237 return !fn.$stateful;
14240 function findConstantAndWatchExpressions(ast, $filter) {
14243 switch (ast.type) {
14245 allConstants = true;
14246 forEach(ast.body, function(expr) {
14247 findConstantAndWatchExpressions(expr.expression, $filter);
14248 allConstants = allConstants && expr.expression.constant;
14250 ast.constant = allConstants;
14253 ast.constant = true;
14256 case AST.UnaryExpression:
14257 findConstantAndWatchExpressions(ast.argument, $filter);
14258 ast.constant = ast.argument.constant;
14259 ast.toWatch = ast.argument.toWatch;
14261 case AST.BinaryExpression:
14262 findConstantAndWatchExpressions(ast.left, $filter);
14263 findConstantAndWatchExpressions(ast.right, $filter);
14264 ast.constant = ast.left.constant && ast.right.constant;
14265 ast.toWatch = ast.left.toWatch.concat(ast.right.toWatch);
14267 case AST.LogicalExpression:
14268 findConstantAndWatchExpressions(ast.left, $filter);
14269 findConstantAndWatchExpressions(ast.right, $filter);
14270 ast.constant = ast.left.constant && ast.right.constant;
14271 ast.toWatch = ast.constant ? [] : [ast];
14273 case AST.ConditionalExpression:
14274 findConstantAndWatchExpressions(ast.test, $filter);
14275 findConstantAndWatchExpressions(ast.alternate, $filter);
14276 findConstantAndWatchExpressions(ast.consequent, $filter);
14277 ast.constant = ast.test.constant && ast.alternate.constant && ast.consequent.constant;
14278 ast.toWatch = ast.constant ? [] : [ast];
14280 case AST.Identifier:
14281 ast.constant = false;
14282 ast.toWatch = [ast];
14284 case AST.MemberExpression:
14285 findConstantAndWatchExpressions(ast.object, $filter);
14286 if (ast.computed) {
14287 findConstantAndWatchExpressions(ast.property, $filter);
14289 ast.constant = ast.object.constant && (!ast.computed || ast.property.constant);
14290 ast.toWatch = [ast];
14292 case AST.CallExpression:
14293 allConstants = ast.filter ? isStateless($filter, ast.callee.name) : false;
14295 forEach(ast.arguments, function(expr) {
14296 findConstantAndWatchExpressions(expr, $filter);
14297 allConstants = allConstants && expr.constant;
14298 if (!expr.constant) {
14299 argsToWatch.push.apply(argsToWatch, expr.toWatch);
14302 ast.constant = allConstants;
14303 ast.toWatch = ast.filter && isStateless($filter, ast.callee.name) ? argsToWatch : [ast];
14305 case AST.AssignmentExpression:
14306 findConstantAndWatchExpressions(ast.left, $filter);
14307 findConstantAndWatchExpressions(ast.right, $filter);
14308 ast.constant = ast.left.constant && ast.right.constant;
14309 ast.toWatch = [ast];
14311 case AST.ArrayExpression:
14312 allConstants = true;
14314 forEach(ast.elements, function(expr) {
14315 findConstantAndWatchExpressions(expr, $filter);
14316 allConstants = allConstants && expr.constant;
14317 if (!expr.constant) {
14318 argsToWatch.push.apply(argsToWatch, expr.toWatch);
14321 ast.constant = allConstants;
14322 ast.toWatch = argsToWatch;
14324 case AST.ObjectExpression:
14325 allConstants = true;
14327 forEach(ast.properties, function(property) {
14328 findConstantAndWatchExpressions(property.value, $filter);
14329 allConstants = allConstants && property.value.constant;
14330 if (!property.value.constant) {
14331 argsToWatch.push.apply(argsToWatch, property.value.toWatch);
14334 ast.constant = allConstants;
14335 ast.toWatch = argsToWatch;
14337 case AST.ThisExpression:
14338 ast.constant = false;
14341 case AST.LocalsExpression:
14342 ast.constant = false;
14348 function getInputs(body) {
14349 if (body.length != 1) return;
14350 var lastExpression = body[0].expression;
14351 var candidate = lastExpression.toWatch;
14352 if (candidate.length !== 1) return candidate;
14353 return candidate[0] !== lastExpression ? candidate : undefined;
14356 function isAssignable(ast) {
14357 return ast.type === AST.Identifier || ast.type === AST.MemberExpression;
14360 function assignableAST(ast) {
14361 if (ast.body.length === 1 && isAssignable(ast.body[0].expression)) {
14362 return {type: AST.AssignmentExpression, left: ast.body[0].expression, right: {type: AST.NGValueParameter}, operator: '='};
14366 function isLiteral(ast) {
14367 return ast.body.length === 0 ||
14368 ast.body.length === 1 && (
14369 ast.body[0].expression.type === AST.Literal ||
14370 ast.body[0].expression.type === AST.ArrayExpression ||
14371 ast.body[0].expression.type === AST.ObjectExpression);
14374 function isConstant(ast) {
14375 return ast.constant;
14378 function ASTCompiler(astBuilder, $filter) {
14379 this.astBuilder = astBuilder;
14380 this.$filter = $filter;
14383 ASTCompiler.prototype = {
14384 compile: function(expression, expensiveChecks) {
14386 var ast = this.astBuilder.ast(expression);
14390 expensiveChecks: expensiveChecks,
14391 fn: {vars: [], body: [], own: {}},
14392 assign: {vars: [], body: [], own: {}},
14395 findConstantAndWatchExpressions(ast, self.$filter);
14398 this.stage = 'assign';
14399 if ((assignable = assignableAST(ast))) {
14400 this.state.computing = 'assign';
14401 var result = this.nextId();
14402 this.recurse(assignable, result);
14403 this.return_(result);
14404 extra = 'fn.assign=' + this.generateFunction('assign', 's,v,l');
14406 var toWatch = getInputs(ast.body);
14407 self.stage = 'inputs';
14408 forEach(toWatch, function(watch, key) {
14409 var fnKey = 'fn' + key;
14410 self.state[fnKey] = {vars: [], body: [], own: {}};
14411 self.state.computing = fnKey;
14412 var intoId = self.nextId();
14413 self.recurse(watch, intoId);
14414 self.return_(intoId);
14415 self.state.inputs.push(fnKey);
14416 watch.watchId = key;
14418 this.state.computing = 'fn';
14419 this.stage = 'main';
14422 // The build and minification steps remove the string "use strict" from the code, but this is done using a regex.
14423 // This is a workaround for this until we do a better job at only removing the prefix only when we should.
14424 '"' + this.USE + ' ' + this.STRICT + '";\n' +
14425 this.filterPrefix() +
14426 'var fn=' + this.generateFunction('fn', 's,l,a,i') +
14432 var fn = (new Function('$filter',
14433 'ensureSafeMemberName',
14434 'ensureSafeObject',
14435 'ensureSafeFunction',
14437 'ensureSafeAssignContext',
14443 ensureSafeMemberName,
14445 ensureSafeFunction,
14447 ensureSafeAssignContext,
14452 this.state = this.stage = undefined;
14453 fn.literal = isLiteral(ast);
14454 fn.constant = isConstant(ast);
14462 watchFns: function() {
14464 var fns = this.state.inputs;
14466 forEach(fns, function(name) {
14467 result.push('var ' + name + '=' + self.generateFunction(name, 's'));
14470 result.push('fn.inputs=[' + fns.join(',') + '];');
14472 return result.join('');
14475 generateFunction: function(name, params) {
14476 return 'function(' + params + '){' +
14477 this.varsPrefix(name) +
14482 filterPrefix: function() {
14485 forEach(this.state.filters, function(id, filter) {
14486 parts.push(id + '=$filter(' + self.escape(filter) + ')');
14488 if (parts.length) return 'var ' + parts.join(',') + ';';
14492 varsPrefix: function(section) {
14493 return this.state[section].vars.length ? 'var ' + this.state[section].vars.join(',') + ';' : '';
14496 body: function(section) {
14497 return this.state[section].body.join('');
14500 recurse: function(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck) {
14501 var left, right, self = this, args, expression;
14502 recursionFn = recursionFn || noop;
14503 if (!skipWatchIdCheck && isDefined(ast.watchId)) {
14504 intoId = intoId || this.nextId();
14506 this.lazyAssign(intoId, this.computedMember('i', ast.watchId)),
14507 this.lazyRecurse(ast, intoId, nameId, recursionFn, create, true)
14511 switch (ast.type) {
14513 forEach(ast.body, function(expression, pos) {
14514 self.recurse(expression.expression, undefined, undefined, function(expr) { right = expr; });
14515 if (pos !== ast.body.length - 1) {
14516 self.current().body.push(right, ';');
14518 self.return_(right);
14523 expression = this.escape(ast.value);
14524 this.assign(intoId, expression);
14525 recursionFn(expression);
14527 case AST.UnaryExpression:
14528 this.recurse(ast.argument, undefined, undefined, function(expr) { right = expr; });
14529 expression = ast.operator + '(' + this.ifDefined(right, 0) + ')';
14530 this.assign(intoId, expression);
14531 recursionFn(expression);
14533 case AST.BinaryExpression:
14534 this.recurse(ast.left, undefined, undefined, function(expr) { left = expr; });
14535 this.recurse(ast.right, undefined, undefined, function(expr) { right = expr; });
14536 if (ast.operator === '+') {
14537 expression = this.plus(left, right);
14538 } else if (ast.operator === '-') {
14539 expression = this.ifDefined(left, 0) + ast.operator + this.ifDefined(right, 0);
14541 expression = '(' + left + ')' + ast.operator + '(' + right + ')';
14543 this.assign(intoId, expression);
14544 recursionFn(expression);
14546 case AST.LogicalExpression:
14547 intoId = intoId || this.nextId();
14548 self.recurse(ast.left, intoId);
14549 self.if_(ast.operator === '&&' ? intoId : self.not(intoId), self.lazyRecurse(ast.right, intoId));
14550 recursionFn(intoId);
14552 case AST.ConditionalExpression:
14553 intoId = intoId || this.nextId();
14554 self.recurse(ast.test, intoId);
14555 self.if_(intoId, self.lazyRecurse(ast.alternate, intoId), self.lazyRecurse(ast.consequent, intoId));
14556 recursionFn(intoId);
14558 case AST.Identifier:
14559 intoId = intoId || this.nextId();
14561 nameId.context = self.stage === 'inputs' ? 's' : this.assign(this.nextId(), this.getHasOwnProperty('l', ast.name) + '?l:s');
14562 nameId.computed = false;
14563 nameId.name = ast.name;
14565 ensureSafeMemberName(ast.name);
14566 self.if_(self.stage === 'inputs' || self.not(self.getHasOwnProperty('l', ast.name)),
14568 self.if_(self.stage === 'inputs' || 's', function() {
14569 if (create && create !== 1) {
14571 self.not(self.nonComputedMember('s', ast.name)),
14572 self.lazyAssign(self.nonComputedMember('s', ast.name), '{}'));
14574 self.assign(intoId, self.nonComputedMember('s', ast.name));
14576 }, intoId && self.lazyAssign(intoId, self.nonComputedMember('l', ast.name))
14578 if (self.state.expensiveChecks || isPossiblyDangerousMemberName(ast.name)) {
14579 self.addEnsureSafeObject(intoId);
14581 recursionFn(intoId);
14583 case AST.MemberExpression:
14584 left = nameId && (nameId.context = this.nextId()) || this.nextId();
14585 intoId = intoId || this.nextId();
14586 self.recurse(ast.object, left, undefined, function() {
14587 self.if_(self.notNull(left), function() {
14588 if (create && create !== 1) {
14589 self.addEnsureSafeAssignContext(left);
14591 if (ast.computed) {
14592 right = self.nextId();
14593 self.recurse(ast.property, right);
14594 self.getStringValue(right);
14595 self.addEnsureSafeMemberName(right);
14596 if (create && create !== 1) {
14597 self.if_(self.not(self.computedMember(left, right)), self.lazyAssign(self.computedMember(left, right), '{}'));
14599 expression = self.ensureSafeObject(self.computedMember(left, right));
14600 self.assign(intoId, expression);
14602 nameId.computed = true;
14603 nameId.name = right;
14606 ensureSafeMemberName(ast.property.name);
14607 if (create && create !== 1) {
14608 self.if_(self.not(self.nonComputedMember(left, ast.property.name)), self.lazyAssign(self.nonComputedMember(left, ast.property.name), '{}'));
14610 expression = self.nonComputedMember(left, ast.property.name);
14611 if (self.state.expensiveChecks || isPossiblyDangerousMemberName(ast.property.name)) {
14612 expression = self.ensureSafeObject(expression);
14614 self.assign(intoId, expression);
14616 nameId.computed = false;
14617 nameId.name = ast.property.name;
14621 self.assign(intoId, 'undefined');
14623 recursionFn(intoId);
14626 case AST.CallExpression:
14627 intoId = intoId || this.nextId();
14629 right = self.filter(ast.callee.name);
14631 forEach(ast.arguments, function(expr) {
14632 var argument = self.nextId();
14633 self.recurse(expr, argument);
14634 args.push(argument);
14636 expression = right + '(' + args.join(',') + ')';
14637 self.assign(intoId, expression);
14638 recursionFn(intoId);
14640 right = self.nextId();
14643 self.recurse(ast.callee, right, left, function() {
14644 self.if_(self.notNull(right), function() {
14645 self.addEnsureSafeFunction(right);
14646 forEach(ast.arguments, function(expr) {
14647 self.recurse(expr, self.nextId(), undefined, function(argument) {
14648 args.push(self.ensureSafeObject(argument));
14652 if (!self.state.expensiveChecks) {
14653 self.addEnsureSafeObject(left.context);
14655 expression = self.member(left.context, left.name, left.computed) + '(' + args.join(',') + ')';
14657 expression = right + '(' + args.join(',') + ')';
14659 expression = self.ensureSafeObject(expression);
14660 self.assign(intoId, expression);
14662 self.assign(intoId, 'undefined');
14664 recursionFn(intoId);
14668 case AST.AssignmentExpression:
14669 right = this.nextId();
14671 if (!isAssignable(ast.left)) {
14672 throw $parseMinErr('lval', 'Trying to assign a value to a non l-value');
14674 this.recurse(ast.left, undefined, left, function() {
14675 self.if_(self.notNull(left.context), function() {
14676 self.recurse(ast.right, right);
14677 self.addEnsureSafeObject(self.member(left.context, left.name, left.computed));
14678 self.addEnsureSafeAssignContext(left.context);
14679 expression = self.member(left.context, left.name, left.computed) + ast.operator + right;
14680 self.assign(intoId, expression);
14681 recursionFn(intoId || expression);
14685 case AST.ArrayExpression:
14687 forEach(ast.elements, function(expr) {
14688 self.recurse(expr, self.nextId(), undefined, function(argument) {
14689 args.push(argument);
14692 expression = '[' + args.join(',') + ']';
14693 this.assign(intoId, expression);
14694 recursionFn(expression);
14696 case AST.ObjectExpression:
14698 forEach(ast.properties, function(property) {
14699 self.recurse(property.value, self.nextId(), undefined, function(expr) {
14700 args.push(self.escape(
14701 property.key.type === AST.Identifier ? property.key.name :
14702 ('' + property.key.value)) +
14706 expression = '{' + args.join(',') + '}';
14707 this.assign(intoId, expression);
14708 recursionFn(expression);
14710 case AST.ThisExpression:
14711 this.assign(intoId, 's');
14714 case AST.LocalsExpression:
14715 this.assign(intoId, 'l');
14718 case AST.NGValueParameter:
14719 this.assign(intoId, 'v');
14725 getHasOwnProperty: function(element, property) {
14726 var key = element + '.' + property;
14727 var own = this.current().own;
14728 if (!own.hasOwnProperty(key)) {
14729 own[key] = this.nextId(false, element + '&&(' + this.escape(property) + ' in ' + element + ')');
14734 assign: function(id, value) {
14736 this.current().body.push(id, '=', value, ';');
14740 filter: function(filterName) {
14741 if (!this.state.filters.hasOwnProperty(filterName)) {
14742 this.state.filters[filterName] = this.nextId(true);
14744 return this.state.filters[filterName];
14747 ifDefined: function(id, defaultValue) {
14748 return 'ifDefined(' + id + ',' + this.escape(defaultValue) + ')';
14751 plus: function(left, right) {
14752 return 'plus(' + left + ',' + right + ')';
14755 return_: function(id) {
14756 this.current().body.push('return ', id, ';');
14759 if_: function(test, alternate, consequent) {
14760 if (test === true) {
14763 var body = this.current().body;
14764 body.push('if(', test, '){');
14768 body.push('else{');
14775 not: function(expression) {
14776 return '!(' + expression + ')';
14779 notNull: function(expression) {
14780 return expression + '!=null';
14783 nonComputedMember: function(left, right) {
14784 var SAFE_IDENTIFIER = /[$_a-zA-Z][$_a-zA-Z0-9]*/;
14785 var UNSAFE_CHARACTERS = /[^$_a-zA-Z0-9]/g;
14786 if (SAFE_IDENTIFIER.test(right)) {
14787 return left + '.' + right;
14789 return left + '["' + right.replace(UNSAFE_CHARACTERS, this.stringEscapeFn) + '"]';
14793 computedMember: function(left, right) {
14794 return left + '[' + right + ']';
14797 member: function(left, right, computed) {
14798 if (computed) return this.computedMember(left, right);
14799 return this.nonComputedMember(left, right);
14802 addEnsureSafeObject: function(item) {
14803 this.current().body.push(this.ensureSafeObject(item), ';');
14806 addEnsureSafeMemberName: function(item) {
14807 this.current().body.push(this.ensureSafeMemberName(item), ';');
14810 addEnsureSafeFunction: function(item) {
14811 this.current().body.push(this.ensureSafeFunction(item), ';');
14814 addEnsureSafeAssignContext: function(item) {
14815 this.current().body.push(this.ensureSafeAssignContext(item), ';');
14818 ensureSafeObject: function(item) {
14819 return 'ensureSafeObject(' + item + ',text)';
14822 ensureSafeMemberName: function(item) {
14823 return 'ensureSafeMemberName(' + item + ',text)';
14826 ensureSafeFunction: function(item) {
14827 return 'ensureSafeFunction(' + item + ',text)';
14830 getStringValue: function(item) {
14831 this.assign(item, 'getStringValue(' + item + ')');
14834 ensureSafeAssignContext: function(item) {
14835 return 'ensureSafeAssignContext(' + item + ',text)';
14838 lazyRecurse: function(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck) {
14840 return function() {
14841 self.recurse(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck);
14845 lazyAssign: function(id, value) {
14847 return function() {
14848 self.assign(id, value);
14852 stringEscapeRegex: /[^ a-zA-Z0-9]/g,
14854 stringEscapeFn: function(c) {
14855 return '\\u' + ('0000' + c.charCodeAt(0).toString(16)).slice(-4);
14858 escape: function(value) {
14859 if (isString(value)) return "'" + value.replace(this.stringEscapeRegex, this.stringEscapeFn) + "'";
14860 if (isNumber(value)) return value.toString();
14861 if (value === true) return 'true';
14862 if (value === false) return 'false';
14863 if (value === null) return 'null';
14864 if (typeof value === 'undefined') return 'undefined';
14866 throw $parseMinErr('esc', 'IMPOSSIBLE');
14869 nextId: function(skip, init) {
14870 var id = 'v' + (this.state.nextId++);
14872 this.current().vars.push(id + (init ? '=' + init : ''));
14877 current: function() {
14878 return this.state[this.state.computing];
14883 function ASTInterpreter(astBuilder, $filter) {
14884 this.astBuilder = astBuilder;
14885 this.$filter = $filter;
14888 ASTInterpreter.prototype = {
14889 compile: function(expression, expensiveChecks) {
14891 var ast = this.astBuilder.ast(expression);
14892 this.expression = expression;
14893 this.expensiveChecks = expensiveChecks;
14894 findConstantAndWatchExpressions(ast, self.$filter);
14897 if ((assignable = assignableAST(ast))) {
14898 assign = this.recurse(assignable);
14900 var toWatch = getInputs(ast.body);
14904 forEach(toWatch, function(watch, key) {
14905 var input = self.recurse(watch);
14906 watch.input = input;
14907 inputs.push(input);
14908 watch.watchId = key;
14911 var expressions = [];
14912 forEach(ast.body, function(expression) {
14913 expressions.push(self.recurse(expression.expression));
14915 var fn = ast.body.length === 0 ? noop :
14916 ast.body.length === 1 ? expressions[0] :
14917 function(scope, locals) {
14919 forEach(expressions, function(exp) {
14920 lastValue = exp(scope, locals);
14925 fn.assign = function(scope, value, locals) {
14926 return assign(scope, locals, value);
14930 fn.inputs = inputs;
14932 fn.literal = isLiteral(ast);
14933 fn.constant = isConstant(ast);
14937 recurse: function(ast, context, create) {
14938 var left, right, self = this, args, expression;
14940 return this.inputs(ast.input, ast.watchId);
14942 switch (ast.type) {
14944 return this.value(ast.value, context);
14945 case AST.UnaryExpression:
14946 right = this.recurse(ast.argument);
14947 return this['unary' + ast.operator](right, context);
14948 case AST.BinaryExpression:
14949 left = this.recurse(ast.left);
14950 right = this.recurse(ast.right);
14951 return this['binary' + ast.operator](left, right, context);
14952 case AST.LogicalExpression:
14953 left = this.recurse(ast.left);
14954 right = this.recurse(ast.right);
14955 return this['binary' + ast.operator](left, right, context);
14956 case AST.ConditionalExpression:
14957 return this['ternary?:'](
14958 this.recurse(ast.test),
14959 this.recurse(ast.alternate),
14960 this.recurse(ast.consequent),
14963 case AST.Identifier:
14964 ensureSafeMemberName(ast.name, self.expression);
14965 return self.identifier(ast.name,
14966 self.expensiveChecks || isPossiblyDangerousMemberName(ast.name),
14967 context, create, self.expression);
14968 case AST.MemberExpression:
14969 left = this.recurse(ast.object, false, !!create);
14970 if (!ast.computed) {
14971 ensureSafeMemberName(ast.property.name, self.expression);
14972 right = ast.property.name;
14974 if (ast.computed) right = this.recurse(ast.property);
14975 return ast.computed ?
14976 this.computedMember(left, right, context, create, self.expression) :
14977 this.nonComputedMember(left, right, self.expensiveChecks, context, create, self.expression);
14978 case AST.CallExpression:
14980 forEach(ast.arguments, function(expr) {
14981 args.push(self.recurse(expr));
14983 if (ast.filter) right = this.$filter(ast.callee.name);
14984 if (!ast.filter) right = this.recurse(ast.callee, true);
14985 return ast.filter ?
14986 function(scope, locals, assign, inputs) {
14988 for (var i = 0; i < args.length; ++i) {
14989 values.push(args[i](scope, locals, assign, inputs));
14991 var value = right.apply(undefined, values, inputs);
14992 return context ? {context: undefined, name: undefined, value: value} : value;
14994 function(scope, locals, assign, inputs) {
14995 var rhs = right(scope, locals, assign, inputs);
14997 if (rhs.value != null) {
14998 ensureSafeObject(rhs.context, self.expression);
14999 ensureSafeFunction(rhs.value, self.expression);
15001 for (var i = 0; i < args.length; ++i) {
15002 values.push(ensureSafeObject(args[i](scope, locals, assign, inputs), self.expression));
15004 value = ensureSafeObject(rhs.value.apply(rhs.context, values), self.expression);
15006 return context ? {value: value} : value;
15008 case AST.AssignmentExpression:
15009 left = this.recurse(ast.left, true, 1);
15010 right = this.recurse(ast.right);
15011 return function(scope, locals, assign, inputs) {
15012 var lhs = left(scope, locals, assign, inputs);
15013 var rhs = right(scope, locals, assign, inputs);
15014 ensureSafeObject(lhs.value, self.expression);
15015 ensureSafeAssignContext(lhs.context);
15016 lhs.context[lhs.name] = rhs;
15017 return context ? {value: rhs} : rhs;
15019 case AST.ArrayExpression:
15021 forEach(ast.elements, function(expr) {
15022 args.push(self.recurse(expr));
15024 return function(scope, locals, assign, inputs) {
15026 for (var i = 0; i < args.length; ++i) {
15027 value.push(args[i](scope, locals, assign, inputs));
15029 return context ? {value: value} : value;
15031 case AST.ObjectExpression:
15033 forEach(ast.properties, function(property) {
15034 args.push({key: property.key.type === AST.Identifier ?
15035 property.key.name :
15036 ('' + property.key.value),
15037 value: self.recurse(property.value)
15040 return function(scope, locals, assign, inputs) {
15042 for (var i = 0; i < args.length; ++i) {
15043 value[args[i].key] = args[i].value(scope, locals, assign, inputs);
15045 return context ? {value: value} : value;
15047 case AST.ThisExpression:
15048 return function(scope) {
15049 return context ? {value: scope} : scope;
15051 case AST.LocalsExpression:
15052 return function(scope, locals) {
15053 return context ? {value: locals} : locals;
15055 case AST.NGValueParameter:
15056 return function(scope, locals, assign) {
15057 return context ? {value: assign} : assign;
15062 'unary+': function(argument, context) {
15063 return function(scope, locals, assign, inputs) {
15064 var arg = argument(scope, locals, assign, inputs);
15065 if (isDefined(arg)) {
15070 return context ? {value: arg} : arg;
15073 'unary-': function(argument, context) {
15074 return function(scope, locals, assign, inputs) {
15075 var arg = argument(scope, locals, assign, inputs);
15076 if (isDefined(arg)) {
15081 return context ? {value: arg} : arg;
15084 'unary!': function(argument, context) {
15085 return function(scope, locals, assign, inputs) {
15086 var arg = !argument(scope, locals, assign, inputs);
15087 return context ? {value: arg} : arg;
15090 'binary+': function(left, right, context) {
15091 return function(scope, locals, assign, inputs) {
15092 var lhs = left(scope, locals, assign, inputs);
15093 var rhs = right(scope, locals, assign, inputs);
15094 var arg = plusFn(lhs, rhs);
15095 return context ? {value: arg} : arg;
15098 'binary-': function(left, right, context) {
15099 return function(scope, locals, assign, inputs) {
15100 var lhs = left(scope, locals, assign, inputs);
15101 var rhs = right(scope, locals, assign, inputs);
15102 var arg = (isDefined(lhs) ? lhs : 0) - (isDefined(rhs) ? rhs : 0);
15103 return context ? {value: arg} : arg;
15106 'binary*': function(left, right, context) {
15107 return function(scope, locals, assign, inputs) {
15108 var arg = left(scope, locals, assign, inputs) * right(scope, locals, assign, inputs);
15109 return context ? {value: arg} : arg;
15112 'binary/': function(left, right, context) {
15113 return function(scope, locals, assign, inputs) {
15114 var arg = left(scope, locals, assign, inputs) / right(scope, locals, assign, inputs);
15115 return context ? {value: arg} : arg;
15118 'binary%': function(left, right, context) {
15119 return function(scope, locals, assign, inputs) {
15120 var arg = left(scope, locals, assign, inputs) % right(scope, locals, assign, inputs);
15121 return context ? {value: arg} : arg;
15124 'binary===': function(left, right, context) {
15125 return function(scope, locals, assign, inputs) {
15126 var arg = left(scope, locals, assign, inputs) === right(scope, locals, assign, inputs);
15127 return context ? {value: arg} : arg;
15130 'binary!==': function(left, right, context) {
15131 return function(scope, locals, assign, inputs) {
15132 var arg = left(scope, locals, assign, inputs) !== right(scope, locals, assign, inputs);
15133 return context ? {value: arg} : arg;
15136 'binary==': function(left, right, context) {
15137 return function(scope, locals, assign, inputs) {
15138 var arg = left(scope, locals, assign, inputs) == right(scope, locals, assign, inputs);
15139 return context ? {value: arg} : arg;
15142 'binary!=': function(left, right, context) {
15143 return function(scope, locals, assign, inputs) {
15144 var arg = left(scope, locals, assign, inputs) != right(scope, locals, assign, inputs);
15145 return context ? {value: arg} : arg;
15148 'binary<': function(left, right, context) {
15149 return function(scope, locals, assign, inputs) {
15150 var arg = left(scope, locals, assign, inputs) < right(scope, locals, assign, inputs);
15151 return context ? {value: arg} : arg;
15154 'binary>': function(left, right, context) {
15155 return function(scope, locals, assign, inputs) {
15156 var arg = left(scope, locals, assign, inputs) > right(scope, locals, assign, inputs);
15157 return context ? {value: arg} : arg;
15160 'binary<=': function(left, right, context) {
15161 return function(scope, locals, assign, inputs) {
15162 var arg = left(scope, locals, assign, inputs) <= right(scope, locals, assign, inputs);
15163 return context ? {value: arg} : arg;
15166 'binary>=': function(left, right, context) {
15167 return function(scope, locals, assign, inputs) {
15168 var arg = left(scope, locals, assign, inputs) >= right(scope, locals, assign, inputs);
15169 return context ? {value: arg} : arg;
15172 'binary&&': function(left, right, context) {
15173 return function(scope, locals, assign, inputs) {
15174 var arg = left(scope, locals, assign, inputs) && right(scope, locals, assign, inputs);
15175 return context ? {value: arg} : arg;
15178 'binary||': function(left, right, context) {
15179 return function(scope, locals, assign, inputs) {
15180 var arg = left(scope, locals, assign, inputs) || right(scope, locals, assign, inputs);
15181 return context ? {value: arg} : arg;
15184 'ternary?:': function(test, alternate, consequent, context) {
15185 return function(scope, locals, assign, inputs) {
15186 var arg = test(scope, locals, assign, inputs) ? alternate(scope, locals, assign, inputs) : consequent(scope, locals, assign, inputs);
15187 return context ? {value: arg} : arg;
15190 value: function(value, context) {
15191 return function() { return context ? {context: undefined, name: undefined, value: value} : value; };
15193 identifier: function(name, expensiveChecks, context, create, expression) {
15194 return function(scope, locals, assign, inputs) {
15195 var base = locals && (name in locals) ? locals : scope;
15196 if (create && create !== 1 && base && !(base[name])) {
15199 var value = base ? base[name] : undefined;
15200 if (expensiveChecks) {
15201 ensureSafeObject(value, expression);
15204 return {context: base, name: name, value: value};
15210 computedMember: function(left, right, context, create, expression) {
15211 return function(scope, locals, assign, inputs) {
15212 var lhs = left(scope, locals, assign, inputs);
15216 rhs = right(scope, locals, assign, inputs);
15217 rhs = getStringValue(rhs);
15218 ensureSafeMemberName(rhs, expression);
15219 if (create && create !== 1) {
15220 ensureSafeAssignContext(lhs);
15221 if (lhs && !(lhs[rhs])) {
15226 ensureSafeObject(value, expression);
15229 return {context: lhs, name: rhs, value: value};
15235 nonComputedMember: function(left, right, expensiveChecks, context, create, expression) {
15236 return function(scope, locals, assign, inputs) {
15237 var lhs = left(scope, locals, assign, inputs);
15238 if (create && create !== 1) {
15239 ensureSafeAssignContext(lhs);
15240 if (lhs && !(lhs[right])) {
15244 var value = lhs != null ? lhs[right] : undefined;
15245 if (expensiveChecks || isPossiblyDangerousMemberName(right)) {
15246 ensureSafeObject(value, expression);
15249 return {context: lhs, name: right, value: value};
15255 inputs: function(input, watchId) {
15256 return function(scope, value, locals, inputs) {
15257 if (inputs) return inputs[watchId];
15258 return input(scope, value, locals);
15266 var Parser = function(lexer, $filter, options) {
15267 this.lexer = lexer;
15268 this.$filter = $filter;
15269 this.options = options;
15270 this.ast = new AST(lexer, options);
15271 this.astCompiler = options.csp ? new ASTInterpreter(this.ast, $filter) :
15272 new ASTCompiler(this.ast, $filter);
15275 Parser.prototype = {
15276 constructor: Parser,
15278 parse: function(text) {
15279 return this.astCompiler.compile(text, this.options.expensiveChecks);
15283 function isPossiblyDangerousMemberName(name) {
15284 return name == 'constructor';
15287 var objectValueOf = Object.prototype.valueOf;
15289 function getValueOf(value) {
15290 return isFunction(value.valueOf) ? value.valueOf() : objectValueOf.call(value);
15293 ///////////////////////////////////
15302 * Converts Angular {@link guide/expression expression} into a function.
15305 * var getter = $parse('user.name');
15306 * var setter = getter.assign;
15307 * var context = {user:{name:'angular'}};
15308 * var locals = {user:{name:'local'}};
15310 * expect(getter(context)).toEqual('angular');
15311 * setter(context, 'newValue');
15312 * expect(context.user.name).toEqual('newValue');
15313 * expect(getter(context, locals)).toEqual('local');
15317 * @param {string} expression String expression to compile.
15318 * @returns {function(context, locals)} a function which represents the compiled expression:
15320 * * `context` – `{object}` – an object against which any expressions embedded in the strings
15321 * are evaluated against (typically a scope object).
15322 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
15325 * The returned function also has the following properties:
15326 * * `literal` – `{boolean}` – whether the expression's top-level node is a JavaScript
15328 * * `constant` – `{boolean}` – whether the expression is made entirely of JavaScript
15329 * constant literals.
15330 * * `assign` – `{?function(context, value)}` – if the expression is assignable, this will be
15331 * set to a function to change its value on the given context.
15338 * @name $parseProvider
15341 * `$parseProvider` can be used for configuring the default behavior of the {@link ng.$parse $parse}
15344 function $ParseProvider() {
15345 var cacheDefault = createMap();
15346 var cacheExpensive = createMap();
15351 'undefined': undefined
15353 var identStart, identContinue;
15357 * @name $parseProvider#addLiteral
15360 * Configure $parse service to add literal values that will be present as literal at expressions.
15362 * @param {string} literalName Token for the literal value. The literal name value must be a valid literal name.
15363 * @param {*} literalValue Value for this literal. All literal values must be primitives or `undefined`.
15366 this.addLiteral = function(literalName, literalValue) {
15367 literals[literalName] = literalValue;
15372 * @name $parseProvider#setIdentifierFns
15375 * Allows defining the set of characters that are allowed in Angular expressions. The function
15376 * `identifierStart` will get called to know if a given character is a valid character to be the
15377 * first character for an identifier. The function `identifierContinue` will get called to know if
15378 * a given character is a valid character to be a follow-up identifier character. The functions
15379 * `identifierStart` and `identifierContinue` will receive as arguments the single character to be
15380 * identifier and the character code point. These arguments will be `string` and `numeric`. Keep in
15381 * mind that the `string` parameter can be two characters long depending on the character
15382 * representation. It is expected for the function to return `true` or `false`, whether that
15383 * character is allowed or not.
15385 * Since this function will be called extensivelly, keep the implementation of these functions fast,
15386 * as the performance of these functions have a direct impact on the expressions parsing speed.
15388 * @param {function=} identifierStart The function that will decide whether the given character is
15389 * a valid identifier start character.
15390 * @param {function=} identifierContinue The function that will decide whether the given character is
15391 * a valid identifier continue character.
15393 this.setIdentifierFns = function(identifierStart, identifierContinue) {
15394 identStart = identifierStart;
15395 identContinue = identifierContinue;
15399 this.$get = ['$filter', function($filter) {
15400 var noUnsafeEval = csp().noUnsafeEval;
15401 var $parseOptions = {
15403 expensiveChecks: false,
15404 literals: copy(literals),
15405 isIdentifierStart: isFunction(identStart) && identStart,
15406 isIdentifierContinue: isFunction(identContinue) && identContinue
15408 $parseOptionsExpensive = {
15410 expensiveChecks: true,
15411 literals: copy(literals),
15412 isIdentifierStart: isFunction(identStart) && identStart,
15413 isIdentifierContinue: isFunction(identContinue) && identContinue
15415 var runningChecksEnabled = false;
15417 $parse.$$runningExpensiveChecks = function() {
15418 return runningChecksEnabled;
15423 function $parse(exp, interceptorFn, expensiveChecks) {
15424 var parsedExpression, oneTime, cacheKey;
15426 expensiveChecks = expensiveChecks || runningChecksEnabled;
15428 switch (typeof exp) {
15433 var cache = (expensiveChecks ? cacheExpensive : cacheDefault);
15434 parsedExpression = cache[cacheKey];
15436 if (!parsedExpression) {
15437 if (exp.charAt(0) === ':' && exp.charAt(1) === ':') {
15439 exp = exp.substring(2);
15441 var parseOptions = expensiveChecks ? $parseOptionsExpensive : $parseOptions;
15442 var lexer = new Lexer(parseOptions);
15443 var parser = new Parser(lexer, $filter, parseOptions);
15444 parsedExpression = parser.parse(exp);
15445 if (parsedExpression.constant) {
15446 parsedExpression.$$watchDelegate = constantWatchDelegate;
15447 } else if (oneTime) {
15448 parsedExpression.$$watchDelegate = parsedExpression.literal ?
15449 oneTimeLiteralWatchDelegate : oneTimeWatchDelegate;
15450 } else if (parsedExpression.inputs) {
15451 parsedExpression.$$watchDelegate = inputsWatchDelegate;
15453 if (expensiveChecks) {
15454 parsedExpression = expensiveChecksInterceptor(parsedExpression);
15456 cache[cacheKey] = parsedExpression;
15458 return addInterceptor(parsedExpression, interceptorFn);
15461 return addInterceptor(exp, interceptorFn);
15464 return addInterceptor(noop, interceptorFn);
15468 function expensiveChecksInterceptor(fn) {
15469 if (!fn) return fn;
15470 expensiveCheckFn.$$watchDelegate = fn.$$watchDelegate;
15471 expensiveCheckFn.assign = expensiveChecksInterceptor(fn.assign);
15472 expensiveCheckFn.constant = fn.constant;
15473 expensiveCheckFn.literal = fn.literal;
15474 for (var i = 0; fn.inputs && i < fn.inputs.length; ++i) {
15475 fn.inputs[i] = expensiveChecksInterceptor(fn.inputs[i]);
15477 expensiveCheckFn.inputs = fn.inputs;
15479 return expensiveCheckFn;
15481 function expensiveCheckFn(scope, locals, assign, inputs) {
15482 var expensiveCheckOldValue = runningChecksEnabled;
15483 runningChecksEnabled = true;
15485 return fn(scope, locals, assign, inputs);
15487 runningChecksEnabled = expensiveCheckOldValue;
15492 function expressionInputDirtyCheck(newValue, oldValueOfValue) {
15494 if (newValue == null || oldValueOfValue == null) { // null/undefined
15495 return newValue === oldValueOfValue;
15498 if (typeof newValue === 'object') {
15500 // attempt to convert the value to a primitive type
15501 // TODO(docs): add a note to docs that by implementing valueOf even objects and arrays can
15502 // be cheaply dirty-checked
15503 newValue = getValueOf(newValue);
15505 if (typeof newValue === 'object') {
15506 // objects/arrays are not supported - deep-watching them would be too expensive
15510 // fall-through to the primitive equality check
15514 return newValue === oldValueOfValue || (newValue !== newValue && oldValueOfValue !== oldValueOfValue);
15517 function inputsWatchDelegate(scope, listener, objectEquality, parsedExpression, prettyPrintExpression) {
15518 var inputExpressions = parsedExpression.inputs;
15521 if (inputExpressions.length === 1) {
15522 var oldInputValueOf = expressionInputDirtyCheck; // init to something unique so that equals check fails
15523 inputExpressions = inputExpressions[0];
15524 return scope.$watch(function expressionInputWatch(scope) {
15525 var newInputValue = inputExpressions(scope);
15526 if (!expressionInputDirtyCheck(newInputValue, oldInputValueOf)) {
15527 lastResult = parsedExpression(scope, undefined, undefined, [newInputValue]);
15528 oldInputValueOf = newInputValue && getValueOf(newInputValue);
15531 }, listener, objectEquality, prettyPrintExpression);
15534 var oldInputValueOfValues = [];
15535 var oldInputValues = [];
15536 for (var i = 0, ii = inputExpressions.length; i < ii; i++) {
15537 oldInputValueOfValues[i] = expressionInputDirtyCheck; // init to something unique so that equals check fails
15538 oldInputValues[i] = null;
15541 return scope.$watch(function expressionInputsWatch(scope) {
15542 var changed = false;
15544 for (var i = 0, ii = inputExpressions.length; i < ii; i++) {
15545 var newInputValue = inputExpressions[i](scope);
15546 if (changed || (changed = !expressionInputDirtyCheck(newInputValue, oldInputValueOfValues[i]))) {
15547 oldInputValues[i] = newInputValue;
15548 oldInputValueOfValues[i] = newInputValue && getValueOf(newInputValue);
15553 lastResult = parsedExpression(scope, undefined, undefined, oldInputValues);
15557 }, listener, objectEquality, prettyPrintExpression);
15560 function oneTimeWatchDelegate(scope, listener, objectEquality, parsedExpression) {
15561 var unwatch, lastValue;
15562 return unwatch = scope.$watch(function oneTimeWatch(scope) {
15563 return parsedExpression(scope);
15564 }, function oneTimeListener(value, old, scope) {
15566 if (isFunction(listener)) {
15567 listener.apply(this, arguments);
15569 if (isDefined(value)) {
15570 scope.$$postDigest(function() {
15571 if (isDefined(lastValue)) {
15576 }, objectEquality);
15579 function oneTimeLiteralWatchDelegate(scope, listener, objectEquality, parsedExpression) {
15580 var unwatch, lastValue;
15581 return unwatch = scope.$watch(function oneTimeWatch(scope) {
15582 return parsedExpression(scope);
15583 }, function oneTimeListener(value, old, scope) {
15585 if (isFunction(listener)) {
15586 listener.call(this, value, old, scope);
15588 if (isAllDefined(value)) {
15589 scope.$$postDigest(function() {
15590 if (isAllDefined(lastValue)) unwatch();
15593 }, objectEquality);
15595 function isAllDefined(value) {
15596 var allDefined = true;
15597 forEach(value, function(val) {
15598 if (!isDefined(val)) allDefined = false;
15604 function constantWatchDelegate(scope, listener, objectEquality, parsedExpression) {
15606 return unwatch = scope.$watch(function constantWatch(scope) {
15608 return parsedExpression(scope);
15609 }, listener, objectEquality);
15612 function addInterceptor(parsedExpression, interceptorFn) {
15613 if (!interceptorFn) return parsedExpression;
15614 var watchDelegate = parsedExpression.$$watchDelegate;
15615 var useInputs = false;
15618 watchDelegate !== oneTimeLiteralWatchDelegate &&
15619 watchDelegate !== oneTimeWatchDelegate;
15621 var fn = regularWatch ? function regularInterceptedExpression(scope, locals, assign, inputs) {
15622 var value = useInputs && inputs ? inputs[0] : parsedExpression(scope, locals, assign, inputs);
15623 return interceptorFn(value, scope, locals);
15624 } : function oneTimeInterceptedExpression(scope, locals, assign, inputs) {
15625 var value = parsedExpression(scope, locals, assign, inputs);
15626 var result = interceptorFn(value, scope, locals);
15627 // we only return the interceptor's result if the
15628 // initial value is defined (for bind-once)
15629 return isDefined(value) ? result : value;
15632 // Propagate $$watchDelegates other then inputsWatchDelegate
15633 if (parsedExpression.$$watchDelegate &&
15634 parsedExpression.$$watchDelegate !== inputsWatchDelegate) {
15635 fn.$$watchDelegate = parsedExpression.$$watchDelegate;
15636 } else if (!interceptorFn.$stateful) {
15637 // If there is an interceptor, but no watchDelegate then treat the interceptor like
15638 // we treat filters - it is assumed to be a pure function unless flagged with $stateful
15639 fn.$$watchDelegate = inputsWatchDelegate;
15640 useInputs = !parsedExpression.inputs;
15641 fn.inputs = parsedExpression.inputs ? parsedExpression.inputs : [parsedExpression];
15652 * @requires $rootScope
15655 * A service that helps you run functions asynchronously, and use their return values (or exceptions)
15656 * when they are done processing.
15658 * This is an implementation of promises/deferred objects inspired by
15659 * [Kris Kowal's Q](https://github.com/kriskowal/q).
15661 * $q can be used in two fashions --- one which is more similar to Kris Kowal's Q or jQuery's Deferred
15662 * implementations, and the other which resembles ES6 (ES2015) promises to some degree.
15666 * The streamlined ES6 style promise is essentially just using $q as a constructor which takes a `resolver`
15667 * function as the first argument. This is similar to the native Promise implementation from ES6,
15668 * see [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise).
15670 * While the constructor-style use is supported, not all of the supporting methods from ES6 promises are
15673 * It can be used like so:
15676 * // for the purpose of this example let's assume that variables `$q` and `okToGreet`
15677 * // are available in the current lexical scope (they could have been injected or passed in).
15679 * function asyncGreet(name) {
15680 * // perform some asynchronous operation, resolve or reject the promise when appropriate.
15681 * return $q(function(resolve, reject) {
15682 * setTimeout(function() {
15683 * if (okToGreet(name)) {
15684 * resolve('Hello, ' + name + '!');
15686 * reject('Greeting ' + name + ' is not allowed.');
15692 * var promise = asyncGreet('Robin Hood');
15693 * promise.then(function(greeting) {
15694 * alert('Success: ' + greeting);
15695 * }, function(reason) {
15696 * alert('Failed: ' + reason);
15700 * Note: progress/notify callbacks are not currently supported via the ES6-style interface.
15702 * Note: unlike ES6 behavior, an exception thrown in the constructor function will NOT implicitly reject the promise.
15704 * However, the more traditional CommonJS-style usage is still available, and documented below.
15706 * [The CommonJS Promise proposal](http://wiki.commonjs.org/wiki/Promises) describes a promise as an
15707 * interface for interacting with an object that represents the result of an action that is
15708 * performed asynchronously, and may or may not be finished at any given point in time.
15710 * From the perspective of dealing with error handling, deferred and promise APIs are to
15711 * asynchronous programming what `try`, `catch` and `throw` keywords are to synchronous programming.
15714 * // for the purpose of this example let's assume that variables `$q` and `okToGreet`
15715 * // are available in the current lexical scope (they could have been injected or passed in).
15717 * function asyncGreet(name) {
15718 * var deferred = $q.defer();
15720 * setTimeout(function() {
15721 * deferred.notify('About to greet ' + name + '.');
15723 * if (okToGreet(name)) {
15724 * deferred.resolve('Hello, ' + name + '!');
15726 * deferred.reject('Greeting ' + name + ' is not allowed.');
15730 * return deferred.promise;
15733 * var promise = asyncGreet('Robin Hood');
15734 * promise.then(function(greeting) {
15735 * alert('Success: ' + greeting);
15736 * }, function(reason) {
15737 * alert('Failed: ' + reason);
15738 * }, function(update) {
15739 * alert('Got notification: ' + update);
15743 * At first it might not be obvious why this extra complexity is worth the trouble. The payoff
15744 * comes in the way of guarantees that promise and deferred APIs make, see
15745 * https://github.com/kriskowal/uncommonjs/blob/master/promises/specification.md.
15747 * Additionally the promise api allows for composition that is very hard to do with the
15748 * traditional callback ([CPS](http://en.wikipedia.org/wiki/Continuation-passing_style)) approach.
15749 * For more on this please see the [Q documentation](https://github.com/kriskowal/q) especially the
15750 * section on serial or parallel joining of promises.
15752 * # The Deferred API
15754 * A new instance of deferred is constructed by calling `$q.defer()`.
15756 * The purpose of the deferred object is to expose the associated Promise instance as well as APIs
15757 * that can be used for signaling the successful or unsuccessful completion, as well as the status
15762 * - `resolve(value)` – resolves the derived promise with the `value`. If the value is a rejection
15763 * constructed via `$q.reject`, the promise will be rejected instead.
15764 * - `reject(reason)` – rejects the derived promise with the `reason`. This is equivalent to
15765 * resolving it with a rejection constructed via `$q.reject`.
15766 * - `notify(value)` - provides updates on the status of the promise's execution. This may be called
15767 * multiple times before the promise is either resolved or rejected.
15771 * - promise – `{Promise}` – promise object associated with this deferred.
15774 * # The Promise API
15776 * A new promise instance is created when a deferred instance is created and can be retrieved by
15777 * calling `deferred.promise`.
15779 * The purpose of the promise object is to allow for interested parties to get access to the result
15780 * of the deferred task when it completes.
15784 * - `then(successCallback, errorCallback, notifyCallback)` – regardless of when the promise was or
15785 * will be resolved or rejected, `then` calls one of the success or error callbacks asynchronously
15786 * as soon as the result is available. The callbacks are called with a single argument: the result
15787 * or rejection reason. Additionally, the notify callback may be called zero or more times to
15788 * provide a progress indication, before the promise is resolved or rejected.
15790 * This method *returns a new promise* which is resolved or rejected via the return value of the
15791 * `successCallback`, `errorCallback` (unless that value is a promise, in which case it is resolved
15792 * with the value which is resolved in that promise using
15793 * [promise chaining](http://www.html5rocks.com/en/tutorials/es6/promises/#toc-promises-queues)).
15794 * It also notifies via the return value of the `notifyCallback` method. The promise cannot be
15795 * resolved or rejected from the notifyCallback method.
15797 * - `catch(errorCallback)` – shorthand for `promise.then(null, errorCallback)`
15799 * - `finally(callback, notifyCallback)` – allows you to observe either the fulfillment or rejection of a promise,
15800 * but to do so without modifying the final value. This is useful to release resources or do some
15801 * clean-up that needs to be done whether the promise was rejected or resolved. See the [full
15802 * specification](https://github.com/kriskowal/q/wiki/API-Reference#promisefinallycallback) for
15803 * more information.
15805 * # Chaining promises
15807 * Because calling the `then` method of a promise returns a new derived promise, it is easily
15808 * possible to create a chain of promises:
15811 * promiseB = promiseA.then(function(result) {
15812 * return result + 1;
15815 * // promiseB will be resolved immediately after promiseA is resolved and its value
15816 * // will be the result of promiseA incremented by 1
15819 * It is possible to create chains of any length and since a promise can be resolved with another
15820 * promise (which will defer its resolution further), it is possible to pause/defer resolution of
15821 * the promises at any point in the chain. This makes it possible to implement powerful APIs like
15822 * $http's response interceptors.
15825 * # Differences between Kris Kowal's Q and $q
15827 * There are two main differences:
15829 * - $q is integrated with the {@link ng.$rootScope.Scope} Scope model observation
15830 * mechanism in angular, which means faster propagation of resolution or rejection into your
15831 * models and avoiding unnecessary browser repaints, which would result in flickering UI.
15832 * - Q has many more features than $q, but that comes at a cost of bytes. $q is tiny, but contains
15833 * all the important functionality needed for common async tasks.
15838 * it('should simulate promise', inject(function($q, $rootScope) {
15839 * var deferred = $q.defer();
15840 * var promise = deferred.promise;
15841 * var resolvedValue;
15843 * promise.then(function(value) { resolvedValue = value; });
15844 * expect(resolvedValue).toBeUndefined();
15846 * // Simulate resolving of promise
15847 * deferred.resolve(123);
15848 * // Note that the 'then' function does not get called synchronously.
15849 * // This is because we want the promise API to always be async, whether or not
15850 * // it got called synchronously or asynchronously.
15851 * expect(resolvedValue).toBeUndefined();
15853 * // Propagate promise resolution to 'then' functions using $apply().
15854 * $rootScope.$apply();
15855 * expect(resolvedValue).toEqual(123);
15859 * @param {function(function, function)} resolver Function which is responsible for resolving or
15860 * rejecting the newly created promise. The first parameter is a function which resolves the
15861 * promise, the second parameter is a function which rejects the promise.
15863 * @returns {Promise} The newly created promise.
15865 function $QProvider() {
15867 this.$get = ['$rootScope', '$exceptionHandler', function($rootScope, $exceptionHandler) {
15868 return qFactory(function(callback) {
15869 $rootScope.$evalAsync(callback);
15870 }, $exceptionHandler);
15874 function $$QProvider() {
15875 this.$get = ['$browser', '$exceptionHandler', function($browser, $exceptionHandler) {
15876 return qFactory(function(callback) {
15877 $browser.defer(callback);
15878 }, $exceptionHandler);
15883 * Constructs a promise manager.
15885 * @param {function(function)} nextTick Function for executing functions in the next turn.
15886 * @param {function(...*)} exceptionHandler Function into which unexpected exceptions are passed for
15887 * debugging purposes.
15888 * @returns {object} Promise manager.
15890 function qFactory(nextTick, exceptionHandler) {
15891 var $qMinErr = minErr('$q', TypeError);
15895 * @name ng.$q#defer
15899 * Creates a `Deferred` object which represents a task which will finish in the future.
15901 * @returns {Deferred} Returns a new instance of deferred.
15903 var defer = function() {
15904 var d = new Deferred();
15905 //Necessary to support unbound execution :/
15906 d.resolve = simpleBind(d, d.resolve);
15907 d.reject = simpleBind(d, d.reject);
15908 d.notify = simpleBind(d, d.notify);
15912 function Promise() {
15913 this.$$state = { status: 0 };
15916 extend(Promise.prototype, {
15917 then: function(onFulfilled, onRejected, progressBack) {
15918 if (isUndefined(onFulfilled) && isUndefined(onRejected) && isUndefined(progressBack)) {
15921 var result = new Deferred();
15923 this.$$state.pending = this.$$state.pending || [];
15924 this.$$state.pending.push([result, onFulfilled, onRejected, progressBack]);
15925 if (this.$$state.status > 0) scheduleProcessQueue(this.$$state);
15927 return result.promise;
15930 "catch": function(callback) {
15931 return this.then(null, callback);
15934 "finally": function(callback, progressBack) {
15935 return this.then(function(value) {
15936 return handleCallback(value, true, callback);
15937 }, function(error) {
15938 return handleCallback(error, false, callback);
15943 //Faster, more basic than angular.bind http://jsperf.com/angular-bind-vs-custom-vs-native
15944 function simpleBind(context, fn) {
15945 return function(value) {
15946 fn.call(context, value);
15950 function processQueue(state) {
15951 var fn, deferred, pending;
15953 pending = state.pending;
15954 state.processScheduled = false;
15955 state.pending = undefined;
15956 for (var i = 0, ii = pending.length; i < ii; ++i) {
15957 deferred = pending[i][0];
15958 fn = pending[i][state.status];
15960 if (isFunction(fn)) {
15961 deferred.resolve(fn(state.value));
15962 } else if (state.status === 1) {
15963 deferred.resolve(state.value);
15965 deferred.reject(state.value);
15968 deferred.reject(e);
15969 exceptionHandler(e);
15974 function scheduleProcessQueue(state) {
15975 if (state.processScheduled || !state.pending) return;
15976 state.processScheduled = true;
15977 nextTick(function() { processQueue(state); });
15980 function Deferred() {
15981 this.promise = new Promise();
15984 extend(Deferred.prototype, {
15985 resolve: function(val) {
15986 if (this.promise.$$state.status) return;
15987 if (val === this.promise) {
15988 this.$$reject($qMinErr(
15990 "Expected promise to be resolved with value other than itself '{0}'",
15993 this.$$resolve(val);
15998 $$resolve: function(val) {
16003 if ((isObject(val) || isFunction(val))) then = val && val.then;
16004 if (isFunction(then)) {
16005 this.promise.$$state.status = -1;
16006 then.call(val, resolvePromise, rejectPromise, simpleBind(this, this.notify));
16008 this.promise.$$state.value = val;
16009 this.promise.$$state.status = 1;
16010 scheduleProcessQueue(this.promise.$$state);
16014 exceptionHandler(e);
16017 function resolvePromise(val) {
16020 that.$$resolve(val);
16022 function rejectPromise(val) {
16025 that.$$reject(val);
16029 reject: function(reason) {
16030 if (this.promise.$$state.status) return;
16031 this.$$reject(reason);
16034 $$reject: function(reason) {
16035 this.promise.$$state.value = reason;
16036 this.promise.$$state.status = 2;
16037 scheduleProcessQueue(this.promise.$$state);
16040 notify: function(progress) {
16041 var callbacks = this.promise.$$state.pending;
16043 if ((this.promise.$$state.status <= 0) && callbacks && callbacks.length) {
16044 nextTick(function() {
16045 var callback, result;
16046 for (var i = 0, ii = callbacks.length; i < ii; i++) {
16047 result = callbacks[i][0];
16048 callback = callbacks[i][3];
16050 result.notify(isFunction(callback) ? callback(progress) : progress);
16052 exceptionHandler(e);
16066 * Creates a promise that is resolved as rejected with the specified `reason`. This api should be
16067 * used to forward rejection in a chain of promises. If you are dealing with the last promise in
16068 * a promise chain, you don't need to worry about it.
16070 * When comparing deferreds/promises to the familiar behavior of try/catch/throw, think of
16071 * `reject` as the `throw` keyword in JavaScript. This also means that if you "catch" an error via
16072 * a promise error callback and you want to forward the error to the promise derived from the
16073 * current promise, you have to "rethrow" the error by returning a rejection constructed via
16077 * promiseB = promiseA.then(function(result) {
16078 * // success: do something and resolve promiseB
16079 * // with the old or a new result
16081 * }, function(reason) {
16082 * // error: handle the error if possible and
16083 * // resolve promiseB with newPromiseOrValue,
16084 * // otherwise forward the rejection to promiseB
16085 * if (canHandle(reason)) {
16086 * // handle the error and recover
16087 * return newPromiseOrValue;
16089 * return $q.reject(reason);
16093 * @param {*} reason Constant, message, exception or an object representing the rejection reason.
16094 * @returns {Promise} Returns a promise that was already resolved as rejected with the `reason`.
16096 var reject = function(reason) {
16097 var result = new Deferred();
16098 result.reject(reason);
16099 return result.promise;
16102 var makePromise = function makePromise(value, resolved) {
16103 var result = new Deferred();
16105 result.resolve(value);
16107 result.reject(value);
16109 return result.promise;
16112 var handleCallback = function handleCallback(value, isResolved, callback) {
16113 var callbackOutput = null;
16115 if (isFunction(callback)) callbackOutput = callback();
16117 return makePromise(e, false);
16119 if (isPromiseLike(callbackOutput)) {
16120 return callbackOutput.then(function() {
16121 return makePromise(value, isResolved);
16122 }, function(error) {
16123 return makePromise(error, false);
16126 return makePromise(value, isResolved);
16136 * Wraps an object that might be a value or a (3rd party) then-able promise into a $q promise.
16137 * This is useful when you are dealing with an object that might or might not be a promise, or if
16138 * the promise comes from a source that can't be trusted.
16140 * @param {*} value Value or a promise
16141 * @param {Function=} successCallback
16142 * @param {Function=} errorCallback
16143 * @param {Function=} progressCallback
16144 * @returns {Promise} Returns a promise of the passed value or promise
16148 var when = function(value, callback, errback, progressBack) {
16149 var result = new Deferred();
16150 result.resolve(value);
16151 return result.promise.then(callback, errback, progressBack);
16160 * Alias of {@link ng.$q#when when} to maintain naming consistency with ES6.
16162 * @param {*} value Value or a promise
16163 * @param {Function=} successCallback
16164 * @param {Function=} errorCallback
16165 * @param {Function=} progressCallback
16166 * @returns {Promise} Returns a promise of the passed value or promise
16168 var resolve = when;
16176 * Combines multiple promises into a single promise that is resolved when all of the input
16177 * promises are resolved.
16179 * @param {Array.<Promise>|Object.<Promise>} promises An array or hash of promises.
16180 * @returns {Promise} Returns a single promise that will be resolved with an array/hash of values,
16181 * each value corresponding to the promise at the same index/key in the `promises` array/hash.
16182 * If any of the promises is resolved with a rejection, this resulting promise will be rejected
16183 * with the same rejection value.
16186 function all(promises) {
16187 var deferred = new Deferred(),
16189 results = isArray(promises) ? [] : {};
16191 forEach(promises, function(promise, key) {
16193 when(promise).then(function(value) {
16194 if (results.hasOwnProperty(key)) return;
16195 results[key] = value;
16196 if (!(--counter)) deferred.resolve(results);
16197 }, function(reason) {
16198 if (results.hasOwnProperty(key)) return;
16199 deferred.reject(reason);
16203 if (counter === 0) {
16204 deferred.resolve(results);
16207 return deferred.promise;
16210 var $Q = function Q(resolver) {
16211 if (!isFunction(resolver)) {
16212 throw $qMinErr('norslvr', "Expected resolverFn, got '{0}'", resolver);
16215 var deferred = new Deferred();
16217 function resolveFn(value) {
16218 deferred.resolve(value);
16221 function rejectFn(reason) {
16222 deferred.reject(reason);
16225 resolver(resolveFn, rejectFn);
16227 return deferred.promise;
16230 // Let's make the instanceof operator work for promises, so that
16231 // `new $q(fn) instanceof $q` would evaluate to true.
16232 $Q.prototype = Promise.prototype;
16235 $Q.reject = reject;
16237 $Q.resolve = resolve;
16243 function $$RAFProvider() { //rAF
16244 this.$get = ['$window', '$timeout', function($window, $timeout) {
16245 var requestAnimationFrame = $window.requestAnimationFrame ||
16246 $window.webkitRequestAnimationFrame;
16248 var cancelAnimationFrame = $window.cancelAnimationFrame ||
16249 $window.webkitCancelAnimationFrame ||
16250 $window.webkitCancelRequestAnimationFrame;
16252 var rafSupported = !!requestAnimationFrame;
16253 var raf = rafSupported
16255 var id = requestAnimationFrame(fn);
16256 return function() {
16257 cancelAnimationFrame(id);
16261 var timer = $timeout(fn, 16.66, false); // 1000 / 60 = 16.666
16262 return function() {
16263 $timeout.cancel(timer);
16267 raf.supported = rafSupported;
16276 * The design decisions behind the scope are heavily favored for speed and memory consumption.
16278 * The typical use of scope is to watch the expressions, which most of the time return the same
16279 * value as last time so we optimize the operation.
16281 * Closures construction is expensive in terms of speed as well as memory:
16282 * - No closures, instead use prototypical inheritance for API
16283 * - Internal state needs to be stored on scope directly, which means that private state is
16284 * exposed as $$____ properties
16286 * Loop operations are optimized by using while(count--) { ... }
16287 * - This means that in order to keep the same order of execution as addition we have to add
16288 * items to the array at the beginning (unshift) instead of at the end (push)
16290 * Child scopes are created and removed often
16291 * - Using an array would be slow since inserts in the middle are expensive; so we use linked lists
16293 * There are fewer watches than observers. This is why you don't want the observer to be implemented
16294 * in the same way as watch. Watch requires return of the initialization function which is expensive
16301 * @name $rootScopeProvider
16304 * Provider for the $rootScope service.
16309 * @name $rootScopeProvider#digestTtl
16312 * Sets the number of `$digest` iterations the scope should attempt to execute before giving up and
16313 * assuming that the model is unstable.
16315 * The current default is 10 iterations.
16317 * In complex applications it's possible that the dependencies between `$watch`s will result in
16318 * several digest iterations. However if an application needs more than the default 10 digest
16319 * iterations for its model to stabilize then you should investigate what is causing the model to
16320 * continuously change during the digest.
16322 * Increasing the TTL could have performance implications, so you should not change it without
16323 * proper justification.
16325 * @param {number} limit The number of digest iterations.
16334 * Every application has a single root {@link ng.$rootScope.Scope scope}.
16335 * All other scopes are descendant scopes of the root scope. Scopes provide separation
16336 * between the model and the view, via a mechanism for watching the model for changes.
16337 * They also provide event emission/broadcast and subscription facility. See the
16338 * {@link guide/scope developer guide on scopes}.
16340 function $RootScopeProvider() {
16342 var $rootScopeMinErr = minErr('$rootScope');
16343 var lastDirtyWatch = null;
16344 var applyAsyncId = null;
16346 this.digestTtl = function(value) {
16347 if (arguments.length) {
16353 function createChildScopeClass(parent) {
16354 function ChildScope() {
16355 this.$$watchers = this.$$nextSibling =
16356 this.$$childHead = this.$$childTail = null;
16357 this.$$listeners = {};
16358 this.$$listenerCount = {};
16359 this.$$watchersCount = 0;
16360 this.$id = nextUid();
16361 this.$$ChildScope = null;
16363 ChildScope.prototype = parent;
16367 this.$get = ['$exceptionHandler', '$parse', '$browser',
16368 function($exceptionHandler, $parse, $browser) {
16370 function destroyChildScope($event) {
16371 $event.currentScope.$$destroyed = true;
16374 function cleanUpScope($scope) {
16377 // There is a memory leak in IE9 if all child scopes are not disconnected
16378 // completely when a scope is destroyed. So this code will recurse up through
16379 // all this scopes children
16381 // See issue https://github.com/angular/angular.js/issues/10706
16382 $scope.$$childHead && cleanUpScope($scope.$$childHead);
16383 $scope.$$nextSibling && cleanUpScope($scope.$$nextSibling);
16386 // The code below works around IE9 and V8's memory leaks
16389 // - https://code.google.com/p/v8/issues/detail?id=2073#c26
16390 // - https://github.com/angular/angular.js/issues/6794#issuecomment-38648909
16391 // - https://github.com/angular/angular.js/issues/1313#issuecomment-10378451
16393 $scope.$parent = $scope.$$nextSibling = $scope.$$prevSibling = $scope.$$childHead =
16394 $scope.$$childTail = $scope.$root = $scope.$$watchers = null;
16399 * @name $rootScope.Scope
16402 * A root scope can be retrieved using the {@link ng.$rootScope $rootScope} key from the
16403 * {@link auto.$injector $injector}. Child scopes are created using the
16404 * {@link ng.$rootScope.Scope#$new $new()} method. (Most scopes are created automatically when
16405 * compiled HTML template is executed.) See also the {@link guide/scope Scopes guide} for
16406 * an in-depth introduction and usage examples.
16410 * A scope can inherit from a parent scope, as in this example:
16412 var parent = $rootScope;
16413 var child = parent.$new();
16415 parent.salutation = "Hello";
16416 expect(child.salutation).toEqual('Hello');
16418 child.salutation = "Welcome";
16419 expect(child.salutation).toEqual('Welcome');
16420 expect(parent.salutation).toEqual('Hello');
16423 * When interacting with `Scope` in tests, additional helper methods are available on the
16424 * instances of `Scope` type. See {@link ngMock.$rootScope.Scope ngMock Scope} for additional
16428 * @param {Object.<string, function()>=} providers Map of service factory which need to be
16429 * provided for the current scope. Defaults to {@link ng}.
16430 * @param {Object.<string, *>=} instanceCache Provides pre-instantiated services which should
16431 * append/override services provided by `providers`. This is handy
16432 * when unit-testing and having the need to override a default
16434 * @returns {Object} Newly created scope.
16438 this.$id = nextUid();
16439 this.$$phase = this.$parent = this.$$watchers =
16440 this.$$nextSibling = this.$$prevSibling =
16441 this.$$childHead = this.$$childTail = null;
16443 this.$$destroyed = false;
16444 this.$$listeners = {};
16445 this.$$listenerCount = {};
16446 this.$$watchersCount = 0;
16447 this.$$isolateBindings = null;
16452 * @name $rootScope.Scope#$id
16455 * Unique scope ID (monotonically increasing) useful for debugging.
16460 * @name $rootScope.Scope#$parent
16463 * Reference to the parent scope.
16468 * @name $rootScope.Scope#$root
16471 * Reference to the root scope.
16474 Scope.prototype = {
16475 constructor: Scope,
16478 * @name $rootScope.Scope#$new
16482 * Creates a new child {@link ng.$rootScope.Scope scope}.
16484 * The parent scope will propagate the {@link ng.$rootScope.Scope#$digest $digest()} event.
16485 * The scope can be removed from the scope hierarchy using {@link ng.$rootScope.Scope#$destroy $destroy()}.
16487 * {@link ng.$rootScope.Scope#$destroy $destroy()} must be called on a scope when it is
16488 * desired for the scope and its child scopes to be permanently detached from the parent and
16489 * thus stop participating in model change detection and listener notification by invoking.
16491 * @param {boolean} isolate If true, then the scope does not prototypically inherit from the
16492 * parent scope. The scope is isolated, as it can not see parent scope properties.
16493 * When creating widgets, it is useful for the widget to not accidentally read parent
16496 * @param {Scope} [parent=this] The {@link ng.$rootScope.Scope `Scope`} that will be the `$parent`
16497 * of the newly created scope. Defaults to `this` scope if not provided.
16498 * This is used when creating a transclude scope to correctly place it
16499 * in the scope hierarchy while maintaining the correct prototypical
16502 * @returns {Object} The newly created child scope.
16505 $new: function(isolate, parent) {
16508 parent = parent || this;
16511 child = new Scope();
16512 child.$root = this.$root;
16514 // Only create a child scope class if somebody asks for one,
16515 // but cache it to allow the VM to optimize lookups.
16516 if (!this.$$ChildScope) {
16517 this.$$ChildScope = createChildScopeClass(this);
16519 child = new this.$$ChildScope();
16521 child.$parent = parent;
16522 child.$$prevSibling = parent.$$childTail;
16523 if (parent.$$childHead) {
16524 parent.$$childTail.$$nextSibling = child;
16525 parent.$$childTail = child;
16527 parent.$$childHead = parent.$$childTail = child;
16530 // When the new scope is not isolated or we inherit from `this`, and
16531 // the parent scope is destroyed, the property `$$destroyed` is inherited
16532 // prototypically. In all other cases, this property needs to be set
16533 // when the parent scope is destroyed.
16534 // The listener needs to be added after the parent is set
16535 if (isolate || parent != this) child.$on('$destroy', destroyChildScope);
16542 * @name $rootScope.Scope#$watch
16546 * Registers a `listener` callback to be executed whenever the `watchExpression` changes.
16548 * - The `watchExpression` is called on every call to {@link ng.$rootScope.Scope#$digest
16549 * $digest()} and should return the value that will be watched. (`watchExpression` should not change
16550 * its value when executed multiple times with the same input because it may be executed multiple
16551 * times by {@link ng.$rootScope.Scope#$digest $digest()}. That is, `watchExpression` should be
16552 * [idempotent](http://en.wikipedia.org/wiki/Idempotence).
16553 * - The `listener` is called only when the value from the current `watchExpression` and the
16554 * previous call to `watchExpression` are not equal (with the exception of the initial run,
16555 * see below). Inequality is determined according to reference inequality,
16556 * [strict comparison](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators)
16557 * via the `!==` Javascript operator, unless `objectEquality == true`
16559 * - When `objectEquality == true`, inequality of the `watchExpression` is determined
16560 * according to the {@link angular.equals} function. To save the value of the object for
16561 * later comparison, the {@link angular.copy} function is used. This therefore means that
16562 * watching complex objects will have adverse memory and performance implications.
16563 * - The watch `listener` may change the model, which may trigger other `listener`s to fire.
16564 * This is achieved by rerunning the watchers until no changes are detected. The rerun
16565 * iteration limit is 10 to prevent an infinite loop deadlock.
16568 * If you want to be notified whenever {@link ng.$rootScope.Scope#$digest $digest} is called,
16569 * you can register a `watchExpression` function with no `listener`. (Be prepared for
16570 * multiple calls to your `watchExpression` because it will execute multiple times in a
16571 * single {@link ng.$rootScope.Scope#$digest $digest} cycle if a change is detected.)
16573 * After a watcher is registered with the scope, the `listener` fn is called asynchronously
16574 * (via {@link ng.$rootScope.Scope#$evalAsync $evalAsync}) to initialize the
16575 * watcher. In rare cases, this is undesirable because the listener is called when the result
16576 * of `watchExpression` didn't change. To detect this scenario within the `listener` fn, you
16577 * can compare the `newVal` and `oldVal`. If these two values are identical (`===`) then the
16578 * listener was called due to initialization.
16584 // let's assume that scope was dependency injected as the $rootScope
16585 var scope = $rootScope;
16586 scope.name = 'misko';
16589 expect(scope.counter).toEqual(0);
16590 scope.$watch('name', function(newValue, oldValue) {
16591 scope.counter = scope.counter + 1;
16593 expect(scope.counter).toEqual(0);
16596 // the listener is always called during the first $digest loop after it was registered
16597 expect(scope.counter).toEqual(1);
16600 // but now it will not be called unless the value changes
16601 expect(scope.counter).toEqual(1);
16603 scope.name = 'adam';
16605 expect(scope.counter).toEqual(2);
16609 // Using a function as a watchExpression
16611 scope.foodCounter = 0;
16612 expect(scope.foodCounter).toEqual(0);
16614 // This function returns the value being watched. It is called for each turn of the $digest loop
16615 function() { return food; },
16616 // This is the change listener, called when the value returned from the above function changes
16617 function(newValue, oldValue) {
16618 if ( newValue !== oldValue ) {
16619 // Only increment the counter if the value changed
16620 scope.foodCounter = scope.foodCounter + 1;
16624 // No digest has been run so the counter will be zero
16625 expect(scope.foodCounter).toEqual(0);
16627 // Run the digest but since food has not changed count will still be zero
16629 expect(scope.foodCounter).toEqual(0);
16631 // Update food and run digest. Now the counter will increment
16632 food = 'cheeseburger';
16634 expect(scope.foodCounter).toEqual(1);
16640 * @param {(function()|string)} watchExpression Expression that is evaluated on each
16641 * {@link ng.$rootScope.Scope#$digest $digest} cycle. A change in the return value triggers
16642 * a call to the `listener`.
16644 * - `string`: Evaluated as {@link guide/expression expression}
16645 * - `function(scope)`: called with current `scope` as a parameter.
16646 * @param {function(newVal, oldVal, scope)} listener Callback called whenever the value
16647 * of `watchExpression` changes.
16649 * - `newVal` contains the current value of the `watchExpression`
16650 * - `oldVal` contains the previous value of the `watchExpression`
16651 * - `scope` refers to the current scope
16652 * @param {boolean=} [objectEquality=false] Compare for object equality using {@link angular.equals} instead of
16653 * comparing for reference equality.
16654 * @returns {function()} Returns a deregistration function for this listener.
16656 $watch: function(watchExp, listener, objectEquality, prettyPrintExpression) {
16657 var get = $parse(watchExp);
16659 if (get.$$watchDelegate) {
16660 return get.$$watchDelegate(this, listener, objectEquality, get, watchExp);
16663 array = scope.$$watchers,
16666 last: initWatchVal,
16668 exp: prettyPrintExpression || watchExp,
16669 eq: !!objectEquality
16672 lastDirtyWatch = null;
16674 if (!isFunction(listener)) {
16679 array = scope.$$watchers = [];
16681 // we use unshift since we use a while loop in $digest for speed.
16682 // the while loop reads in reverse order.
16683 array.unshift(watcher);
16684 incrementWatchersCount(this, 1);
16686 return function deregisterWatch() {
16687 if (arrayRemove(array, watcher) >= 0) {
16688 incrementWatchersCount(scope, -1);
16690 lastDirtyWatch = null;
16696 * @name $rootScope.Scope#$watchGroup
16700 * A variant of {@link ng.$rootScope.Scope#$watch $watch()} where it watches an array of `watchExpressions`.
16701 * If any one expression in the collection changes the `listener` is executed.
16703 * - The items in the `watchExpressions` array are observed via standard $watch operation and are examined on every
16704 * call to $digest() to see if any items changes.
16705 * - The `listener` is called whenever any expression in the `watchExpressions` array changes.
16707 * @param {Array.<string|Function(scope)>} watchExpressions Array of expressions that will be individually
16708 * watched using {@link ng.$rootScope.Scope#$watch $watch()}
16710 * @param {function(newValues, oldValues, scope)} listener Callback called whenever the return value of any
16711 * expression in `watchExpressions` changes
16712 * The `newValues` array contains the current values of the `watchExpressions`, with the indexes matching
16713 * those of `watchExpression`
16714 * and the `oldValues` array contains the previous values of the `watchExpressions`, with the indexes matching
16715 * those of `watchExpression`
16716 * The `scope` refers to the current scope.
16717 * @returns {function()} Returns a de-registration function for all listeners.
16719 $watchGroup: function(watchExpressions, listener) {
16720 var oldValues = new Array(watchExpressions.length);
16721 var newValues = new Array(watchExpressions.length);
16722 var deregisterFns = [];
16724 var changeReactionScheduled = false;
16725 var firstRun = true;
16727 if (!watchExpressions.length) {
16728 // No expressions means we call the listener ASAP
16729 var shouldCall = true;
16730 self.$evalAsync(function() {
16731 if (shouldCall) listener(newValues, newValues, self);
16733 return function deregisterWatchGroup() {
16734 shouldCall = false;
16738 if (watchExpressions.length === 1) {
16739 // Special case size of one
16740 return this.$watch(watchExpressions[0], function watchGroupAction(value, oldValue, scope) {
16741 newValues[0] = value;
16742 oldValues[0] = oldValue;
16743 listener(newValues, (value === oldValue) ? newValues : oldValues, scope);
16747 forEach(watchExpressions, function(expr, i) {
16748 var unwatchFn = self.$watch(expr, function watchGroupSubAction(value, oldValue) {
16749 newValues[i] = value;
16750 oldValues[i] = oldValue;
16751 if (!changeReactionScheduled) {
16752 changeReactionScheduled = true;
16753 self.$evalAsync(watchGroupAction);
16756 deregisterFns.push(unwatchFn);
16759 function watchGroupAction() {
16760 changeReactionScheduled = false;
16764 listener(newValues, newValues, self);
16766 listener(newValues, oldValues, self);
16770 return function deregisterWatchGroup() {
16771 while (deregisterFns.length) {
16772 deregisterFns.shift()();
16780 * @name $rootScope.Scope#$watchCollection
16784 * Shallow watches the properties of an object and fires whenever any of the properties change
16785 * (for arrays, this implies watching the array items; for object maps, this implies watching
16786 * the properties). If a change is detected, the `listener` callback is fired.
16788 * - The `obj` collection is observed via standard $watch operation and is examined on every
16789 * call to $digest() to see if any items have been added, removed, or moved.
16790 * - The `listener` is called whenever anything within the `obj` has changed. Examples include
16791 * adding, removing, and moving items belonging to an object or array.
16796 $scope.names = ['igor', 'matias', 'misko', 'james'];
16797 $scope.dataCount = 4;
16799 $scope.$watchCollection('names', function(newNames, oldNames) {
16800 $scope.dataCount = newNames.length;
16803 expect($scope.dataCount).toEqual(4);
16806 //still at 4 ... no changes
16807 expect($scope.dataCount).toEqual(4);
16809 $scope.names.pop();
16812 //now there's been a change
16813 expect($scope.dataCount).toEqual(3);
16817 * @param {string|function(scope)} obj Evaluated as {@link guide/expression expression}. The
16818 * expression value should evaluate to an object or an array which is observed on each
16819 * {@link ng.$rootScope.Scope#$digest $digest} cycle. Any shallow change within the
16820 * collection will trigger a call to the `listener`.
16822 * @param {function(newCollection, oldCollection, scope)} listener a callback function called
16823 * when a change is detected.
16824 * - The `newCollection` object is the newly modified data obtained from the `obj` expression
16825 * - The `oldCollection` object is a copy of the former collection data.
16826 * Due to performance considerations, the`oldCollection` value is computed only if the
16827 * `listener` function declares two or more arguments.
16828 * - The `scope` argument refers to the current scope.
16830 * @returns {function()} Returns a de-registration function for this listener. When the
16831 * de-registration function is executed, the internal watch operation is terminated.
16833 $watchCollection: function(obj, listener) {
16834 $watchCollectionInterceptor.$stateful = true;
16837 // the current value, updated on each dirty-check run
16839 // a shallow copy of the newValue from the last dirty-check run,
16840 // updated to match newValue during dirty-check run
16842 // a shallow copy of the newValue from when the last change happened
16844 // only track veryOldValue if the listener is asking for it
16845 var trackVeryOldValue = (listener.length > 1);
16846 var changeDetected = 0;
16847 var changeDetector = $parse(obj, $watchCollectionInterceptor);
16848 var internalArray = [];
16849 var internalObject = {};
16850 var initRun = true;
16853 function $watchCollectionInterceptor(_value) {
16855 var newLength, key, bothNaN, newItem, oldItem;
16857 // If the new value is undefined, then return undefined as the watch may be a one-time watch
16858 if (isUndefined(newValue)) return;
16860 if (!isObject(newValue)) { // if primitive
16861 if (oldValue !== newValue) {
16862 oldValue = newValue;
16865 } else if (isArrayLike(newValue)) {
16866 if (oldValue !== internalArray) {
16867 // we are transitioning from something which was not an array into array.
16868 oldValue = internalArray;
16869 oldLength = oldValue.length = 0;
16873 newLength = newValue.length;
16875 if (oldLength !== newLength) {
16876 // if lengths do not match we need to trigger change notification
16878 oldValue.length = oldLength = newLength;
16880 // copy the items to oldValue and look for changes.
16881 for (var i = 0; i < newLength; i++) {
16882 oldItem = oldValue[i];
16883 newItem = newValue[i];
16885 bothNaN = (oldItem !== oldItem) && (newItem !== newItem);
16886 if (!bothNaN && (oldItem !== newItem)) {
16888 oldValue[i] = newItem;
16892 if (oldValue !== internalObject) {
16893 // we are transitioning from something which was not an object into object.
16894 oldValue = internalObject = {};
16898 // copy the items to oldValue and look for changes.
16900 for (key in newValue) {
16901 if (hasOwnProperty.call(newValue, key)) {
16903 newItem = newValue[key];
16904 oldItem = oldValue[key];
16906 if (key in oldValue) {
16907 bothNaN = (oldItem !== oldItem) && (newItem !== newItem);
16908 if (!bothNaN && (oldItem !== newItem)) {
16910 oldValue[key] = newItem;
16914 oldValue[key] = newItem;
16919 if (oldLength > newLength) {
16920 // we used to have more keys, need to find them and destroy them.
16922 for (key in oldValue) {
16923 if (!hasOwnProperty.call(newValue, key)) {
16925 delete oldValue[key];
16930 return changeDetected;
16933 function $watchCollectionAction() {
16936 listener(newValue, newValue, self);
16938 listener(newValue, veryOldValue, self);
16941 // make a copy for the next time a collection is changed
16942 if (trackVeryOldValue) {
16943 if (!isObject(newValue)) {
16945 veryOldValue = newValue;
16946 } else if (isArrayLike(newValue)) {
16947 veryOldValue = new Array(newValue.length);
16948 for (var i = 0; i < newValue.length; i++) {
16949 veryOldValue[i] = newValue[i];
16951 } else { // if object
16953 for (var key in newValue) {
16954 if (hasOwnProperty.call(newValue, key)) {
16955 veryOldValue[key] = newValue[key];
16962 return this.$watch(changeDetector, $watchCollectionAction);
16967 * @name $rootScope.Scope#$digest
16971 * Processes all of the {@link ng.$rootScope.Scope#$watch watchers} of the current scope and
16972 * its children. Because a {@link ng.$rootScope.Scope#$watch watcher}'s listener can change
16973 * the model, the `$digest()` keeps calling the {@link ng.$rootScope.Scope#$watch watchers}
16974 * until no more listeners are firing. This means that it is possible to get into an infinite
16975 * loop. This function will throw `'Maximum iteration limit exceeded.'` if the number of
16976 * iterations exceeds 10.
16978 * Usually, you don't call `$digest()` directly in
16979 * {@link ng.directive:ngController controllers} or in
16980 * {@link ng.$compileProvider#directive directives}.
16981 * Instead, you should call {@link ng.$rootScope.Scope#$apply $apply()} (typically from within
16982 * a {@link ng.$compileProvider#directive directive}), which will force a `$digest()`.
16984 * If you want to be notified whenever `$digest()` is called,
16985 * you can register a `watchExpression` function with
16986 * {@link ng.$rootScope.Scope#$watch $watch()} with no `listener`.
16988 * In unit tests, you may need to call `$digest()` to simulate the scope life cycle.
16993 scope.name = 'misko';
16996 expect(scope.counter).toEqual(0);
16997 scope.$watch('name', function(newValue, oldValue) {
16998 scope.counter = scope.counter + 1;
17000 expect(scope.counter).toEqual(0);
17003 // the listener is always called during the first $digest loop after it was registered
17004 expect(scope.counter).toEqual(1);
17007 // but now it will not be called unless the value changes
17008 expect(scope.counter).toEqual(1);
17010 scope.name = 'adam';
17012 expect(scope.counter).toEqual(2);
17016 $digest: function() {
17017 var watch, value, last, fn, get,
17021 next, current, target = this,
17025 beginPhase('$digest');
17026 // Check for changes to browser url that happened in sync before the call to $digest
17027 $browser.$$checkUrlChange();
17029 if (this === $rootScope && applyAsyncId !== null) {
17030 // If this is the root scope, and $applyAsync has scheduled a deferred $apply(), then
17031 // cancel the scheduled $apply and flush the queue of expressions to be evaluated.
17032 $browser.defer.cancel(applyAsyncId);
17036 lastDirtyWatch = null;
17038 do { // "while dirty" loop
17042 while (asyncQueue.length) {
17044 asyncTask = asyncQueue.shift();
17045 asyncTask.scope.$eval(asyncTask.expression, asyncTask.locals);
17047 $exceptionHandler(e);
17049 lastDirtyWatch = null;
17052 traverseScopesLoop:
17053 do { // "traverse the scopes" loop
17054 if ((watchers = current.$$watchers)) {
17055 // process our watches
17056 length = watchers.length;
17059 watch = watchers[length];
17060 // Most common watches are on primitives, in which case we can short
17061 // circuit it with === operator, only when === fails do we use .equals
17064 if ((value = get(current)) !== (last = watch.last) &&
17066 ? equals(value, last)
17067 : (typeof value === 'number' && typeof last === 'number'
17068 && isNaN(value) && isNaN(last)))) {
17070 lastDirtyWatch = watch;
17071 watch.last = watch.eq ? copy(value, null) : value;
17073 fn(value, ((last === initWatchVal) ? value : last), current);
17076 if (!watchLog[logIdx]) watchLog[logIdx] = [];
17077 watchLog[logIdx].push({
17078 msg: isFunction(watch.exp) ? 'fn: ' + (watch.exp.name || watch.exp.toString()) : watch.exp,
17083 } else if (watch === lastDirtyWatch) {
17084 // If the most recently dirty watcher is now clean, short circuit since the remaining watchers
17085 // have already been tested.
17087 break traverseScopesLoop;
17091 $exceptionHandler(e);
17096 // Insanity Warning: scope depth-first traversal
17097 // yes, this code is a bit crazy, but it works and we have tests to prove it!
17098 // this piece should be kept in sync with the traversal in $broadcast
17099 if (!(next = ((current.$$watchersCount && current.$$childHead) ||
17100 (current !== target && current.$$nextSibling)))) {
17101 while (current !== target && !(next = current.$$nextSibling)) {
17102 current = current.$parent;
17105 } while ((current = next));
17107 // `break traverseScopesLoop;` takes us to here
17109 if ((dirty || asyncQueue.length) && !(ttl--)) {
17111 throw $rootScopeMinErr('infdig',
17112 '{0} $digest() iterations reached. Aborting!\n' +
17113 'Watchers fired in the last 5 iterations: {1}',
17117 } while (dirty || asyncQueue.length);
17121 while (postDigestQueue.length) {
17123 postDigestQueue.shift()();
17125 $exceptionHandler(e);
17133 * @name $rootScope.Scope#$destroy
17134 * @eventType broadcast on scope being destroyed
17137 * Broadcasted when a scope and its children are being destroyed.
17139 * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to
17140 * clean up DOM bindings before an element is removed from the DOM.
17145 * @name $rootScope.Scope#$destroy
17149 * Removes the current scope (and all of its children) from the parent scope. Removal implies
17150 * that calls to {@link ng.$rootScope.Scope#$digest $digest()} will no longer
17151 * propagate to the current scope and its children. Removal also implies that the current
17152 * scope is eligible for garbage collection.
17154 * The `$destroy()` is usually used by directives such as
17155 * {@link ng.directive:ngRepeat ngRepeat} for managing the
17156 * unrolling of the loop.
17158 * Just before a scope is destroyed, a `$destroy` event is broadcasted on this scope.
17159 * Application code can register a `$destroy` event handler that will give it a chance to
17160 * perform any necessary cleanup.
17162 * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to
17163 * clean up DOM bindings before an element is removed from the DOM.
17165 $destroy: function() {
17166 // We can't destroy a scope that has been already destroyed.
17167 if (this.$$destroyed) return;
17168 var parent = this.$parent;
17170 this.$broadcast('$destroy');
17171 this.$$destroyed = true;
17173 if (this === $rootScope) {
17174 //Remove handlers attached to window when $rootScope is removed
17175 $browser.$$applicationDestroyed();
17178 incrementWatchersCount(this, -this.$$watchersCount);
17179 for (var eventName in this.$$listenerCount) {
17180 decrementListenerCount(this, this.$$listenerCount[eventName], eventName);
17183 // sever all the references to parent scopes (after this cleanup, the current scope should
17184 // not be retained by any of our references and should be eligible for garbage collection)
17185 if (parent && parent.$$childHead == this) parent.$$childHead = this.$$nextSibling;
17186 if (parent && parent.$$childTail == this) parent.$$childTail = this.$$prevSibling;
17187 if (this.$$prevSibling) this.$$prevSibling.$$nextSibling = this.$$nextSibling;
17188 if (this.$$nextSibling) this.$$nextSibling.$$prevSibling = this.$$prevSibling;
17190 // Disable listeners, watchers and apply/digest methods
17191 this.$destroy = this.$digest = this.$apply = this.$evalAsync = this.$applyAsync = noop;
17192 this.$on = this.$watch = this.$watchGroup = function() { return noop; };
17193 this.$$listeners = {};
17195 // Disconnect the next sibling to prevent `cleanUpScope` destroying those too
17196 this.$$nextSibling = null;
17197 cleanUpScope(this);
17202 * @name $rootScope.Scope#$eval
17206 * Executes the `expression` on the current scope and returns the result. Any exceptions in
17207 * the expression are propagated (uncaught). This is useful when evaluating Angular
17212 var scope = ng.$rootScope.Scope();
17216 expect(scope.$eval('a+b')).toEqual(3);
17217 expect(scope.$eval(function(scope){ return scope.a + scope.b; })).toEqual(3);
17220 * @param {(string|function())=} expression An angular expression to be executed.
17222 * - `string`: execute using the rules as defined in {@link guide/expression expression}.
17223 * - `function(scope)`: execute the function with the current `scope` parameter.
17225 * @param {(object)=} locals Local variables object, useful for overriding values in scope.
17226 * @returns {*} The result of evaluating the expression.
17228 $eval: function(expr, locals) {
17229 return $parse(expr)(this, locals);
17234 * @name $rootScope.Scope#$evalAsync
17238 * Executes the expression on the current scope at a later point in time.
17240 * The `$evalAsync` makes no guarantees as to when the `expression` will be executed, only
17243 * - it will execute after the function that scheduled the evaluation (preferably before DOM
17245 * - at least one {@link ng.$rootScope.Scope#$digest $digest cycle} will be performed after
17246 * `expression` execution.
17248 * Any exceptions from the execution of the expression are forwarded to the
17249 * {@link ng.$exceptionHandler $exceptionHandler} service.
17251 * __Note:__ if this function is called outside of a `$digest` cycle, a new `$digest` cycle
17252 * will be scheduled. However, it is encouraged to always call code that changes the model
17253 * from within an `$apply` call. That includes code evaluated via `$evalAsync`.
17255 * @param {(string|function())=} expression An angular expression to be executed.
17257 * - `string`: execute using the rules as defined in {@link guide/expression expression}.
17258 * - `function(scope)`: execute the function with the current `scope` parameter.
17260 * @param {(object)=} locals Local variables object, useful for overriding values in scope.
17262 $evalAsync: function(expr, locals) {
17263 // if we are outside of an $digest loop and this is the first time we are scheduling async
17264 // task also schedule async auto-flush
17265 if (!$rootScope.$$phase && !asyncQueue.length) {
17266 $browser.defer(function() {
17267 if (asyncQueue.length) {
17268 $rootScope.$digest();
17273 asyncQueue.push({scope: this, expression: $parse(expr), locals: locals});
17276 $$postDigest: function(fn) {
17277 postDigestQueue.push(fn);
17282 * @name $rootScope.Scope#$apply
17286 * `$apply()` is used to execute an expression in angular from outside of the angular
17287 * framework. (For example from browser DOM events, setTimeout, XHR or third party libraries).
17288 * Because we are calling into the angular framework we need to perform proper scope life
17289 * cycle of {@link ng.$exceptionHandler exception handling},
17290 * {@link ng.$rootScope.Scope#$digest executing watches}.
17294 * # Pseudo-Code of `$apply()`
17296 function $apply(expr) {
17298 return $eval(expr);
17300 $exceptionHandler(e);
17308 * Scope's `$apply()` method transitions through the following stages:
17310 * 1. The {@link guide/expression expression} is executed using the
17311 * {@link ng.$rootScope.Scope#$eval $eval()} method.
17312 * 2. Any exceptions from the execution of the expression are forwarded to the
17313 * {@link ng.$exceptionHandler $exceptionHandler} service.
17314 * 3. The {@link ng.$rootScope.Scope#$watch watch} listeners are fired immediately after the
17315 * expression was executed using the {@link ng.$rootScope.Scope#$digest $digest()} method.
17318 * @param {(string|function())=} exp An angular expression to be executed.
17320 * - `string`: execute using the rules as defined in {@link guide/expression expression}.
17321 * - `function(scope)`: execute the function with current `scope` parameter.
17323 * @returns {*} The result of evaluating the expression.
17325 $apply: function(expr) {
17327 beginPhase('$apply');
17329 return this.$eval(expr);
17334 $exceptionHandler(e);
17337 $rootScope.$digest();
17339 $exceptionHandler(e);
17347 * @name $rootScope.Scope#$applyAsync
17351 * Schedule the invocation of $apply to occur at a later time. The actual time difference
17352 * varies across browsers, but is typically around ~10 milliseconds.
17354 * This can be used to queue up multiple expressions which need to be evaluated in the same
17357 * @param {(string|function())=} exp An angular expression to be executed.
17359 * - `string`: execute using the rules as defined in {@link guide/expression expression}.
17360 * - `function(scope)`: execute the function with current `scope` parameter.
17362 $applyAsync: function(expr) {
17364 expr && applyAsyncQueue.push($applyAsyncExpression);
17365 expr = $parse(expr);
17366 scheduleApplyAsync();
17368 function $applyAsyncExpression() {
17375 * @name $rootScope.Scope#$on
17379 * Listens on events of a given type. See {@link ng.$rootScope.Scope#$emit $emit} for
17380 * discussion of event life cycle.
17382 * The event listener function format is: `function(event, args...)`. The `event` object
17383 * passed into the listener has the following attributes:
17385 * - `targetScope` - `{Scope}`: the scope on which the event was `$emit`-ed or
17387 * - `currentScope` - `{Scope}`: the scope that is currently handling the event. Once the
17388 * event propagates through the scope hierarchy, this property is set to null.
17389 * - `name` - `{string}`: name of the event.
17390 * - `stopPropagation` - `{function=}`: calling `stopPropagation` function will cancel
17391 * further event propagation (available only for events that were `$emit`-ed).
17392 * - `preventDefault` - `{function}`: calling `preventDefault` sets `defaultPrevented` flag
17394 * - `defaultPrevented` - `{boolean}`: true if `preventDefault` was called.
17396 * @param {string} name Event name to listen on.
17397 * @param {function(event, ...args)} listener Function to call when the event is emitted.
17398 * @returns {function()} Returns a deregistration function for this listener.
17400 $on: function(name, listener) {
17401 var namedListeners = this.$$listeners[name];
17402 if (!namedListeners) {
17403 this.$$listeners[name] = namedListeners = [];
17405 namedListeners.push(listener);
17407 var current = this;
17409 if (!current.$$listenerCount[name]) {
17410 current.$$listenerCount[name] = 0;
17412 current.$$listenerCount[name]++;
17413 } while ((current = current.$parent));
17416 return function() {
17417 var indexOfListener = namedListeners.indexOf(listener);
17418 if (indexOfListener !== -1) {
17419 namedListeners[indexOfListener] = null;
17420 decrementListenerCount(self, 1, name);
17428 * @name $rootScope.Scope#$emit
17432 * Dispatches an event `name` upwards through the scope hierarchy notifying the
17433 * registered {@link ng.$rootScope.Scope#$on} listeners.
17435 * The event life cycle starts at the scope on which `$emit` was called. All
17436 * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get
17437 * notified. Afterwards, the event traverses upwards toward the root scope and calls all
17438 * registered listeners along the way. The event will stop propagating if one of the listeners
17441 * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
17442 * onto the {@link ng.$exceptionHandler $exceptionHandler} service.
17444 * @param {string} name Event name to emit.
17445 * @param {...*} args Optional one or more arguments which will be passed onto the event listeners.
17446 * @return {Object} Event object (see {@link ng.$rootScope.Scope#$on}).
17448 $emit: function(name, args) {
17452 stopPropagation = false,
17455 targetScope: scope,
17456 stopPropagation: function() {stopPropagation = true;},
17457 preventDefault: function() {
17458 event.defaultPrevented = true;
17460 defaultPrevented: false
17462 listenerArgs = concat([event], arguments, 1),
17466 namedListeners = scope.$$listeners[name] || empty;
17467 event.currentScope = scope;
17468 for (i = 0, length = namedListeners.length; i < length; i++) {
17470 // if listeners were deregistered, defragment the array
17471 if (!namedListeners[i]) {
17472 namedListeners.splice(i, 1);
17478 //allow all listeners attached to the current scope to run
17479 namedListeners[i].apply(null, listenerArgs);
17481 $exceptionHandler(e);
17484 //if any listener on the current scope stops propagation, prevent bubbling
17485 if (stopPropagation) {
17486 event.currentScope = null;
17490 scope = scope.$parent;
17493 event.currentScope = null;
17501 * @name $rootScope.Scope#$broadcast
17505 * Dispatches an event `name` downwards to all child scopes (and their children) notifying the
17506 * registered {@link ng.$rootScope.Scope#$on} listeners.
17508 * The event life cycle starts at the scope on which `$broadcast` was called. All
17509 * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get
17510 * notified. Afterwards, the event propagates to all direct and indirect scopes of the current
17511 * scope and calls all registered listeners along the way. The event cannot be canceled.
17513 * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
17514 * onto the {@link ng.$exceptionHandler $exceptionHandler} service.
17516 * @param {string} name Event name to broadcast.
17517 * @param {...*} args Optional one or more arguments which will be passed onto the event listeners.
17518 * @return {Object} Event object, see {@link ng.$rootScope.Scope#$on}
17520 $broadcast: function(name, args) {
17526 targetScope: target,
17527 preventDefault: function() {
17528 event.defaultPrevented = true;
17530 defaultPrevented: false
17533 if (!target.$$listenerCount[name]) return event;
17535 var listenerArgs = concat([event], arguments, 1),
17536 listeners, i, length;
17538 //down while you can, then up and next sibling or up and next sibling until back at root
17539 while ((current = next)) {
17540 event.currentScope = current;
17541 listeners = current.$$listeners[name] || [];
17542 for (i = 0, length = listeners.length; i < length; i++) {
17543 // if listeners were deregistered, defragment the array
17544 if (!listeners[i]) {
17545 listeners.splice(i, 1);
17552 listeners[i].apply(null, listenerArgs);
17554 $exceptionHandler(e);
17558 // Insanity Warning: scope depth-first traversal
17559 // yes, this code is a bit crazy, but it works and we have tests to prove it!
17560 // this piece should be kept in sync with the traversal in $digest
17561 // (though it differs due to having the extra check for $$listenerCount)
17562 if (!(next = ((current.$$listenerCount[name] && current.$$childHead) ||
17563 (current !== target && current.$$nextSibling)))) {
17564 while (current !== target && !(next = current.$$nextSibling)) {
17565 current = current.$parent;
17570 event.currentScope = null;
17575 var $rootScope = new Scope();
17577 //The internal queues. Expose them on the $rootScope for debugging/testing purposes.
17578 var asyncQueue = $rootScope.$$asyncQueue = [];
17579 var postDigestQueue = $rootScope.$$postDigestQueue = [];
17580 var applyAsyncQueue = $rootScope.$$applyAsyncQueue = [];
17585 function beginPhase(phase) {
17586 if ($rootScope.$$phase) {
17587 throw $rootScopeMinErr('inprog', '{0} already in progress', $rootScope.$$phase);
17590 $rootScope.$$phase = phase;
17593 function clearPhase() {
17594 $rootScope.$$phase = null;
17597 function incrementWatchersCount(current, count) {
17599 current.$$watchersCount += count;
17600 } while ((current = current.$parent));
17603 function decrementListenerCount(current, count, name) {
17605 current.$$listenerCount[name] -= count;
17607 if (current.$$listenerCount[name] === 0) {
17608 delete current.$$listenerCount[name];
17610 } while ((current = current.$parent));
17614 * function used as an initial value for watchers.
17615 * because it's unique we can easily tell it apart from other values
17617 function initWatchVal() {}
17619 function flushApplyAsync() {
17620 while (applyAsyncQueue.length) {
17622 applyAsyncQueue.shift()();
17624 $exceptionHandler(e);
17627 applyAsyncId = null;
17630 function scheduleApplyAsync() {
17631 if (applyAsyncId === null) {
17632 applyAsyncId = $browser.defer(function() {
17633 $rootScope.$apply(flushApplyAsync);
17642 * @name $rootElement
17645 * The root element of Angular application. This is either the element where {@link
17646 * ng.directive:ngApp ngApp} was declared or the element passed into
17647 * {@link angular.bootstrap}. The element represents the root element of application. It is also the
17648 * location where the application's {@link auto.$injector $injector} service gets
17649 * published, and can be retrieved using `$rootElement.injector()`.
17653 // the implementation is in angular.bootstrap
17657 * Private service to sanitize uris for links and images. Used by $compile and $sanitize.
17659 function $$SanitizeUriProvider() {
17660 var aHrefSanitizationWhitelist = /^\s*(https?|ftp|mailto|tel|file):/,
17661 imgSrcSanitizationWhitelist = /^\s*((https?|ftp|file|blob):|data:image\/)/;
17665 * Retrieves or overrides the default regular expression that is used for whitelisting of safe
17666 * urls during a[href] sanitization.
17668 * The sanitization is a security measure aimed at prevent XSS attacks via html links.
17670 * Any url about to be assigned to a[href] via data-binding is first normalized and turned into
17671 * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist`
17672 * regular expression. If a match is found, the original url is written into the dom. Otherwise,
17673 * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
17675 * @param {RegExp=} regexp New regexp to whitelist urls with.
17676 * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
17677 * chaining otherwise.
17679 this.aHrefSanitizationWhitelist = function(regexp) {
17680 if (isDefined(regexp)) {
17681 aHrefSanitizationWhitelist = regexp;
17684 return aHrefSanitizationWhitelist;
17690 * Retrieves or overrides the default regular expression that is used for whitelisting of safe
17691 * urls during img[src] sanitization.
17693 * The sanitization is a security measure aimed at prevent XSS attacks via html links.
17695 * Any url about to be assigned to img[src] via data-binding is first normalized and turned into
17696 * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist`
17697 * regular expression. If a match is found, the original url is written into the dom. Otherwise,
17698 * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
17700 * @param {RegExp=} regexp New regexp to whitelist urls with.
17701 * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
17702 * chaining otherwise.
17704 this.imgSrcSanitizationWhitelist = function(regexp) {
17705 if (isDefined(regexp)) {
17706 imgSrcSanitizationWhitelist = regexp;
17709 return imgSrcSanitizationWhitelist;
17712 this.$get = function() {
17713 return function sanitizeUri(uri, isImage) {
17714 var regex = isImage ? imgSrcSanitizationWhitelist : aHrefSanitizationWhitelist;
17716 normalizedVal = urlResolve(uri).href;
17717 if (normalizedVal !== '' && !normalizedVal.match(regex)) {
17718 return 'unsafe:' + normalizedVal;
17725 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
17726 * Any commits to this file should be reviewed with security in mind. *
17727 * Changes to this file can potentially create security vulnerabilities. *
17728 * An approval from 2 Core members with history of modifying *
17729 * this file is required. *
17731 * Does the change somehow allow for arbitrary javascript to be executed? *
17732 * Or allows for someone to change the prototype of built-in objects? *
17733 * Or gives undesired access to variables likes document or window? *
17734 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
17736 var $sceMinErr = minErr('$sce');
17738 var SCE_CONTEXTS = {
17742 // RESOURCE_URL is a subtype of URL used in contexts where a privileged resource is sourced from a
17743 // url. (e.g. ng-include, script src, templateUrl)
17744 RESOURCE_URL: 'resourceUrl',
17748 // Helper functions follow.
17750 function adjustMatcher(matcher) {
17751 if (matcher === 'self') {
17753 } else if (isString(matcher)) {
17754 // Strings match exactly except for 2 wildcards - '*' and '**'.
17755 // '*' matches any character except those from the set ':/.?&'.
17756 // '**' matches any character (like .* in a RegExp).
17757 // More than 2 *'s raises an error as it's ill defined.
17758 if (matcher.indexOf('***') > -1) {
17759 throw $sceMinErr('iwcard',
17760 'Illegal sequence *** in string matcher. String: {0}', matcher);
17762 matcher = escapeForRegexp(matcher).
17763 replace('\\*\\*', '.*').
17764 replace('\\*', '[^:/.?&;]*');
17765 return new RegExp('^' + matcher + '$');
17766 } else if (isRegExp(matcher)) {
17767 // The only other type of matcher allowed is a Regexp.
17768 // Match entire URL / disallow partial matches.
17769 // Flags are reset (i.e. no global, ignoreCase or multiline)
17770 return new RegExp('^' + matcher.source + '$');
17772 throw $sceMinErr('imatcher',
17773 'Matchers may only be "self", string patterns or RegExp objects');
17778 function adjustMatchers(matchers) {
17779 var adjustedMatchers = [];
17780 if (isDefined(matchers)) {
17781 forEach(matchers, function(matcher) {
17782 adjustedMatchers.push(adjustMatcher(matcher));
17785 return adjustedMatchers;
17791 * @name $sceDelegate
17796 * `$sceDelegate` is a service that is used by the `$sce` service to provide {@link ng.$sce Strict
17797 * Contextual Escaping (SCE)} services to AngularJS.
17799 * Typically, you would configure or override the {@link ng.$sceDelegate $sceDelegate} instead of
17800 * the `$sce` service to customize the way Strict Contextual Escaping works in AngularJS. This is
17801 * because, while the `$sce` provides numerous shorthand methods, etc., you really only need to
17802 * override 3 core functions (`trustAs`, `getTrusted` and `valueOf`) to replace the way things
17803 * work because `$sce` delegates to `$sceDelegate` for these operations.
17805 * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} to configure this service.
17807 * The default instance of `$sceDelegate` should work out of the box with little pain. While you
17808 * can override it completely to change the behavior of `$sce`, the common case would
17809 * involve configuring the {@link ng.$sceDelegateProvider $sceDelegateProvider} instead by setting
17810 * your own whitelists and blacklists for trusting URLs used for loading AngularJS resources such as
17811 * templates. Refer {@link ng.$sceDelegateProvider#resourceUrlWhitelist
17812 * $sceDelegateProvider.resourceUrlWhitelist} and {@link
17813 * ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist}
17818 * @name $sceDelegateProvider
17821 * The `$sceDelegateProvider` provider allows developers to configure the {@link ng.$sceDelegate
17822 * $sceDelegate} service. This allows one to get/set the whitelists and blacklists used to ensure
17823 * that the URLs used for sourcing Angular templates are safe. Refer {@link
17824 * ng.$sceDelegateProvider#resourceUrlWhitelist $sceDelegateProvider.resourceUrlWhitelist} and
17825 * {@link ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist}
17827 * For the general details about this service in Angular, read the main page for {@link ng.$sce
17828 * Strict Contextual Escaping (SCE)}.
17830 * **Example**: Consider the following case. <a name="example"></a>
17832 * - your app is hosted at url `http://myapp.example.com/`
17833 * - but some of your templates are hosted on other domains you control such as
17834 * `http://srv01.assets.example.com/`, `http://srv02.assets.example.com/`, etc.
17835 * - and you have an open redirect at `http://myapp.example.com/clickThru?...`.
17837 * Here is what a secure configuration for this scenario might look like:
17840 * angular.module('myApp', []).config(function($sceDelegateProvider) {
17841 * $sceDelegateProvider.resourceUrlWhitelist([
17842 * // Allow same origin resource loads.
17844 * // Allow loading from our assets domain. Notice the difference between * and **.
17845 * 'http://srv*.assets.example.com/**'
17848 * // The blacklist overrides the whitelist so the open redirect here is blocked.
17849 * $sceDelegateProvider.resourceUrlBlacklist([
17850 * 'http://myapp.example.com/clickThru**'
17856 function $SceDelegateProvider() {
17857 this.SCE_CONTEXTS = SCE_CONTEXTS;
17859 // Resource URLs can also be trusted by policy.
17860 var resourceUrlWhitelist = ['self'],
17861 resourceUrlBlacklist = [];
17865 * @name $sceDelegateProvider#resourceUrlWhitelist
17868 * @param {Array=} whitelist When provided, replaces the resourceUrlWhitelist with the value
17869 * provided. This must be an array or null. A snapshot of this array is used so further
17870 * changes to the array are ignored.
17872 * Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
17873 * allowed in this array.
17875 * <div class="alert alert-warning">
17876 * **Note:** an empty whitelist array will block all URLs!
17879 * @return {Array} the currently set whitelist array.
17881 * The **default value** when no whitelist has been explicitly set is `['self']` allowing only
17882 * same origin resource requests.
17885 * Sets/Gets the whitelist of trusted resource URLs.
17887 this.resourceUrlWhitelist = function(value) {
17888 if (arguments.length) {
17889 resourceUrlWhitelist = adjustMatchers(value);
17891 return resourceUrlWhitelist;
17896 * @name $sceDelegateProvider#resourceUrlBlacklist
17899 * @param {Array=} blacklist When provided, replaces the resourceUrlBlacklist with the value
17900 * provided. This must be an array or null. A snapshot of this array is used so further
17901 * changes to the array are ignored.
17903 * Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
17904 * allowed in this array.
17906 * The typical usage for the blacklist is to **block
17907 * [open redirects](http://cwe.mitre.org/data/definitions/601.html)** served by your domain as
17908 * these would otherwise be trusted but actually return content from the redirected domain.
17910 * Finally, **the blacklist overrides the whitelist** and has the final say.
17912 * @return {Array} the currently set blacklist array.
17914 * The **default value** when no whitelist has been explicitly set is the empty array (i.e. there
17915 * is no blacklist.)
17918 * Sets/Gets the blacklist of trusted resource URLs.
17921 this.resourceUrlBlacklist = function(value) {
17922 if (arguments.length) {
17923 resourceUrlBlacklist = adjustMatchers(value);
17925 return resourceUrlBlacklist;
17928 this.$get = ['$injector', function($injector) {
17930 var htmlSanitizer = function htmlSanitizer(html) {
17931 throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.');
17934 if ($injector.has('$sanitize')) {
17935 htmlSanitizer = $injector.get('$sanitize');
17939 function matchUrl(matcher, parsedUrl) {
17940 if (matcher === 'self') {
17941 return urlIsSameOrigin(parsedUrl);
17943 // definitely a regex. See adjustMatchers()
17944 return !!matcher.exec(parsedUrl.href);
17948 function isResourceUrlAllowedByPolicy(url) {
17949 var parsedUrl = urlResolve(url.toString());
17950 var i, n, allowed = false;
17951 // Ensure that at least one item from the whitelist allows this url.
17952 for (i = 0, n = resourceUrlWhitelist.length; i < n; i++) {
17953 if (matchUrl(resourceUrlWhitelist[i], parsedUrl)) {
17959 // Ensure that no item from the blacklist blocked this url.
17960 for (i = 0, n = resourceUrlBlacklist.length; i < n; i++) {
17961 if (matchUrl(resourceUrlBlacklist[i], parsedUrl)) {
17970 function generateHolderType(Base) {
17971 var holderType = function TrustedValueHolderType(trustedValue) {
17972 this.$$unwrapTrustedValue = function() {
17973 return trustedValue;
17977 holderType.prototype = new Base();
17979 holderType.prototype.valueOf = function sceValueOf() {
17980 return this.$$unwrapTrustedValue();
17982 holderType.prototype.toString = function sceToString() {
17983 return this.$$unwrapTrustedValue().toString();
17988 var trustedValueHolderBase = generateHolderType(),
17991 byType[SCE_CONTEXTS.HTML] = generateHolderType(trustedValueHolderBase);
17992 byType[SCE_CONTEXTS.CSS] = generateHolderType(trustedValueHolderBase);
17993 byType[SCE_CONTEXTS.URL] = generateHolderType(trustedValueHolderBase);
17994 byType[SCE_CONTEXTS.JS] = generateHolderType(trustedValueHolderBase);
17995 byType[SCE_CONTEXTS.RESOURCE_URL] = generateHolderType(byType[SCE_CONTEXTS.URL]);
17999 * @name $sceDelegate#trustAs
18002 * Returns an object that is trusted by angular for use in specified strict
18003 * contextual escaping contexts (such as ng-bind-html, ng-include, any src
18004 * attribute interpolation, any dom event binding attribute interpolation
18005 * such as for onclick, etc.) that uses the provided value.
18006 * See {@link ng.$sce $sce} for enabling strict contextual escaping.
18008 * @param {string} type The kind of context in which this value is safe for use. e.g. url,
18009 * resourceUrl, html, js and css.
18010 * @param {*} value The value that that should be considered trusted/safe.
18011 * @returns {*} A value that can be used to stand in for the provided `value` in places
18012 * where Angular expects a $sce.trustAs() return value.
18014 function trustAs(type, trustedValue) {
18015 var Constructor = (byType.hasOwnProperty(type) ? byType[type] : null);
18016 if (!Constructor) {
18017 throw $sceMinErr('icontext',
18018 'Attempted to trust a value in invalid context. Context: {0}; Value: {1}',
18019 type, trustedValue);
18021 if (trustedValue === null || isUndefined(trustedValue) || trustedValue === '') {
18022 return trustedValue;
18024 // All the current contexts in SCE_CONTEXTS happen to be strings. In order to avoid trusting
18025 // mutable objects, we ensure here that the value passed in is actually a string.
18026 if (typeof trustedValue !== 'string') {
18027 throw $sceMinErr('itype',
18028 'Attempted to trust a non-string value in a content requiring a string: Context: {0}',
18031 return new Constructor(trustedValue);
18036 * @name $sceDelegate#valueOf
18039 * If the passed parameter had been returned by a prior call to {@link ng.$sceDelegate#trustAs
18040 * `$sceDelegate.trustAs`}, returns the value that had been passed to {@link
18041 * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}.
18043 * If the passed parameter is not a value that had been returned by {@link
18044 * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}, returns it as-is.
18046 * @param {*} value The result of a prior {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}
18047 * call or anything else.
18048 * @returns {*} The `value` that was originally provided to {@link ng.$sceDelegate#trustAs
18049 * `$sceDelegate.trustAs`} if `value` is the result of such a call. Otherwise, returns
18050 * `value` unchanged.
18052 function valueOf(maybeTrusted) {
18053 if (maybeTrusted instanceof trustedValueHolderBase) {
18054 return maybeTrusted.$$unwrapTrustedValue();
18056 return maybeTrusted;
18062 * @name $sceDelegate#getTrusted
18065 * Takes the result of a {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`} call and
18066 * returns the originally supplied value if the queried context type is a supertype of the
18067 * created type. If this condition isn't satisfied, throws an exception.
18069 * <div class="alert alert-danger">
18070 * Disabling auto-escaping is extremely dangerous, it usually creates a Cross Site Scripting
18071 * (XSS) vulnerability in your application.
18074 * @param {string} type The kind of context in which this value is to be used.
18075 * @param {*} maybeTrusted The result of a prior {@link ng.$sceDelegate#trustAs
18076 * `$sceDelegate.trustAs`} call.
18077 * @returns {*} The value the was originally provided to {@link ng.$sceDelegate#trustAs
18078 * `$sceDelegate.trustAs`} if valid in this context. Otherwise, throws an exception.
18080 function getTrusted(type, maybeTrusted) {
18081 if (maybeTrusted === null || isUndefined(maybeTrusted) || maybeTrusted === '') {
18082 return maybeTrusted;
18084 var constructor = (byType.hasOwnProperty(type) ? byType[type] : null);
18085 if (constructor && maybeTrusted instanceof constructor) {
18086 return maybeTrusted.$$unwrapTrustedValue();
18088 // If we get here, then we may only take one of two actions.
18089 // 1. sanitize the value for the requested type, or
18090 // 2. throw an exception.
18091 if (type === SCE_CONTEXTS.RESOURCE_URL) {
18092 if (isResourceUrlAllowedByPolicy(maybeTrusted)) {
18093 return maybeTrusted;
18095 throw $sceMinErr('insecurl',
18096 'Blocked loading resource from url not allowed by $sceDelegate policy. URL: {0}',
18097 maybeTrusted.toString());
18099 } else if (type === SCE_CONTEXTS.HTML) {
18100 return htmlSanitizer(maybeTrusted);
18102 throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.');
18105 return { trustAs: trustAs,
18106 getTrusted: getTrusted,
18107 valueOf: valueOf };
18114 * @name $sceProvider
18117 * The $sceProvider provider allows developers to configure the {@link ng.$sce $sce} service.
18118 * - enable/disable Strict Contextual Escaping (SCE) in a module
18119 * - override the default implementation with a custom delegate
18121 * Read more about {@link ng.$sce Strict Contextual Escaping (SCE)}.
18124 /* jshint maxlen: false*/
18133 * `$sce` is a service that provides Strict Contextual Escaping services to AngularJS.
18135 * # Strict Contextual Escaping
18137 * Strict Contextual Escaping (SCE) is a mode in which AngularJS requires bindings in certain
18138 * contexts to result in a value that is marked as safe to use for that context. One example of
18139 * such a context is binding arbitrary html controlled by the user via `ng-bind-html`. We refer
18140 * to these contexts as privileged or SCE contexts.
18142 * As of version 1.2, Angular ships with SCE enabled by default.
18144 * Note: When enabled (the default), IE<11 in quirks mode is not supported. In this mode, IE<11 allow
18145 * one to execute arbitrary javascript by the use of the expression() syntax. Refer
18146 * <http://blogs.msdn.com/b/ie/archive/2008/10/16/ending-expressions.aspx> to learn more about them.
18147 * You can ensure your document is in standards mode and not quirks mode by adding `<!doctype html>`
18148 * to the top of your HTML document.
18150 * SCE assists in writing code in way that (a) is secure by default and (b) makes auditing for
18151 * security vulnerabilities such as XSS, clickjacking, etc. a lot easier.
18153 * Here's an example of a binding in a privileged context:
18156 * <input ng-model="userHtml" aria-label="User input">
18157 * <div ng-bind-html="userHtml"></div>
18160 * Notice that `ng-bind-html` is bound to `userHtml` controlled by the user. With SCE
18161 * disabled, this application allows the user to render arbitrary HTML into the DIV.
18162 * In a more realistic example, one may be rendering user comments, blog articles, etc. via
18163 * bindings. (HTML is just one example of a context where rendering user controlled input creates
18164 * security vulnerabilities.)
18166 * For the case of HTML, you might use a library, either on the client side, or on the server side,
18167 * to sanitize unsafe HTML before binding to the value and rendering it in the document.
18169 * How would you ensure that every place that used these types of bindings was bound to a value that
18170 * was sanitized by your library (or returned as safe for rendering by your server?) How can you
18171 * ensure that you didn't accidentally delete the line that sanitized the value, or renamed some
18172 * properties/fields and forgot to update the binding to the sanitized value?
18174 * To be secure by default, you want to ensure that any such bindings are disallowed unless you can
18175 * determine that something explicitly says it's safe to use a value for binding in that
18176 * context. You can then audit your code (a simple grep would do) to ensure that this is only done
18177 * for those values that you can easily tell are safe - because they were received from your server,
18178 * sanitized by your library, etc. You can organize your codebase to help with this - perhaps
18179 * allowing only the files in a specific directory to do this. Ensuring that the internal API
18180 * exposed by that code doesn't markup arbitrary values as safe then becomes a more manageable task.
18182 * In the case of AngularJS' SCE service, one uses {@link ng.$sce#trustAs $sce.trustAs}
18183 * (and shorthand methods such as {@link ng.$sce#trustAsHtml $sce.trustAsHtml}, etc.) to
18184 * obtain values that will be accepted by SCE / privileged contexts.
18187 * ## How does it work?
18189 * In privileged contexts, directives and code will bind to the result of {@link ng.$sce#getTrusted
18190 * $sce.getTrusted(context, value)} rather than to the value directly. Directives use {@link
18191 * ng.$sce#parseAs $sce.parseAs} rather than `$parse` to watch attribute bindings, which performs the
18192 * {@link ng.$sce#getTrusted $sce.getTrusted} behind the scenes on non-constant literals.
18194 * As an example, {@link ng.directive:ngBindHtml ngBindHtml} uses {@link
18195 * ng.$sce#parseAsHtml $sce.parseAsHtml(binding expression)}. Here's the actual code (slightly
18199 * var ngBindHtmlDirective = ['$sce', function($sce) {
18200 * return function(scope, element, attr) {
18201 * scope.$watch($sce.parseAsHtml(attr.ngBindHtml), function(value) {
18202 * element.html(value || '');
18208 * ## Impact on loading templates
18210 * This applies both to the {@link ng.directive:ngInclude `ng-include`} directive as well as
18211 * `templateUrl`'s specified by {@link guide/directive directives}.
18213 * By default, Angular only loads templates from the same domain and protocol as the application
18214 * document. This is done by calling {@link ng.$sce#getTrustedResourceUrl
18215 * $sce.getTrustedResourceUrl} on the template URL. To load templates from other domains and/or
18216 * protocols, you may either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist
18217 * them} or {@link ng.$sce#trustAsResourceUrl wrap it} into a trusted value.
18221 * [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest)
18222 * and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/)
18223 * policy apply in addition to this and may further restrict whether the template is successfully
18224 * loaded. This means that without the right CORS policy, loading templates from a different domain
18225 * won't work on all browsers. Also, loading templates from `file://` URL does not work on some
18228 * ## This feels like too much overhead
18230 * It's important to remember that SCE only applies to interpolation expressions.
18232 * If your expressions are constant literals, they're automatically trusted and you don't need to
18233 * call `$sce.trustAs` on them (remember to include the `ngSanitize` module) (e.g.
18234 * `<div ng-bind-html="'<b>implicitly trusted</b>'"></div>`) just works.
18236 * Additionally, `a[href]` and `img[src]` automatically sanitize their URLs and do not pass them
18237 * through {@link ng.$sce#getTrusted $sce.getTrusted}. SCE doesn't play a role here.
18239 * The included {@link ng.$sceDelegate $sceDelegate} comes with sane defaults to allow you to load
18240 * templates in `ng-include` from your application's domain without having to even know about SCE.
18241 * It blocks loading templates from other domains or loading templates over http from an https
18242 * served document. You can change these by setting your own custom {@link
18243 * ng.$sceDelegateProvider#resourceUrlWhitelist whitelists} and {@link
18244 * ng.$sceDelegateProvider#resourceUrlBlacklist blacklists} for matching such URLs.
18246 * This significantly reduces the overhead. It is far easier to pay the small overhead and have an
18247 * application that's secure and can be audited to verify that with much more ease than bolting
18248 * security onto an application later.
18250 * <a name="contexts"></a>
18251 * ## What trusted context types are supported?
18253 * | Context | Notes |
18254 * |---------------------|----------------|
18255 * | `$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. |
18256 * | `$sce.CSS` | For CSS that's safe to source into the application. Currently unused. Feel free to use it in your own directives. |
18257 * | `$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. |
18258 * | `$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. |
18259 * | `$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. |
18261 * ## Format of items in {@link ng.$sceDelegateProvider#resourceUrlWhitelist resourceUrlWhitelist}/{@link ng.$sceDelegateProvider#resourceUrlBlacklist Blacklist} <a name="resourceUrlPatternItem"></a>
18263 * Each element in these arrays must be one of the following:
18266 * - The special **string**, `'self'`, can be used to match against all URLs of the **same
18267 * domain** as the application document using the **same protocol**.
18268 * - **String** (except the special value `'self'`)
18269 * - The string is matched against the full *normalized / absolute URL* of the resource
18270 * being tested (substring matches are not good enough.)
18271 * - There are exactly **two wildcard sequences** - `*` and `**`. All other characters
18272 * match themselves.
18273 * - `*`: matches zero or more occurrences of any character other than one of the following 6
18274 * characters: '`:`', '`/`', '`.`', '`?`', '`&`' and '`;`'. It's a useful wildcard for use
18276 * - `**`: matches zero or more occurrences of *any* character. As such, it's not
18277 * appropriate for use in a scheme, domain, etc. as it would match too much. (e.g.
18278 * http://**.example.com/ would match http://evil.com/?ignore=.example.com/ and that might
18279 * not have been the intention.) Its usage at the very end of the path is ok. (e.g.
18280 * http://foo.example.com/templates/**).
18281 * - **RegExp** (*see caveat below*)
18282 * - *Caveat*: While regular expressions are powerful and offer great flexibility, their syntax
18283 * (and all the inevitable escaping) makes them *harder to maintain*. It's easy to
18284 * accidentally introduce a bug when one updates a complex expression (imho, all regexes should
18285 * have good test coverage). For instance, the use of `.` in the regex is correct only in a
18286 * small number of cases. A `.` character in the regex used when matching the scheme or a
18287 * subdomain could be matched against a `:` or literal `.` that was likely not intended. It
18288 * is highly recommended to use the string patterns and only fall back to regular expressions
18289 * as a last resort.
18290 * - The regular expression must be an instance of RegExp (i.e. not a string.) It is
18291 * matched against the **entire** *normalized / absolute URL* of the resource being tested
18292 * (even when the RegExp did not have the `^` and `$` codes.) In addition, any flags
18293 * present on the RegExp (such as multiline, global, ignoreCase) are ignored.
18294 * - If you are generating your JavaScript from some other templating engine (not
18295 * recommended, e.g. in issue [#4006](https://github.com/angular/angular.js/issues/4006)),
18296 * remember to escape your regular expression (and be aware that you might need more than
18297 * one level of escaping depending on your templating engine and the way you interpolated
18298 * the value.) Do make use of your platform's escaping mechanism as it might be good
18299 * enough before coding your own. E.g. Ruby has
18300 * [Regexp.escape(str)](http://www.ruby-doc.org/core-2.0.0/Regexp.html#method-c-escape)
18301 * and Python has [re.escape](http://docs.python.org/library/re.html#re.escape).
18302 * Javascript lacks a similar built in function for escaping. Take a look at Google
18303 * Closure library's [goog.string.regExpEscape(s)](
18304 * http://docs.closure-library.googlecode.com/git/closure_goog_string_string.js.source.html#line962).
18306 * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} for an example.
18308 * ## Show me an example using SCE.
18310 * <example module="mySceApp" deps="angular-sanitize.js">
18311 * <file name="index.html">
18312 * <div ng-controller="AppController as myCtrl">
18313 * <i ng-bind-html="myCtrl.explicitlyTrustedHtml" id="explicitlyTrustedHtml"></i><br><br>
18314 * <b>User comments</b><br>
18315 * By default, HTML that isn't explicitly trusted (e.g. Alice's comment) is sanitized when
18316 * $sanitize is available. If $sanitize isn't available, this results in an error instead of an
18318 * <div class="well">
18319 * <div ng-repeat="userComment in myCtrl.userComments">
18320 * <b>{{userComment.name}}</b>:
18321 * <span ng-bind-html="userComment.htmlComment" class="htmlComment"></span>
18328 * <file name="script.js">
18329 * angular.module('mySceApp', ['ngSanitize'])
18330 * .controller('AppController', ['$http', '$templateCache', '$sce',
18331 * function($http, $templateCache, $sce) {
18333 * $http.get("test_data.json", {cache: $templateCache}).success(function(userComments) {
18334 * self.userComments = userComments;
18336 * self.explicitlyTrustedHtml = $sce.trustAsHtml(
18337 * '<span onmouseover="this.textContent="Explicitly trusted HTML bypasses ' +
18338 * 'sanitization."">Hover over this text.</span>');
18342 * <file name="test_data.json">
18344 * { "name": "Alice",
18346 * "<span onmouseover='this.textContent=\"PWN3D!\"'>Is <i>anyone</i> reading this?</span>"
18349 * "htmlComment": "<i>Yes!</i> Am I the only other one?"
18354 * <file name="protractor.js" type="protractor">
18355 * describe('SCE doc demo', function() {
18356 * it('should sanitize untrusted values', function() {
18357 * expect(element.all(by.css('.htmlComment')).first().getInnerHtml())
18358 * .toBe('<span>Is <i>anyone</i> reading this?</span>');
18361 * it('should NOT sanitize explicitly trusted values', function() {
18362 * expect(element(by.id('explicitlyTrustedHtml')).getInnerHtml()).toBe(
18363 * '<span onmouseover="this.textContent="Explicitly trusted HTML bypasses ' +
18364 * 'sanitization."">Hover over this text.</span>');
18372 * ## Can I disable SCE completely?
18374 * Yes, you can. However, this is strongly discouraged. SCE gives you a lot of security benefits
18375 * for little coding overhead. It will be much harder to take an SCE disabled application and
18376 * either secure it on your own or enable SCE at a later stage. It might make sense to disable SCE
18377 * for cases where you have a lot of existing code that was written before SCE was introduced and
18378 * you're migrating them a module at a time.
18380 * That said, here's how you can completely disable SCE:
18383 * angular.module('myAppWithSceDisabledmyApp', []).config(function($sceProvider) {
18384 * // Completely disable SCE. For demonstration purposes only!
18385 * // Do not use in new projects.
18386 * $sceProvider.enabled(false);
18391 /* jshint maxlen: 100 */
18393 function $SceProvider() {
18394 var enabled = true;
18398 * @name $sceProvider#enabled
18401 * @param {boolean=} value If provided, then enables/disables SCE.
18402 * @return {boolean} true if SCE is enabled, false otherwise.
18405 * Enables/disables SCE and returns the current value.
18407 this.enabled = function(value) {
18408 if (arguments.length) {
18415 /* Design notes on the default implementation for SCE.
18417 * The API contract for the SCE delegate
18418 * -------------------------------------
18419 * The SCE delegate object must provide the following 3 methods:
18421 * - trustAs(contextEnum, value)
18422 * This method is used to tell the SCE service that the provided value is OK to use in the
18423 * contexts specified by contextEnum. It must return an object that will be accepted by
18424 * getTrusted() for a compatible contextEnum and return this value.
18427 * For values that were not produced by trustAs(), return them as is. For values that were
18428 * produced by trustAs(), return the corresponding input value to trustAs. Basically, if
18429 * trustAs is wrapping the given values into some type, this operation unwraps it when given
18432 * - getTrusted(contextEnum, value)
18433 * This function should return the a value that is safe to use in the context specified by
18434 * contextEnum or throw and exception otherwise.
18436 * NOTE: This contract deliberately does NOT state that values returned by trustAs() must be
18437 * opaque or wrapped in some holder object. That happens to be an implementation detail. For
18438 * instance, an implementation could maintain a registry of all trusted objects by context. In
18439 * such a case, trustAs() would return the same object that was passed in. getTrusted() would
18440 * return the same object passed in if it was found in the registry under a compatible context or
18441 * throw an exception otherwise. An implementation might only wrap values some of the time based
18442 * on some criteria. getTrusted() might return a value and not throw an exception for special
18443 * constants or objects even if not wrapped. All such implementations fulfill this contract.
18446 * A note on the inheritance model for SCE contexts
18447 * ------------------------------------------------
18448 * I've used inheritance and made RESOURCE_URL wrapped types a subtype of URL wrapped types. This
18449 * is purely an implementation details.
18451 * The contract is simply this:
18453 * getTrusted($sce.RESOURCE_URL, value) succeeding implies that getTrusted($sce.URL, value)
18454 * will also succeed.
18456 * Inheritance happens to capture this in a natural way. In some future, we
18457 * may not use inheritance anymore. That is OK because no code outside of
18458 * sce.js and sceSpecs.js would need to be aware of this detail.
18461 this.$get = ['$parse', '$sceDelegate', function(
18462 $parse, $sceDelegate) {
18463 // Prereq: Ensure that we're not running in IE<11 quirks mode. In that mode, IE < 11 allow
18464 // the "expression(javascript expression)" syntax which is insecure.
18465 if (enabled && msie < 8) {
18466 throw $sceMinErr('iequirks',
18467 'Strict Contextual Escaping does not support Internet Explorer version < 11 in quirks ' +
18468 'mode. You can fix this by adding the text <!doctype html> to the top of your HTML ' +
18469 'document. See http://docs.angularjs.org/api/ng.$sce for more information.');
18472 var sce = shallowCopy(SCE_CONTEXTS);
18476 * @name $sce#isEnabled
18479 * @return {Boolean} true if SCE is enabled, false otherwise. If you want to set the value, you
18480 * have to do it at module config time on {@link ng.$sceProvider $sceProvider}.
18483 * Returns a boolean indicating if SCE is enabled.
18485 sce.isEnabled = function() {
18488 sce.trustAs = $sceDelegate.trustAs;
18489 sce.getTrusted = $sceDelegate.getTrusted;
18490 sce.valueOf = $sceDelegate.valueOf;
18493 sce.trustAs = sce.getTrusted = function(type, value) { return value; };
18494 sce.valueOf = identity;
18499 * @name $sce#parseAs
18502 * Converts Angular {@link guide/expression expression} into a function. This is like {@link
18503 * ng.$parse $parse} and is identical when the expression is a literal constant. Otherwise, it
18504 * wraps the expression in a call to {@link ng.$sce#getTrusted $sce.getTrusted(*type*,
18507 * @param {string} type The kind of SCE context in which this result will be used.
18508 * @param {string} expression String expression to compile.
18509 * @returns {function(context, locals)} a function which represents the compiled expression:
18511 * * `context` – `{object}` – an object against which any expressions embedded in the strings
18512 * are evaluated against (typically a scope object).
18513 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
18516 sce.parseAs = function sceParseAs(type, expr) {
18517 var parsed = $parse(expr);
18518 if (parsed.literal && parsed.constant) {
18521 return $parse(expr, function(value) {
18522 return sce.getTrusted(type, value);
18529 * @name $sce#trustAs
18532 * Delegates to {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}. As such,
18533 * returns an object that is trusted by angular for use in specified strict contextual
18534 * escaping contexts (such as ng-bind-html, ng-include, any src attribute
18535 * interpolation, any dom event binding attribute interpolation such as for onclick, etc.)
18536 * that uses the provided value. See * {@link ng.$sce $sce} for enabling strict contextual
18539 * @param {string} type The kind of context in which this value is safe for use. e.g. url,
18540 * resourceUrl, html, js and css.
18541 * @param {*} value The value that that should be considered trusted/safe.
18542 * @returns {*} A value that can be used to stand in for the provided `value` in places
18543 * where Angular expects a $sce.trustAs() return value.
18548 * @name $sce#trustAsHtml
18551 * Shorthand method. `$sce.trustAsHtml(value)` →
18552 * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.HTML, value)`}
18554 * @param {*} value The value to trustAs.
18555 * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedHtml
18556 * $sce.getTrustedHtml(value)} to obtain the original value. (privileged directives
18557 * only accept expressions that are either literal constants or are the
18558 * return value of {@link ng.$sce#trustAs $sce.trustAs}.)
18563 * @name $sce#trustAsUrl
18566 * Shorthand method. `$sce.trustAsUrl(value)` →
18567 * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.URL, value)`}
18569 * @param {*} value The value to trustAs.
18570 * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedUrl
18571 * $sce.getTrustedUrl(value)} to obtain the original value. (privileged directives
18572 * only accept expressions that are either literal constants or are the
18573 * return value of {@link ng.$sce#trustAs $sce.trustAs}.)
18578 * @name $sce#trustAsResourceUrl
18581 * Shorthand method. `$sce.trustAsResourceUrl(value)` →
18582 * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.RESOURCE_URL, value)`}
18584 * @param {*} value The value to trustAs.
18585 * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedResourceUrl
18586 * $sce.getTrustedResourceUrl(value)} to obtain the original value. (privileged directives
18587 * only accept expressions that are either literal constants or are the return
18588 * value of {@link ng.$sce#trustAs $sce.trustAs}.)
18593 * @name $sce#trustAsJs
18596 * Shorthand method. `$sce.trustAsJs(value)` →
18597 * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.JS, value)`}
18599 * @param {*} value The value to trustAs.
18600 * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedJs
18601 * $sce.getTrustedJs(value)} to obtain the original value. (privileged directives
18602 * only accept expressions that are either literal constants or are the
18603 * return value of {@link ng.$sce#trustAs $sce.trustAs}.)
18608 * @name $sce#getTrusted
18611 * Delegates to {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted`}. As such,
18612 * takes the result of a {@link ng.$sce#trustAs `$sce.trustAs`}() call and returns the
18613 * originally supplied value if the queried context type is a supertype of the created type.
18614 * If this condition isn't satisfied, throws an exception.
18616 * @param {string} type The kind of context in which this value is to be used.
18617 * @param {*} maybeTrusted The result of a prior {@link ng.$sce#trustAs `$sce.trustAs`}
18619 * @returns {*} The value the was originally provided to
18620 * {@link ng.$sce#trustAs `$sce.trustAs`} if valid in this context.
18621 * Otherwise, throws an exception.
18626 * @name $sce#getTrustedHtml
18629 * Shorthand method. `$sce.getTrustedHtml(value)` →
18630 * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.HTML, value)`}
18632 * @param {*} value The value to pass to `$sce.getTrusted`.
18633 * @returns {*} The return value of `$sce.getTrusted($sce.HTML, value)`
18638 * @name $sce#getTrustedCss
18641 * Shorthand method. `$sce.getTrustedCss(value)` →
18642 * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.CSS, value)`}
18644 * @param {*} value The value to pass to `$sce.getTrusted`.
18645 * @returns {*} The return value of `$sce.getTrusted($sce.CSS, value)`
18650 * @name $sce#getTrustedUrl
18653 * Shorthand method. `$sce.getTrustedUrl(value)` →
18654 * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.URL, value)`}
18656 * @param {*} value The value to pass to `$sce.getTrusted`.
18657 * @returns {*} The return value of `$sce.getTrusted($sce.URL, value)`
18662 * @name $sce#getTrustedResourceUrl
18665 * Shorthand method. `$sce.getTrustedResourceUrl(value)` →
18666 * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.RESOURCE_URL, value)`}
18668 * @param {*} value The value to pass to `$sceDelegate.getTrusted`.
18669 * @returns {*} The return value of `$sce.getTrusted($sce.RESOURCE_URL, value)`
18674 * @name $sce#getTrustedJs
18677 * Shorthand method. `$sce.getTrustedJs(value)` →
18678 * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.JS, value)`}
18680 * @param {*} value The value to pass to `$sce.getTrusted`.
18681 * @returns {*} The return value of `$sce.getTrusted($sce.JS, value)`
18686 * @name $sce#parseAsHtml
18689 * Shorthand method. `$sce.parseAsHtml(expression string)` →
18690 * {@link ng.$sce#parseAs `$sce.parseAs($sce.HTML, value)`}
18692 * @param {string} expression String expression to compile.
18693 * @returns {function(context, locals)} a function which represents the compiled expression:
18695 * * `context` – `{object}` – an object against which any expressions embedded in the strings
18696 * are evaluated against (typically a scope object).
18697 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
18703 * @name $sce#parseAsCss
18706 * Shorthand method. `$sce.parseAsCss(value)` →
18707 * {@link ng.$sce#parseAs `$sce.parseAs($sce.CSS, value)`}
18709 * @param {string} expression String expression to compile.
18710 * @returns {function(context, locals)} a function which represents the compiled expression:
18712 * * `context` – `{object}` – an object against which any expressions embedded in the strings
18713 * are evaluated against (typically a scope object).
18714 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
18720 * @name $sce#parseAsUrl
18723 * Shorthand method. `$sce.parseAsUrl(value)` →
18724 * {@link ng.$sce#parseAs `$sce.parseAs($sce.URL, value)`}
18726 * @param {string} expression String expression to compile.
18727 * @returns {function(context, locals)} a function which represents the compiled expression:
18729 * * `context` – `{object}` – an object against which any expressions embedded in the strings
18730 * are evaluated against (typically a scope object).
18731 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
18737 * @name $sce#parseAsResourceUrl
18740 * Shorthand method. `$sce.parseAsResourceUrl(value)` →
18741 * {@link ng.$sce#parseAs `$sce.parseAs($sce.RESOURCE_URL, value)`}
18743 * @param {string} expression String expression to compile.
18744 * @returns {function(context, locals)} a function which represents the compiled expression:
18746 * * `context` – `{object}` – an object against which any expressions embedded in the strings
18747 * are evaluated against (typically a scope object).
18748 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
18754 * @name $sce#parseAsJs
18757 * Shorthand method. `$sce.parseAsJs(value)` →
18758 * {@link ng.$sce#parseAs `$sce.parseAs($sce.JS, value)`}
18760 * @param {string} expression String expression to compile.
18761 * @returns {function(context, locals)} a function which represents the compiled expression:
18763 * * `context` – `{object}` – an object against which any expressions embedded in the strings
18764 * are evaluated against (typically a scope object).
18765 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
18769 // Shorthand delegations.
18770 var parse = sce.parseAs,
18771 getTrusted = sce.getTrusted,
18772 trustAs = sce.trustAs;
18774 forEach(SCE_CONTEXTS, function(enumValue, name) {
18775 var lName = lowercase(name);
18776 sce[camelCase("parse_as_" + lName)] = function(expr) {
18777 return parse(enumValue, expr);
18779 sce[camelCase("get_trusted_" + lName)] = function(value) {
18780 return getTrusted(enumValue, value);
18782 sce[camelCase("trust_as_" + lName)] = function(value) {
18783 return trustAs(enumValue, value);
18792 * !!! This is an undocumented "private" service !!!
18795 * @requires $window
18796 * @requires $document
18798 * @property {boolean} history Does the browser support html5 history api ?
18799 * @property {boolean} transitions Does the browser support CSS transition events ?
18800 * @property {boolean} animations Does the browser support CSS animation events ?
18803 * This is very simple implementation of testing browser's features.
18805 function $SnifferProvider() {
18806 this.$get = ['$window', '$document', function($window, $document) {
18807 var eventSupport = {},
18808 // Chrome Packaged Apps are not allowed to access `history.pushState`. They can be detected by
18809 // the presence of `chrome.app.runtime` (see https://developer.chrome.com/apps/api_index)
18810 isChromePackagedApp = $window.chrome && $window.chrome.app && $window.chrome.app.runtime,
18811 hasHistoryPushState = !isChromePackagedApp && $window.history && $window.history.pushState,
18813 toInt((/android (\d+)/.exec(lowercase(($window.navigator || {}).userAgent)) || [])[1]),
18814 boxee = /Boxee/i.test(($window.navigator || {}).userAgent),
18815 document = $document[0] || {},
18817 vendorRegex = /^(Moz|webkit|ms)(?=[A-Z])/,
18818 bodyStyle = document.body && document.body.style,
18819 transitions = false,
18820 animations = false,
18824 for (var prop in bodyStyle) {
18825 if (match = vendorRegex.exec(prop)) {
18826 vendorPrefix = match[0];
18827 vendorPrefix = vendorPrefix.substr(0, 1).toUpperCase() + vendorPrefix.substr(1);
18832 if (!vendorPrefix) {
18833 vendorPrefix = ('WebkitOpacity' in bodyStyle) && 'webkit';
18836 transitions = !!(('transition' in bodyStyle) || (vendorPrefix + 'Transition' in bodyStyle));
18837 animations = !!(('animation' in bodyStyle) || (vendorPrefix + 'Animation' in bodyStyle));
18839 if (android && (!transitions || !animations)) {
18840 transitions = isString(bodyStyle.webkitTransition);
18841 animations = isString(bodyStyle.webkitAnimation);
18847 // Android has history.pushState, but it does not update location correctly
18848 // so let's not use the history API at all.
18849 // http://code.google.com/p/android/issues/detail?id=17471
18850 // https://github.com/angular/angular.js/issues/904
18852 // older webkit browser (533.9) on Boxee box has exactly the same problem as Android has
18853 // so let's not use the history API also
18854 // We are purposefully using `!(android < 4)` to cover the case when `android` is undefined
18856 history: !!(hasHistoryPushState && !(android < 4) && !boxee),
18858 hasEvent: function(event) {
18859 // IE9 implements 'input' event it's so fubared that we rather pretend that it doesn't have
18860 // it. In particular the event is not fired when backspace or delete key are pressed or
18861 // when cut operation is performed.
18862 // IE10+ implements 'input' event but it erroneously fires under various situations,
18863 // e.g. when placeholder changes, or a form is focused.
18864 if (event === 'input' && msie <= 11) return false;
18866 if (isUndefined(eventSupport[event])) {
18867 var divElm = document.createElement('div');
18868 eventSupport[event] = 'on' + event in divElm;
18871 return eventSupport[event];
18874 vendorPrefix: vendorPrefix,
18875 transitions: transitions,
18876 animations: animations,
18882 var $templateRequestMinErr = minErr('$compile');
18886 * @name $templateRequestProvider
18888 * Used to configure the options passed to the {@link $http} service when making a template request.
18890 * For example, it can be used for specifying the "Accept" header that is sent to the server, when
18891 * requesting a template.
18893 function $TemplateRequestProvider() {
18899 * @name $templateRequestProvider#httpOptions
18901 * The options to be passed to the {@link $http} service when making the request.
18902 * You can use this to override options such as the "Accept" header for template requests.
18904 * The {@link $templateRequest} will set the `cache` and the `transformResponse` properties of the
18905 * options if not overridden here.
18907 * @param {string=} value new value for the {@link $http} options.
18908 * @returns {string|self} Returns the {@link $http} options when used as getter and self if used as setter.
18910 this.httpOptions = function(val) {
18915 return httpOptions;
18920 * @name $templateRequest
18923 * The `$templateRequest` service runs security checks then downloads the provided template using
18924 * `$http` and, upon success, stores the contents inside of `$templateCache`. If the HTTP request
18925 * fails or the response data of the HTTP request is empty, a `$compile` error will be thrown (the
18926 * exception can be thwarted by setting the 2nd parameter of the function to true). Note that the
18927 * contents of `$templateCache` are trusted, so the call to `$sce.getTrustedUrl(tpl)` is omitted
18928 * when `tpl` is of type string and `$templateCache` has the matching entry.
18930 * If you want to pass custom options to the `$http` service, such as setting the Accept header you
18931 * can configure this via {@link $templateRequestProvider#httpOptions}.
18933 * @param {string|TrustedResourceUrl} tpl The HTTP request template URL
18934 * @param {boolean=} ignoreRequestError Whether or not to ignore the exception when the request fails or the template is empty
18936 * @return {Promise} a promise for the HTTP response data of the given URL.
18938 * @property {number} totalPendingRequests total amount of pending template requests being downloaded.
18940 this.$get = ['$templateCache', '$http', '$q', '$sce', function($templateCache, $http, $q, $sce) {
18942 function handleRequestFn(tpl, ignoreRequestError) {
18943 handleRequestFn.totalPendingRequests++;
18945 // We consider the template cache holds only trusted templates, so
18946 // there's no need to go through whitelisting again for keys that already
18947 // are included in there. This also makes Angular accept any script
18948 // directive, no matter its name. However, we still need to unwrap trusted
18950 if (!isString(tpl) || !$templateCache.get(tpl)) {
18951 tpl = $sce.getTrustedResourceUrl(tpl);
18954 var transformResponse = $http.defaults && $http.defaults.transformResponse;
18956 if (isArray(transformResponse)) {
18957 transformResponse = transformResponse.filter(function(transformer) {
18958 return transformer !== defaultHttpResponseTransform;
18960 } else if (transformResponse === defaultHttpResponseTransform) {
18961 transformResponse = null;
18964 return $http.get(tpl, extend({
18965 cache: $templateCache,
18966 transformResponse: transformResponse
18968 ['finally'](function() {
18969 handleRequestFn.totalPendingRequests--;
18971 .then(function(response) {
18972 $templateCache.put(tpl, response.data);
18973 return response.data;
18976 function handleError(resp) {
18977 if (!ignoreRequestError) {
18978 throw $templateRequestMinErr('tpload', 'Failed to load template: {0} (HTTP status: {1} {2})',
18979 tpl, resp.status, resp.statusText);
18981 return $q.reject(resp);
18985 handleRequestFn.totalPendingRequests = 0;
18987 return handleRequestFn;
18991 function $$TestabilityProvider() {
18992 this.$get = ['$rootScope', '$browser', '$location',
18993 function($rootScope, $browser, $location) {
18996 * @name $testability
18999 * The private $$testability service provides a collection of methods for use when debugging
19000 * or by automated test and debugging tools.
19002 var testability = {};
19005 * @name $$testability#findBindings
19008 * Returns an array of elements that are bound (via ng-bind or {{}})
19009 * to expressions matching the input.
19011 * @param {Element} element The element root to search from.
19012 * @param {string} expression The binding expression to match.
19013 * @param {boolean} opt_exactMatch If true, only returns exact matches
19014 * for the expression. Filters and whitespace are ignored.
19016 testability.findBindings = function(element, expression, opt_exactMatch) {
19017 var bindings = element.getElementsByClassName('ng-binding');
19019 forEach(bindings, function(binding) {
19020 var dataBinding = angular.element(binding).data('$binding');
19022 forEach(dataBinding, function(bindingName) {
19023 if (opt_exactMatch) {
19024 var matcher = new RegExp('(^|\\s)' + escapeForRegexp(expression) + '(\\s|\\||$)');
19025 if (matcher.test(bindingName)) {
19026 matches.push(binding);
19029 if (bindingName.indexOf(expression) != -1) {
19030 matches.push(binding);
19040 * @name $$testability#findModels
19043 * Returns an array of elements that are two-way found via ng-model to
19044 * expressions matching the input.
19046 * @param {Element} element The element root to search from.
19047 * @param {string} expression The model expression to match.
19048 * @param {boolean} opt_exactMatch If true, only returns exact matches
19049 * for the expression.
19051 testability.findModels = function(element, expression, opt_exactMatch) {
19052 var prefixes = ['ng-', 'data-ng-', 'ng\\:'];
19053 for (var p = 0; p < prefixes.length; ++p) {
19054 var attributeEquals = opt_exactMatch ? '=' : '*=';
19055 var selector = '[' + prefixes[p] + 'model' + attributeEquals + '"' + expression + '"]';
19056 var elements = element.querySelectorAll(selector);
19057 if (elements.length) {
19064 * @name $$testability#getLocation
19067 * Shortcut for getting the location in a browser agnostic way. Returns
19068 * the path, search, and hash. (e.g. /path?a=b#hash)
19070 testability.getLocation = function() {
19071 return $location.url();
19075 * @name $$testability#setLocation
19078 * Shortcut for navigating to a location without doing a full page reload.
19080 * @param {string} url The location url (path, search and hash,
19081 * e.g. /path?a=b#hash) to go to.
19083 testability.setLocation = function(url) {
19084 if (url !== $location.url()) {
19085 $location.url(url);
19086 $rootScope.$digest();
19091 * @name $$testability#whenStable
19094 * Calls the callback when $timeout and $http requests are completed.
19096 * @param {function} callback
19098 testability.whenStable = function(callback) {
19099 $browser.notifyWhenNoOutstandingRequests(callback);
19102 return testability;
19106 function $TimeoutProvider() {
19107 this.$get = ['$rootScope', '$browser', '$q', '$$q', '$exceptionHandler',
19108 function($rootScope, $browser, $q, $$q, $exceptionHandler) {
19110 var deferreds = {};
19118 * Angular's wrapper for `window.setTimeout`. The `fn` function is wrapped into a try/catch
19119 * block and delegates any exceptions to
19120 * {@link ng.$exceptionHandler $exceptionHandler} service.
19122 * The return value of calling `$timeout` is a promise, which will be resolved when
19123 * the delay has passed and the timeout function, if provided, is executed.
19125 * To cancel a timeout request, call `$timeout.cancel(promise)`.
19127 * In tests you can use {@link ngMock.$timeout `$timeout.flush()`} to
19128 * synchronously flush the queue of deferred functions.
19130 * If you only want a promise that will be resolved after some specified delay
19131 * then you can call `$timeout` without the `fn` function.
19133 * @param {function()=} fn A function, whose execution should be delayed.
19134 * @param {number=} [delay=0] Delay in milliseconds.
19135 * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
19136 * will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
19137 * @param {...*=} Pass additional parameters to the executed function.
19138 * @returns {Promise} Promise that will be resolved when the timeout is reached. The promise
19139 * will be resolved with the return value of the `fn` function.
19142 function timeout(fn, delay, invokeApply) {
19143 if (!isFunction(fn)) {
19144 invokeApply = delay;
19149 var args = sliceArgs(arguments, 3),
19150 skipApply = (isDefined(invokeApply) && !invokeApply),
19151 deferred = (skipApply ? $$q : $q).defer(),
19152 promise = deferred.promise,
19155 timeoutId = $browser.defer(function() {
19157 deferred.resolve(fn.apply(null, args));
19159 deferred.reject(e);
19160 $exceptionHandler(e);
19163 delete deferreds[promise.$$timeoutId];
19166 if (!skipApply) $rootScope.$apply();
19169 promise.$$timeoutId = timeoutId;
19170 deferreds[timeoutId] = deferred;
19178 * @name $timeout#cancel
19181 * Cancels a task associated with the `promise`. As a result of this, the promise will be
19182 * resolved with a rejection.
19184 * @param {Promise=} promise Promise returned by the `$timeout` function.
19185 * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully
19188 timeout.cancel = function(promise) {
19189 if (promise && promise.$$timeoutId in deferreds) {
19190 deferreds[promise.$$timeoutId].reject('canceled');
19191 delete deferreds[promise.$$timeoutId];
19192 return $browser.defer.cancel(promise.$$timeoutId);
19201 // NOTE: The usage of window and document instead of $window and $document here is
19202 // deliberate. This service depends on the specific behavior of anchor nodes created by the
19203 // browser (resolving and parsing URLs) that is unlikely to be provided by mock objects and
19204 // cause us to break tests. In addition, when the browser resolves a URL for XHR, it
19205 // doesn't know about mocked locations and resolves URLs to the real document - which is
19206 // exactly the behavior needed here. There is little value is mocking these out for this
19208 var urlParsingNode = window.document.createElement("a");
19209 var originUrl = urlResolve(window.location.href);
19214 * Implementation Notes for non-IE browsers
19215 * ----------------------------------------
19216 * Assigning a URL to the href property of an anchor DOM node, even one attached to the DOM,
19217 * results both in the normalizing and parsing of the URL. Normalizing means that a relative
19218 * URL will be resolved into an absolute URL in the context of the application document.
19219 * Parsing means that the anchor node's host, hostname, protocol, port, pathname and related
19220 * properties are all populated to reflect the normalized URL. This approach has wide
19221 * compatibility - Safari 1+, Mozilla 1+, Opera 7+,e etc. See
19222 * http://www.aptana.com/reference/html/api/HTMLAnchorElement.html
19224 * Implementation Notes for IE
19225 * ---------------------------
19226 * IE <= 10 normalizes the URL when assigned to the anchor node similar to the other
19227 * browsers. However, the parsed components will not be set if the URL assigned did not specify
19228 * them. (e.g. if you assign a.href = "foo", then a.protocol, a.host, etc. will be empty.) We
19229 * work around that by performing the parsing in a 2nd step by taking a previously normalized
19230 * URL (e.g. by assigning to a.href) and assigning it a.href again. This correctly populates the
19231 * properties such as protocol, hostname, port, etc.
19234 * http://developer.mozilla.org/en-US/docs/Web/API/HTMLAnchorElement
19235 * http://www.aptana.com/reference/html/api/HTMLAnchorElement.html
19236 * http://url.spec.whatwg.org/#urlutils
19237 * https://github.com/angular/angular.js/pull/2902
19238 * http://james.padolsey.com/javascript/parsing-urls-with-the-dom/
19241 * @param {string} url The URL to be parsed.
19242 * @description Normalizes and parses a URL.
19243 * @returns {object} Returns the normalized URL as a dictionary.
19245 * | member name | Description |
19246 * |---------------|----------------|
19247 * | href | A normalized version of the provided URL if it was not an absolute URL |
19248 * | protocol | The protocol including the trailing colon |
19249 * | host | The host and port (if the port is non-default) of the normalizedUrl |
19250 * | search | The search params, minus the question mark |
19251 * | hash | The hash string, minus the hash symbol
19252 * | hostname | The hostname
19253 * | port | The port, without ":"
19254 * | pathname | The pathname, beginning with "/"
19257 function urlResolve(url) {
19261 // Normalize before parse. Refer Implementation Notes on why this is
19262 // done in two steps on IE.
19263 urlParsingNode.setAttribute("href", href);
19264 href = urlParsingNode.href;
19267 urlParsingNode.setAttribute('href', href);
19269 // urlParsingNode provides the UrlUtils interface - http://url.spec.whatwg.org/#urlutils
19271 href: urlParsingNode.href,
19272 protocol: urlParsingNode.protocol ? urlParsingNode.protocol.replace(/:$/, '') : '',
19273 host: urlParsingNode.host,
19274 search: urlParsingNode.search ? urlParsingNode.search.replace(/^\?/, '') : '',
19275 hash: urlParsingNode.hash ? urlParsingNode.hash.replace(/^#/, '') : '',
19276 hostname: urlParsingNode.hostname,
19277 port: urlParsingNode.port,
19278 pathname: (urlParsingNode.pathname.charAt(0) === '/')
19279 ? urlParsingNode.pathname
19280 : '/' + urlParsingNode.pathname
19285 * Parse a request URL and determine whether this is a same-origin request as the application document.
19287 * @param {string|object} requestUrl The url of the request as a string that will be resolved
19288 * or a parsed URL object.
19289 * @returns {boolean} Whether the request is for the same origin as the application document.
19291 function urlIsSameOrigin(requestUrl) {
19292 var parsed = (isString(requestUrl)) ? urlResolve(requestUrl) : requestUrl;
19293 return (parsed.protocol === originUrl.protocol &&
19294 parsed.host === originUrl.host);
19302 * A reference to the browser's `window` object. While `window`
19303 * is globally available in JavaScript, it causes testability problems, because
19304 * it is a global variable. In angular we always refer to it through the
19305 * `$window` service, so it may be overridden, removed or mocked for testing.
19307 * Expressions, like the one defined for the `ngClick` directive in the example
19308 * below, are evaluated with respect to the current scope. Therefore, there is
19309 * no risk of inadvertently coding in a dependency on a global value in such an
19313 <example module="windowExample">
19314 <file name="index.html">
19316 angular.module('windowExample', [])
19317 .controller('ExampleController', ['$scope', '$window', function($scope, $window) {
19318 $scope.greeting = 'Hello, World!';
19319 $scope.doGreeting = function(greeting) {
19320 $window.alert(greeting);
19324 <div ng-controller="ExampleController">
19325 <input type="text" ng-model="greeting" aria-label="greeting" />
19326 <button ng-click="doGreeting(greeting)">ALERT</button>
19329 <file name="protractor.js" type="protractor">
19330 it('should display the greeting in the input box', function() {
19331 element(by.model('greeting')).sendKeys('Hello, E2E Tests');
19332 // If we click the button it will block the test runner
19333 // element(':button').click();
19338 function $WindowProvider() {
19339 this.$get = valueFn(window);
19343 * @name $$cookieReader
19344 * @requires $document
19347 * This is a private service for reading cookies used by $http and ngCookies
19349 * @return {Object} a key/value map of the current cookies
19351 function $$CookieReader($document) {
19352 var rawDocument = $document[0] || {};
19353 var lastCookies = {};
19354 var lastCookieString = '';
19356 function safeDecodeURIComponent(str) {
19358 return decodeURIComponent(str);
19364 return function() {
19365 var cookieArray, cookie, i, index, name;
19366 var currentCookieString = rawDocument.cookie || '';
19368 if (currentCookieString !== lastCookieString) {
19369 lastCookieString = currentCookieString;
19370 cookieArray = lastCookieString.split('; ');
19373 for (i = 0; i < cookieArray.length; i++) {
19374 cookie = cookieArray[i];
19375 index = cookie.indexOf('=');
19376 if (index > 0) { //ignore nameless cookies
19377 name = safeDecodeURIComponent(cookie.substring(0, index));
19378 // the first value that is seen for a cookie is the most
19379 // specific one. values for the same cookie name that
19380 // follow are for less specific paths.
19381 if (isUndefined(lastCookies[name])) {
19382 lastCookies[name] = safeDecodeURIComponent(cookie.substring(index + 1));
19387 return lastCookies;
19391 $$CookieReader.$inject = ['$document'];
19393 function $$CookieReaderProvider() {
19394 this.$get = $$CookieReader;
19397 /* global currencyFilter: true,
19399 filterFilter: true,
19401 limitToFilter: true,
19402 lowercaseFilter: true,
19403 numberFilter: true,
19404 orderByFilter: true,
19405 uppercaseFilter: true,
19410 * @name $filterProvider
19413 * Filters are just functions which transform input to an output. However filters need to be
19414 * Dependency Injected. To achieve this a filter definition consists of a factory function which is
19415 * annotated with dependencies and is responsible for creating a filter function.
19417 * <div class="alert alert-warning">
19418 * **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`.
19419 * Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace
19420 * your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
19421 * (`myapp_subsection_filterx`).
19425 * // Filter registration
19426 * function MyModule($provide, $filterProvider) {
19427 * // create a service to demonstrate injection (not always needed)
19428 * $provide.value('greet', function(name){
19429 * return 'Hello ' + name + '!';
19432 * // register a filter factory which uses the
19433 * // greet service to demonstrate DI.
19434 * $filterProvider.register('greet', function(greet){
19435 * // return the filter function which uses the greet service
19436 * // to generate salutation
19437 * return function(text) {
19438 * // filters need to be forgiving so check input validity
19439 * return text && greet(text) || text;
19445 * The filter function is registered with the `$injector` under the filter name suffix with
19449 * it('should be the same instance', inject(
19450 * function($filterProvider) {
19451 * $filterProvider.register('reverse', function(){
19455 * function($filter, reverseFilter) {
19456 * expect($filter('reverse')).toBe(reverseFilter);
19461 * For more information about how angular filters work, and how to create your own filters, see
19462 * {@link guide/filter Filters} in the Angular Developer Guide.
19470 * Filters are used for formatting data displayed to the user.
19472 * The general syntax in templates is as follows:
19474 * {{ expression [| filter_name[:parameter_value] ... ] }}
19476 * @param {String} name Name of the filter function to retrieve
19477 * @return {Function} the filter function
19479 <example name="$filter" module="filterExample">
19480 <file name="index.html">
19481 <div ng-controller="MainCtrl">
19482 <h3>{{ originalText }}</h3>
19483 <h3>{{ filteredText }}</h3>
19487 <file name="script.js">
19488 angular.module('filterExample', [])
19489 .controller('MainCtrl', function($scope, $filter) {
19490 $scope.originalText = 'hello';
19491 $scope.filteredText = $filter('uppercase')($scope.originalText);
19496 $FilterProvider.$inject = ['$provide'];
19497 function $FilterProvider($provide) {
19498 var suffix = 'Filter';
19502 * @name $filterProvider#register
19503 * @param {string|Object} name Name of the filter function, or an object map of filters where
19504 * the keys are the filter names and the values are the filter factories.
19506 * <div class="alert alert-warning">
19507 * **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`.
19508 * Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace
19509 * your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
19510 * (`myapp_subsection_filterx`).
19512 * @param {Function} factory If the first argument was a string, a factory function for the filter to be registered.
19513 * @returns {Object} Registered filter instance, or if a map of filters was provided then a map
19514 * of the registered filter instances.
19516 function register(name, factory) {
19517 if (isObject(name)) {
19519 forEach(name, function(filter, key) {
19520 filters[key] = register(key, filter);
19524 return $provide.factory(name + suffix, factory);
19527 this.register = register;
19529 this.$get = ['$injector', function($injector) {
19530 return function(name) {
19531 return $injector.get(name + suffix);
19535 ////////////////////////////////////////
19538 currencyFilter: false,
19540 filterFilter: false,
19542 limitToFilter: false,
19543 lowercaseFilter: false,
19544 numberFilter: false,
19545 orderByFilter: false,
19546 uppercaseFilter: false,
19549 register('currency', currencyFilter);
19550 register('date', dateFilter);
19551 register('filter', filterFilter);
19552 register('json', jsonFilter);
19553 register('limitTo', limitToFilter);
19554 register('lowercase', lowercaseFilter);
19555 register('number', numberFilter);
19556 register('orderBy', orderByFilter);
19557 register('uppercase', uppercaseFilter);
19566 * Selects a subset of items from `array` and returns it as a new array.
19568 * @param {Array} array The source array.
19569 * @param {string|Object|function()} expression The predicate to be used for selecting items from
19574 * - `string`: The string is used for matching against the contents of the `array`. All strings or
19575 * objects with string properties in `array` that match this string will be returned. This also
19576 * applies to nested object properties.
19577 * The predicate can be negated by prefixing the string with `!`.
19579 * - `Object`: A pattern object can be used to filter specific properties on objects contained
19580 * by `array`. For example `{name:"M", phone:"1"}` predicate will return an array of items
19581 * which have property `name` containing "M" and property `phone` containing "1". A special
19582 * property name `$` can be used (as in `{$:"text"}`) to accept a match against any
19583 * property of the object or its nested object properties. That's equivalent to the simple
19584 * substring match with a `string` as described above. The predicate can be negated by prefixing
19585 * the string with `!`.
19586 * For example `{name: "!M"}` predicate will return an array of items which have property `name`
19587 * not containing "M".
19589 * Note that a named property will match properties on the same level only, while the special
19590 * `$` property will match properties on the same level or deeper. E.g. an array item like
19591 * `{name: {first: 'John', last: 'Doe'}}` will **not** be matched by `{name: 'John'}`, but
19592 * **will** be matched by `{$: 'John'}`.
19594 * - `function(value, index, array)`: A predicate function can be used to write arbitrary filters.
19595 * The function is called for each element of the array, with the element, its index, and
19596 * the entire array itself as arguments.
19598 * The final result is an array of those elements that the predicate returned true for.
19600 * @param {function(actual, expected)|true|undefined} comparator Comparator which is used in
19601 * determining if the expected value (from the filter expression) and actual value (from
19602 * the object in the array) should be considered a match.
19606 * - `function(actual, expected)`:
19607 * The function will be given the object value and the predicate value to compare and
19608 * should return true if both values should be considered equal.
19610 * - `true`: A shorthand for `function(actual, expected) { return angular.equals(actual, expected)}`.
19611 * This is essentially strict comparison of expected and actual.
19613 * - `false|undefined`: A short hand for a function which will look for a substring match in case
19616 * Primitive values are converted to strings. Objects are not compared against primitives,
19617 * unless they have a custom `toString` method (e.g. `Date` objects).
19621 <file name="index.html">
19622 <div ng-init="friends = [{name:'John', phone:'555-1276'},
19623 {name:'Mary', phone:'800-BIG-MARY'},
19624 {name:'Mike', phone:'555-4321'},
19625 {name:'Adam', phone:'555-5678'},
19626 {name:'Julie', phone:'555-8765'},
19627 {name:'Juliette', phone:'555-5678'}]"></div>
19629 <label>Search: <input ng-model="searchText"></label>
19630 <table id="searchTextResults">
19631 <tr><th>Name</th><th>Phone</th></tr>
19632 <tr ng-repeat="friend in friends | filter:searchText">
19633 <td>{{friend.name}}</td>
19634 <td>{{friend.phone}}</td>
19638 <label>Any: <input ng-model="search.$"></label> <br>
19639 <label>Name only <input ng-model="search.name"></label><br>
19640 <label>Phone only <input ng-model="search.phone"></label><br>
19641 <label>Equality <input type="checkbox" ng-model="strict"></label><br>
19642 <table id="searchObjResults">
19643 <tr><th>Name</th><th>Phone</th></tr>
19644 <tr ng-repeat="friendObj in friends | filter:search:strict">
19645 <td>{{friendObj.name}}</td>
19646 <td>{{friendObj.phone}}</td>
19650 <file name="protractor.js" type="protractor">
19651 var expectFriendNames = function(expectedNames, key) {
19652 element.all(by.repeater(key + ' in friends').column(key + '.name')).then(function(arr) {
19653 arr.forEach(function(wd, i) {
19654 expect(wd.getText()).toMatch(expectedNames[i]);
19659 it('should search across all fields when filtering with a string', function() {
19660 var searchText = element(by.model('searchText'));
19661 searchText.clear();
19662 searchText.sendKeys('m');
19663 expectFriendNames(['Mary', 'Mike', 'Adam'], 'friend');
19665 searchText.clear();
19666 searchText.sendKeys('76');
19667 expectFriendNames(['John', 'Julie'], 'friend');
19670 it('should search in specific fields when filtering with a predicate object', function() {
19671 var searchAny = element(by.model('search.$'));
19673 searchAny.sendKeys('i');
19674 expectFriendNames(['Mary', 'Mike', 'Julie', 'Juliette'], 'friendObj');
19676 it('should use a equal comparison when comparator is true', function() {
19677 var searchName = element(by.model('search.name'));
19678 var strict = element(by.model('strict'));
19679 searchName.clear();
19680 searchName.sendKeys('Julie');
19682 expectFriendNames(['Julie'], 'friendObj');
19687 function filterFilter() {
19688 return function(array, expression, comparator) {
19689 if (!isArrayLike(array)) {
19690 if (array == null) {
19693 throw minErr('filter')('notarray', 'Expected array but received: {0}', array);
19697 var expressionType = getTypeForFilter(expression);
19699 var matchAgainstAnyProp;
19701 switch (expressionType) {
19703 predicateFn = expression;
19709 matchAgainstAnyProp = true;
19713 predicateFn = createPredicateFn(expression, comparator, matchAgainstAnyProp);
19719 return Array.prototype.filter.call(array, predicateFn);
19723 // Helper functions for `filterFilter`
19724 function createPredicateFn(expression, comparator, matchAgainstAnyProp) {
19725 var shouldMatchPrimitives = isObject(expression) && ('$' in expression);
19728 if (comparator === true) {
19729 comparator = equals;
19730 } else if (!isFunction(comparator)) {
19731 comparator = function(actual, expected) {
19732 if (isUndefined(actual)) {
19733 // No substring matching against `undefined`
19736 if ((actual === null) || (expected === null)) {
19737 // No substring matching against `null`; only match against `null`
19738 return actual === expected;
19740 if (isObject(expected) || (isObject(actual) && !hasCustomToString(actual))) {
19741 // Should not compare primitives against objects, unless they have custom `toString` method
19745 actual = lowercase('' + actual);
19746 expected = lowercase('' + expected);
19747 return actual.indexOf(expected) !== -1;
19751 predicateFn = function(item) {
19752 if (shouldMatchPrimitives && !isObject(item)) {
19753 return deepCompare(item, expression.$, comparator, false);
19755 return deepCompare(item, expression, comparator, matchAgainstAnyProp);
19758 return predicateFn;
19761 function deepCompare(actual, expected, comparator, matchAgainstAnyProp, dontMatchWholeObject) {
19762 var actualType = getTypeForFilter(actual);
19763 var expectedType = getTypeForFilter(expected);
19765 if ((expectedType === 'string') && (expected.charAt(0) === '!')) {
19766 return !deepCompare(actual, expected.substring(1), comparator, matchAgainstAnyProp);
19767 } else if (isArray(actual)) {
19768 // In case `actual` is an array, consider it a match
19769 // if ANY of it's items matches `expected`
19770 return actual.some(function(item) {
19771 return deepCompare(item, expected, comparator, matchAgainstAnyProp);
19775 switch (actualType) {
19778 if (matchAgainstAnyProp) {
19779 for (key in actual) {
19780 if ((key.charAt(0) !== '$') && deepCompare(actual[key], expected, comparator, true)) {
19784 return dontMatchWholeObject ? false : deepCompare(actual, expected, comparator, false);
19785 } else if (expectedType === 'object') {
19786 for (key in expected) {
19787 var expectedVal = expected[key];
19788 if (isFunction(expectedVal) || isUndefined(expectedVal)) {
19792 var matchAnyProperty = key === '$';
19793 var actualVal = matchAnyProperty ? actual : actual[key];
19794 if (!deepCompare(actualVal, expectedVal, comparator, matchAnyProperty, matchAnyProperty)) {
19800 return comparator(actual, expected);
19806 return comparator(actual, expected);
19810 // Used for easily differentiating between `null` and actual `object`
19811 function getTypeForFilter(val) {
19812 return (val === null) ? 'null' : typeof val;
19815 var MAX_DIGITS = 22;
19816 var DECIMAL_SEP = '.';
19817 var ZERO_CHAR = '0';
19825 * Formats a number as a currency (ie $1,234.56). When no currency symbol is provided, default
19826 * symbol for current locale is used.
19828 * @param {number} amount Input to filter.
19829 * @param {string=} symbol Currency symbol or identifier to be displayed.
19830 * @param {number=} fractionSize Number of decimal places to round the amount to, defaults to default max fraction size for current locale
19831 * @returns {string} Formatted number.
19835 <example module="currencyExample">
19836 <file name="index.html">
19838 angular.module('currencyExample', [])
19839 .controller('ExampleController', ['$scope', function($scope) {
19840 $scope.amount = 1234.56;
19843 <div ng-controller="ExampleController">
19844 <input type="number" ng-model="amount" aria-label="amount"> <br>
19845 default currency symbol ($): <span id="currency-default">{{amount | currency}}</span><br>
19846 custom currency identifier (USD$): <span id="currency-custom">{{amount | currency:"USD$"}}</span>
19847 no fractions (0): <span id="currency-no-fractions">{{amount | currency:"USD$":0}}</span>
19850 <file name="protractor.js" type="protractor">
19851 it('should init with 1234.56', function() {
19852 expect(element(by.id('currency-default')).getText()).toBe('$1,234.56');
19853 expect(element(by.id('currency-custom')).getText()).toBe('USD$1,234.56');
19854 expect(element(by.id('currency-no-fractions')).getText()).toBe('USD$1,235');
19856 it('should update', function() {
19857 if (browser.params.browser == 'safari') {
19858 // Safari does not understand the minus key. See
19859 // https://github.com/angular/protractor/issues/481
19862 element(by.model('amount')).clear();
19863 element(by.model('amount')).sendKeys('-1234');
19864 expect(element(by.id('currency-default')).getText()).toBe('-$1,234.00');
19865 expect(element(by.id('currency-custom')).getText()).toBe('-USD$1,234.00');
19866 expect(element(by.id('currency-no-fractions')).getText()).toBe('-USD$1,234');
19871 currencyFilter.$inject = ['$locale'];
19872 function currencyFilter($locale) {
19873 var formats = $locale.NUMBER_FORMATS;
19874 return function(amount, currencySymbol, fractionSize) {
19875 if (isUndefined(currencySymbol)) {
19876 currencySymbol = formats.CURRENCY_SYM;
19879 if (isUndefined(fractionSize)) {
19880 fractionSize = formats.PATTERNS[1].maxFrac;
19883 // if null or undefined pass it through
19884 return (amount == null)
19886 : formatNumber(amount, formats.PATTERNS[1], formats.GROUP_SEP, formats.DECIMAL_SEP, fractionSize).
19887 replace(/\u00A4/g, currencySymbol);
19897 * Formats a number as text.
19899 * If the input is null or undefined, it will just be returned.
19900 * If the input is infinite (Infinity or -Infinity), the Infinity symbol '∞' or '-∞' is returned, respectively.
19901 * If the input is not a number an empty string is returned.
19904 * @param {number|string} number Number to format.
19905 * @param {(number|string)=} fractionSize Number of decimal places to round the number to.
19906 * If this is not provided then the fraction size is computed from the current locale's number
19907 * formatting pattern. In the case of the default locale, it will be 3.
19908 * @returns {string} Number rounded to `fractionSize` appropriately formatted based on the current
19909 * locale (e.g., in the en_US locale it will have "." as the decimal separator and
19910 * include "," group separators after each third digit).
19913 <example module="numberFilterExample">
19914 <file name="index.html">
19916 angular.module('numberFilterExample', [])
19917 .controller('ExampleController', ['$scope', function($scope) {
19918 $scope.val = 1234.56789;
19921 <div ng-controller="ExampleController">
19922 <label>Enter number: <input ng-model='val'></label><br>
19923 Default formatting: <span id='number-default'>{{val | number}}</span><br>
19924 No fractions: <span>{{val | number:0}}</span><br>
19925 Negative number: <span>{{-val | number:4}}</span>
19928 <file name="protractor.js" type="protractor">
19929 it('should format numbers', function() {
19930 expect(element(by.id('number-default')).getText()).toBe('1,234.568');
19931 expect(element(by.binding('val | number:0')).getText()).toBe('1,235');
19932 expect(element(by.binding('-val | number:4')).getText()).toBe('-1,234.5679');
19935 it('should update', function() {
19936 element(by.model('val')).clear();
19937 element(by.model('val')).sendKeys('3374.333');
19938 expect(element(by.id('number-default')).getText()).toBe('3,374.333');
19939 expect(element(by.binding('val | number:0')).getText()).toBe('3,374');
19940 expect(element(by.binding('-val | number:4')).getText()).toBe('-3,374.3330');
19945 numberFilter.$inject = ['$locale'];
19946 function numberFilter($locale) {
19947 var formats = $locale.NUMBER_FORMATS;
19948 return function(number, fractionSize) {
19950 // if null or undefined pass it through
19951 return (number == null)
19953 : formatNumber(number, formats.PATTERNS[0], formats.GROUP_SEP, formats.DECIMAL_SEP,
19959 * Parse a number (as a string) into three components that can be used
19960 * for formatting the number.
19962 * (Significant bits of this parse algorithm came from https://github.com/MikeMcl/big.js/)
19964 * @param {string} numStr The number to parse
19965 * @return {object} An object describing this number, containing the following keys:
19966 * - d : an array of digits containing leading zeros as necessary
19967 * - i : the number of the digits in `d` that are to the left of the decimal point
19968 * - e : the exponent for numbers that would need more than `MAX_DIGITS` digits in `d`
19971 function parse(numStr) {
19972 var exponent = 0, digits, numberOfIntegerDigits;
19976 if ((numberOfIntegerDigits = numStr.indexOf(DECIMAL_SEP)) > -1) {
19977 numStr = numStr.replace(DECIMAL_SEP, '');
19980 // Exponential form?
19981 if ((i = numStr.search(/e/i)) > 0) {
19982 // Work out the exponent.
19983 if (numberOfIntegerDigits < 0) numberOfIntegerDigits = i;
19984 numberOfIntegerDigits += +numStr.slice(i + 1);
19985 numStr = numStr.substring(0, i);
19986 } else if (numberOfIntegerDigits < 0) {
19987 // There was no decimal point or exponent so it is an integer.
19988 numberOfIntegerDigits = numStr.length;
19991 // Count the number of leading zeros.
19992 for (i = 0; numStr.charAt(i) == ZERO_CHAR; i++) {/* jshint noempty: false */}
19994 if (i == (zeros = numStr.length)) {
19995 // The digits are all zero.
19997 numberOfIntegerDigits = 1;
19999 // Count the number of trailing zeros
20001 while (numStr.charAt(zeros) == ZERO_CHAR) zeros--;
20003 // Trailing zeros are insignificant so ignore them
20004 numberOfIntegerDigits -= i;
20006 // Convert string to array of digits without leading/trailing zeros.
20007 for (j = 0; i <= zeros; i++, j++) {
20008 digits[j] = +numStr.charAt(i);
20012 // If the number overflows the maximum allowed digits then use an exponent.
20013 if (numberOfIntegerDigits > MAX_DIGITS) {
20014 digits = digits.splice(0, MAX_DIGITS - 1);
20015 exponent = numberOfIntegerDigits - 1;
20016 numberOfIntegerDigits = 1;
20019 return { d: digits, e: exponent, i: numberOfIntegerDigits };
20023 * Round the parsed number to the specified number of decimal places
20024 * This function changed the parsedNumber in-place
20026 function roundNumber(parsedNumber, fractionSize, minFrac, maxFrac) {
20027 var digits = parsedNumber.d;
20028 var fractionLen = digits.length - parsedNumber.i;
20030 // determine fractionSize if it is not specified; `+fractionSize` converts it to a number
20031 fractionSize = (isUndefined(fractionSize)) ? Math.min(Math.max(minFrac, fractionLen), maxFrac) : +fractionSize;
20033 // The index of the digit to where rounding is to occur
20034 var roundAt = fractionSize + parsedNumber.i;
20035 var digit = digits[roundAt];
20038 // Drop fractional digits beyond `roundAt`
20039 digits.splice(Math.max(parsedNumber.i, roundAt));
20041 // Set non-fractional digits beyond `roundAt` to 0
20042 for (var j = roundAt; j < digits.length; j++) {
20046 // We rounded to zero so reset the parsedNumber
20047 fractionLen = Math.max(0, fractionLen);
20048 parsedNumber.i = 1;
20049 digits.length = Math.max(1, roundAt = fractionSize + 1);
20051 for (var i = 1; i < roundAt; i++) digits[i] = 0;
20055 if (roundAt - 1 < 0) {
20056 for (var k = 0; k > roundAt; k--) {
20063 digits[roundAt - 1]++;
20067 // Pad out with zeros to get the required fraction length
20068 for (; fractionLen < Math.max(0, fractionSize); fractionLen++) digits.push(0);
20071 // Do any carrying, e.g. a digit was rounded up to 10
20072 var carry = digits.reduceRight(function(carry, d, i, digits) {
20074 digits[i] = d % 10;
20075 return Math.floor(d / 10);
20078 digits.unshift(carry);
20084 * Format a number into a string
20085 * @param {number} number The number to format
20087 * minFrac, // the minimum number of digits required in the fraction part of the number
20088 * maxFrac, // the maximum number of digits required in the fraction part of the number
20089 * gSize, // number of digits in each group of separated digits
20090 * lgSize, // number of digits in the last group of digits before the decimal separator
20091 * negPre, // the string to go in front of a negative number (e.g. `-` or `(`))
20092 * posPre, // the string to go in front of a positive number
20093 * negSuf, // the string to go after a negative number (e.g. `)`)
20094 * posSuf // the string to go after a positive number
20096 * @param {string} groupSep The string to separate groups of number (e.g. `,`)
20097 * @param {string} decimalSep The string to act as the decimal separator (e.g. `.`)
20098 * @param {[type]} fractionSize The size of the fractional part of the number
20099 * @return {string} The number formatted as a string
20101 function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) {
20103 if (!(isString(number) || isNumber(number)) || isNaN(number)) return '';
20105 var isInfinity = !isFinite(number);
20106 var isZero = false;
20107 var numStr = Math.abs(number) + '',
20108 formattedText = '',
20112 formattedText = '\u221e';
20114 parsedNumber = parse(numStr);
20116 roundNumber(parsedNumber, fractionSize, pattern.minFrac, pattern.maxFrac);
20118 var digits = parsedNumber.d;
20119 var integerLen = parsedNumber.i;
20120 var exponent = parsedNumber.e;
20122 isZero = digits.reduce(function(isZero, d) { return isZero && !d; }, true);
20124 // pad zeros for small numbers
20125 while (integerLen < 0) {
20130 // extract decimals digits
20131 if (integerLen > 0) {
20132 decimals = digits.splice(integerLen);
20138 // format the integer digits with grouping separators
20140 if (digits.length >= pattern.lgSize) {
20141 groups.unshift(digits.splice(-pattern.lgSize).join(''));
20143 while (digits.length > pattern.gSize) {
20144 groups.unshift(digits.splice(-pattern.gSize).join(''));
20146 if (digits.length) {
20147 groups.unshift(digits.join(''));
20149 formattedText = groups.join(groupSep);
20151 // append the decimal digits
20152 if (decimals.length) {
20153 formattedText += decimalSep + decimals.join('');
20157 formattedText += 'e+' + exponent;
20160 if (number < 0 && !isZero) {
20161 return pattern.negPre + formattedText + pattern.negSuf;
20163 return pattern.posPre + formattedText + pattern.posSuf;
20167 function padNumber(num, digits, trim, negWrap) {
20169 if (num < 0 || (negWrap && num <= 0)) {
20178 while (num.length < digits) num = ZERO_CHAR + num;
20180 num = num.substr(num.length - digits);
20186 function dateGetter(name, size, offset, trim, negWrap) {
20187 offset = offset || 0;
20188 return function(date) {
20189 var value = date['get' + name]();
20190 if (offset > 0 || value > -offset) {
20193 if (value === 0 && offset == -12) value = 12;
20194 return padNumber(value, size, trim, negWrap);
20198 function dateStrGetter(name, shortForm, standAlone) {
20199 return function(date, formats) {
20200 var value = date['get' + name]();
20201 var propPrefix = (standAlone ? 'STANDALONE' : '') + (shortForm ? 'SHORT' : '');
20202 var get = uppercase(propPrefix + name);
20204 return formats[get][value];
20208 function timeZoneGetter(date, formats, offset) {
20209 var zone = -1 * offset;
20210 var paddedZone = (zone >= 0) ? "+" : "";
20212 paddedZone += padNumber(Math[zone > 0 ? 'floor' : 'ceil'](zone / 60), 2) +
20213 padNumber(Math.abs(zone % 60), 2);
20218 function getFirstThursdayOfYear(year) {
20219 // 0 = index of January
20220 var dayOfWeekOnFirst = (new Date(year, 0, 1)).getDay();
20221 // 4 = index of Thursday (+1 to account for 1st = 5)
20222 // 11 = index of *next* Thursday (+1 account for 1st = 12)
20223 return new Date(year, 0, ((dayOfWeekOnFirst <= 4) ? 5 : 12) - dayOfWeekOnFirst);
20226 function getThursdayThisWeek(datetime) {
20227 return new Date(datetime.getFullYear(), datetime.getMonth(),
20228 // 4 = index of Thursday
20229 datetime.getDate() + (4 - datetime.getDay()));
20232 function weekGetter(size) {
20233 return function(date) {
20234 var firstThurs = getFirstThursdayOfYear(date.getFullYear()),
20235 thisThurs = getThursdayThisWeek(date);
20237 var diff = +thisThurs - +firstThurs,
20238 result = 1 + Math.round(diff / 6.048e8); // 6.048e8 ms per week
20240 return padNumber(result, size);
20244 function ampmGetter(date, formats) {
20245 return date.getHours() < 12 ? formats.AMPMS[0] : formats.AMPMS[1];
20248 function eraGetter(date, formats) {
20249 return date.getFullYear() <= 0 ? formats.ERAS[0] : formats.ERAS[1];
20252 function longEraGetter(date, formats) {
20253 return date.getFullYear() <= 0 ? formats.ERANAMES[0] : formats.ERANAMES[1];
20256 var DATE_FORMATS = {
20257 yyyy: dateGetter('FullYear', 4, 0, false, true),
20258 yy: dateGetter('FullYear', 2, 0, true, true),
20259 y: dateGetter('FullYear', 1, 0, false, true),
20260 MMMM: dateStrGetter('Month'),
20261 MMM: dateStrGetter('Month', true),
20262 MM: dateGetter('Month', 2, 1),
20263 M: dateGetter('Month', 1, 1),
20264 LLLL: dateStrGetter('Month', false, true),
20265 dd: dateGetter('Date', 2),
20266 d: dateGetter('Date', 1),
20267 HH: dateGetter('Hours', 2),
20268 H: dateGetter('Hours', 1),
20269 hh: dateGetter('Hours', 2, -12),
20270 h: dateGetter('Hours', 1, -12),
20271 mm: dateGetter('Minutes', 2),
20272 m: dateGetter('Minutes', 1),
20273 ss: dateGetter('Seconds', 2),
20274 s: dateGetter('Seconds', 1),
20275 // while ISO 8601 requires fractions to be prefixed with `.` or `,`
20276 // we can be just safely rely on using `sss` since we currently don't support single or two digit fractions
20277 sss: dateGetter('Milliseconds', 3),
20278 EEEE: dateStrGetter('Day'),
20279 EEE: dateStrGetter('Day', true),
20287 GGGG: longEraGetter
20290 var DATE_FORMATS_SPLIT = /((?:[^yMLdHhmsaZEwG']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|L+|d+|H+|h+|m+|s+|a|Z|G+|w+))(.*)/,
20291 NUMBER_STRING = /^\-?\d+$/;
20299 * Formats `date` to a string based on the requested `format`.
20301 * `format` string can be composed of the following elements:
20303 * * `'yyyy'`: 4 digit representation of year (e.g. AD 1 => 0001, AD 2010 => 2010)
20304 * * `'yy'`: 2 digit representation of year, padded (00-99). (e.g. AD 2001 => 01, AD 2010 => 10)
20305 * * `'y'`: 1 digit representation of year, e.g. (AD 1 => 1, AD 199 => 199)
20306 * * `'MMMM'`: Month in year (January-December)
20307 * * `'MMM'`: Month in year (Jan-Dec)
20308 * * `'MM'`: Month in year, padded (01-12)
20309 * * `'M'`: Month in year (1-12)
20310 * * `'LLLL'`: Stand-alone month in year (January-December)
20311 * * `'dd'`: Day in month, padded (01-31)
20312 * * `'d'`: Day in month (1-31)
20313 * * `'EEEE'`: Day in Week,(Sunday-Saturday)
20314 * * `'EEE'`: Day in Week, (Sun-Sat)
20315 * * `'HH'`: Hour in day, padded (00-23)
20316 * * `'H'`: Hour in day (0-23)
20317 * * `'hh'`: Hour in AM/PM, padded (01-12)
20318 * * `'h'`: Hour in AM/PM, (1-12)
20319 * * `'mm'`: Minute in hour, padded (00-59)
20320 * * `'m'`: Minute in hour (0-59)
20321 * * `'ss'`: Second in minute, padded (00-59)
20322 * * `'s'`: Second in minute (0-59)
20323 * * `'sss'`: Millisecond in second, padded (000-999)
20324 * * `'a'`: AM/PM marker
20325 * * `'Z'`: 4 digit (+sign) representation of the timezone offset (-1200-+1200)
20326 * * `'ww'`: Week of year, padded (00-53). Week 01 is the week with the first Thursday of the year
20327 * * `'w'`: Week of year (0-53). Week 1 is the week with the first Thursday of the year
20328 * * `'G'`, `'GG'`, `'GGG'`: The abbreviated form of the era string (e.g. 'AD')
20329 * * `'GGGG'`: The long form of the era string (e.g. 'Anno Domini')
20331 * `format` string can also be one of the following predefined
20332 * {@link guide/i18n localizable formats}:
20334 * * `'medium'`: equivalent to `'MMM d, y h:mm:ss a'` for en_US locale
20335 * (e.g. Sep 3, 2010 12:05:08 PM)
20336 * * `'short'`: equivalent to `'M/d/yy h:mm a'` for en_US locale (e.g. 9/3/10 12:05 PM)
20337 * * `'fullDate'`: equivalent to `'EEEE, MMMM d, y'` for en_US locale
20338 * (e.g. Friday, September 3, 2010)
20339 * * `'longDate'`: equivalent to `'MMMM d, y'` for en_US locale (e.g. September 3, 2010)
20340 * * `'mediumDate'`: equivalent to `'MMM d, y'` for en_US locale (e.g. Sep 3, 2010)
20341 * * `'shortDate'`: equivalent to `'M/d/yy'` for en_US locale (e.g. 9/3/10)
20342 * * `'mediumTime'`: equivalent to `'h:mm:ss a'` for en_US locale (e.g. 12:05:08 PM)
20343 * * `'shortTime'`: equivalent to `'h:mm a'` for en_US locale (e.g. 12:05 PM)
20345 * `format` string can contain literal values. These need to be escaped by surrounding with single quotes (e.g.
20346 * `"h 'in the morning'"`). In order to output a single quote, escape it - i.e., two single quotes in a sequence
20347 * (e.g. `"h 'o''clock'"`).
20349 * @param {(Date|number|string)} date Date to format either as Date object, milliseconds (string or
20350 * number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.sssZ and its
20351 * shorter versions like yyyy-MM-ddTHH:mmZ, yyyy-MM-dd or yyyyMMddTHHmmssZ). If no timezone is
20352 * specified in the string input, the time is considered to be in the local timezone.
20353 * @param {string=} format Formatting rules (see Description). If not specified,
20354 * `mediumDate` is used.
20355 * @param {string=} timezone Timezone to be used for formatting. It understands UTC/GMT and the
20356 * continental US time zone abbreviations, but for general use, use a time zone offset, for
20357 * example, `'+0430'` (4 hours, 30 minutes east of the Greenwich meridian)
20358 * If not specified, the timezone of the browser will be used.
20359 * @returns {string} Formatted string or the input if input is not recognized as date/millis.
20363 <file name="index.html">
20364 <span ng-non-bindable>{{1288323623006 | date:'medium'}}</span>:
20365 <span>{{1288323623006 | date:'medium'}}</span><br>
20366 <span ng-non-bindable>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span>:
20367 <span>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span><br>
20368 <span ng-non-bindable>{{1288323623006 | date:'MM/dd/yyyy @ h:mma'}}</span>:
20369 <span>{{'1288323623006' | date:'MM/dd/yyyy @ h:mma'}}</span><br>
20370 <span ng-non-bindable>{{1288323623006 | date:"MM/dd/yyyy 'at' h:mma"}}</span>:
20371 <span>{{'1288323623006' | date:"MM/dd/yyyy 'at' h:mma"}}</span><br>
20373 <file name="protractor.js" type="protractor">
20374 it('should format date', function() {
20375 expect(element(by.binding("1288323623006 | date:'medium'")).getText()).
20376 toMatch(/Oct 2\d, 2010 \d{1,2}:\d{2}:\d{2} (AM|PM)/);
20377 expect(element(by.binding("1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'")).getText()).
20378 toMatch(/2010\-10\-2\d \d{2}:\d{2}:\d{2} (\-|\+)?\d{4}/);
20379 expect(element(by.binding("'1288323623006' | date:'MM/dd/yyyy @ h:mma'")).getText()).
20380 toMatch(/10\/2\d\/2010 @ \d{1,2}:\d{2}(AM|PM)/);
20381 expect(element(by.binding("'1288323623006' | date:\"MM/dd/yyyy 'at' h:mma\"")).getText()).
20382 toMatch(/10\/2\d\/2010 at \d{1,2}:\d{2}(AM|PM)/);
20387 dateFilter.$inject = ['$locale'];
20388 function dateFilter($locale) {
20391 var R_ISO8601_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/;
20392 // 1 2 3 4 5 6 7 8 9 10 11
20393 function jsonStringToDate(string) {
20395 if (match = string.match(R_ISO8601_STR)) {
20396 var date = new Date(0),
20399 dateSetter = match[8] ? date.setUTCFullYear : date.setFullYear,
20400 timeSetter = match[8] ? date.setUTCHours : date.setHours;
20403 tzHour = toInt(match[9] + match[10]);
20404 tzMin = toInt(match[9] + match[11]);
20406 dateSetter.call(date, toInt(match[1]), toInt(match[2]) - 1, toInt(match[3]));
20407 var h = toInt(match[4] || 0) - tzHour;
20408 var m = toInt(match[5] || 0) - tzMin;
20409 var s = toInt(match[6] || 0);
20410 var ms = Math.round(parseFloat('0.' + (match[7] || 0)) * 1000);
20411 timeSetter.call(date, h, m, s, ms);
20418 return function(date, format, timezone) {
20423 format = format || 'mediumDate';
20424 format = $locale.DATETIME_FORMATS[format] || format;
20425 if (isString(date)) {
20426 date = NUMBER_STRING.test(date) ? toInt(date) : jsonStringToDate(date);
20429 if (isNumber(date)) {
20430 date = new Date(date);
20433 if (!isDate(date) || !isFinite(date.getTime())) {
20438 match = DATE_FORMATS_SPLIT.exec(format);
20440 parts = concat(parts, match, 1);
20441 format = parts.pop();
20443 parts.push(format);
20448 var dateTimezoneOffset = date.getTimezoneOffset();
20450 dateTimezoneOffset = timezoneToOffset(timezone, dateTimezoneOffset);
20451 date = convertTimezoneToLocal(date, timezone, true);
20453 forEach(parts, function(value) {
20454 fn = DATE_FORMATS[value];
20455 text += fn ? fn(date, $locale.DATETIME_FORMATS, dateTimezoneOffset)
20456 : value === "''" ? "'" : value.replace(/(^'|'$)/g, '').replace(/''/g, "'");
20470 * Allows you to convert a JavaScript object into JSON string.
20472 * This filter is mostly useful for debugging. When using the double curly {{value}} notation
20473 * the binding is automatically converted to JSON.
20475 * @param {*} object Any JavaScript object (including arrays and primitive types) to filter.
20476 * @param {number=} spacing The number of spaces to use per indentation, defaults to 2.
20477 * @returns {string} JSON string.
20482 <file name="index.html">
20483 <pre id="default-spacing">{{ {'name':'value'} | json }}</pre>
20484 <pre id="custom-spacing">{{ {'name':'value'} | json:4 }}</pre>
20486 <file name="protractor.js" type="protractor">
20487 it('should jsonify filtered objects', function() {
20488 expect(element(by.id('default-spacing')).getText()).toMatch(/\{\n "name": ?"value"\n}/);
20489 expect(element(by.id('custom-spacing')).getText()).toMatch(/\{\n "name": ?"value"\n}/);
20495 function jsonFilter() {
20496 return function(object, spacing) {
20497 if (isUndefined(spacing)) {
20500 return toJson(object, spacing);
20510 * Converts string to lowercase.
20511 * @see angular.lowercase
20513 var lowercaseFilter = valueFn(lowercase);
20521 * Converts string to uppercase.
20522 * @see angular.uppercase
20524 var uppercaseFilter = valueFn(uppercase);
20532 * Creates a new array or string containing only a specified number of elements. The elements
20533 * are taken from either the beginning or the end of the source array, string or number, as specified by
20534 * the value and sign (positive or negative) of `limit`. If a number is used as input, it is
20535 * converted to a string.
20537 * @param {Array|string|number} input Source array, string or number to be limited.
20538 * @param {string|number} limit The length of the returned array or string. If the `limit` number
20539 * is positive, `limit` number of items from the beginning of the source array/string are copied.
20540 * If the number is negative, `limit` number of items from the end of the source array/string
20541 * are copied. The `limit` will be trimmed if it exceeds `array.length`. If `limit` is undefined,
20542 * the input will be returned unchanged.
20543 * @param {(string|number)=} begin Index at which to begin limitation. As a negative index, `begin`
20544 * indicates an offset from the end of `input`. Defaults to `0`.
20545 * @returns {Array|string} A new sub-array or substring of length `limit` or less if input array
20546 * had less than `limit` elements.
20549 <example module="limitToExample">
20550 <file name="index.html">
20552 angular.module('limitToExample', [])
20553 .controller('ExampleController', ['$scope', function($scope) {
20554 $scope.numbers = [1,2,3,4,5,6,7,8,9];
20555 $scope.letters = "abcdefghi";
20556 $scope.longNumber = 2345432342;
20557 $scope.numLimit = 3;
20558 $scope.letterLimit = 3;
20559 $scope.longNumberLimit = 3;
20562 <div ng-controller="ExampleController">
20564 Limit {{numbers}} to:
20565 <input type="number" step="1" ng-model="numLimit">
20567 <p>Output numbers: {{ numbers | limitTo:numLimit }}</p>
20569 Limit {{letters}} to:
20570 <input type="number" step="1" ng-model="letterLimit">
20572 <p>Output letters: {{ letters | limitTo:letterLimit }}</p>
20574 Limit {{longNumber}} to:
20575 <input type="number" step="1" ng-model="longNumberLimit">
20577 <p>Output long number: {{ longNumber | limitTo:longNumberLimit }}</p>
20580 <file name="protractor.js" type="protractor">
20581 var numLimitInput = element(by.model('numLimit'));
20582 var letterLimitInput = element(by.model('letterLimit'));
20583 var longNumberLimitInput = element(by.model('longNumberLimit'));
20584 var limitedNumbers = element(by.binding('numbers | limitTo:numLimit'));
20585 var limitedLetters = element(by.binding('letters | limitTo:letterLimit'));
20586 var limitedLongNumber = element(by.binding('longNumber | limitTo:longNumberLimit'));
20588 it('should limit the number array to first three items', function() {
20589 expect(numLimitInput.getAttribute('value')).toBe('3');
20590 expect(letterLimitInput.getAttribute('value')).toBe('3');
20591 expect(longNumberLimitInput.getAttribute('value')).toBe('3');
20592 expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3]');
20593 expect(limitedLetters.getText()).toEqual('Output letters: abc');
20594 expect(limitedLongNumber.getText()).toEqual('Output long number: 234');
20597 // There is a bug in safari and protractor that doesn't like the minus key
20598 // it('should update the output when -3 is entered', function() {
20599 // numLimitInput.clear();
20600 // numLimitInput.sendKeys('-3');
20601 // letterLimitInput.clear();
20602 // letterLimitInput.sendKeys('-3');
20603 // longNumberLimitInput.clear();
20604 // longNumberLimitInput.sendKeys('-3');
20605 // expect(limitedNumbers.getText()).toEqual('Output numbers: [7,8,9]');
20606 // expect(limitedLetters.getText()).toEqual('Output letters: ghi');
20607 // expect(limitedLongNumber.getText()).toEqual('Output long number: 342');
20610 it('should not exceed the maximum size of input array', function() {
20611 numLimitInput.clear();
20612 numLimitInput.sendKeys('100');
20613 letterLimitInput.clear();
20614 letterLimitInput.sendKeys('100');
20615 longNumberLimitInput.clear();
20616 longNumberLimitInput.sendKeys('100');
20617 expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3,4,5,6,7,8,9]');
20618 expect(limitedLetters.getText()).toEqual('Output letters: abcdefghi');
20619 expect(limitedLongNumber.getText()).toEqual('Output long number: 2345432342');
20624 function limitToFilter() {
20625 return function(input, limit, begin) {
20626 if (Math.abs(Number(limit)) === Infinity) {
20627 limit = Number(limit);
20629 limit = toInt(limit);
20631 if (isNaN(limit)) return input;
20633 if (isNumber(input)) input = input.toString();
20634 if (!isArray(input) && !isString(input)) return input;
20636 begin = (!begin || isNaN(begin)) ? 0 : toInt(begin);
20637 begin = (begin < 0) ? Math.max(0, input.length + begin) : begin;
20640 return input.slice(begin, begin + limit);
20643 return input.slice(limit, input.length);
20645 return input.slice(Math.max(0, begin + limit), begin);
20657 * Orders a specified `array` by the `expression` predicate. It is ordered alphabetically
20658 * for strings and numerically for numbers. Note: if you notice numbers are not being sorted
20659 * as expected, make sure they are actually being saved as numbers and not strings.
20660 * Array-like values (e.g. NodeLists, jQuery objects, TypedArrays, Strings, etc) are also supported.
20662 * @param {Array} array The array (or array-like object) to sort.
20663 * @param {function(*)|string|Array.<(function(*)|string)>=} expression A predicate to be
20664 * used by the comparator to determine the order of elements.
20668 * - `function`: Getter function. The result of this function will be sorted using the
20669 * `<`, `===`, `>` operator.
20670 * - `string`: An Angular expression. The result of this expression is used to compare elements
20671 * (for example `name` to sort by a property called `name` or `name.substr(0, 3)` to sort by
20672 * 3 first characters of a property called `name`). The result of a constant expression
20673 * is interpreted as a property name to be used in comparisons (for example `"special name"`
20674 * to sort object by the value of their `special name` property). An expression can be
20675 * optionally prefixed with `+` or `-` to control ascending or descending sort order
20676 * (for example, `+name` or `-name`). If no property is provided, (e.g. `'+'`) then the array
20677 * element itself is used to compare where sorting.
20678 * - `Array`: An array of function or string predicates. The first predicate in the array
20679 * is used for sorting, but when two items are equivalent, the next predicate is used.
20681 * If the predicate is missing or empty then it defaults to `'+'`.
20683 * @param {boolean=} reverse Reverse the order of the array.
20684 * @returns {Array} Sorted copy of the source array.
20688 * The example below demonstrates a simple ngRepeat, where the data is sorted
20689 * by age in descending order (predicate is set to `'-age'`).
20690 * `reverse` is not set, which means it defaults to `false`.
20691 <example module="orderByExample">
20692 <file name="index.html">
20693 <div ng-controller="ExampleController">
20694 <table class="friend">
20697 <th>Phone Number</th>
20700 <tr ng-repeat="friend in friends | orderBy:'-age'">
20701 <td>{{friend.name}}</td>
20702 <td>{{friend.phone}}</td>
20703 <td>{{friend.age}}</td>
20708 <file name="script.js">
20709 angular.module('orderByExample', [])
20710 .controller('ExampleController', ['$scope', function($scope) {
20712 [{name:'John', phone:'555-1212', age:10},
20713 {name:'Mary', phone:'555-9876', age:19},
20714 {name:'Mike', phone:'555-4321', age:21},
20715 {name:'Adam', phone:'555-5678', age:35},
20716 {name:'Julie', phone:'555-8765', age:29}];
20721 * The predicate and reverse parameters can be controlled dynamically through scope properties,
20722 * as shown in the next example.
20724 <example module="orderByExample">
20725 <file name="index.html">
20726 <div ng-controller="ExampleController">
20727 <pre>Sorting predicate = {{predicate}}; reverse = {{reverse}}</pre>
20729 <button ng-click="predicate=''">Set to unsorted</button>
20730 <table class="friend">
20733 <button ng-click="order('name')">Name</button>
20734 <span class="sortorder" ng-show="predicate === 'name'" ng-class="{reverse:reverse}"></span>
20737 <button ng-click="order('phone')">Phone Number</button>
20738 <span class="sortorder" ng-show="predicate === 'phone'" ng-class="{reverse:reverse}"></span>
20741 <button ng-click="order('age')">Age</button>
20742 <span class="sortorder" ng-show="predicate === 'age'" ng-class="{reverse:reverse}"></span>
20745 <tr ng-repeat="friend in friends | orderBy:predicate:reverse">
20746 <td>{{friend.name}}</td>
20747 <td>{{friend.phone}}</td>
20748 <td>{{friend.age}}</td>
20753 <file name="script.js">
20754 angular.module('orderByExample', [])
20755 .controller('ExampleController', ['$scope', function($scope) {
20757 [{name:'John', phone:'555-1212', age:10},
20758 {name:'Mary', phone:'555-9876', age:19},
20759 {name:'Mike', phone:'555-4321', age:21},
20760 {name:'Adam', phone:'555-5678', age:35},
20761 {name:'Julie', phone:'555-8765', age:29}];
20762 $scope.predicate = 'age';
20763 $scope.reverse = true;
20764 $scope.order = function(predicate) {
20765 $scope.reverse = ($scope.predicate === predicate) ? !$scope.reverse : false;
20766 $scope.predicate = predicate;
20770 <file name="style.css">
20774 .sortorder.reverse:after {
20780 * It's also possible to call the orderBy filter manually, by injecting `$filter`, retrieving the
20781 * filter routine with `$filter('orderBy')`, and calling the returned filter routine with the
20782 * desired parameters.
20787 <example module="orderByExample">
20788 <file name="index.html">
20789 <div ng-controller="ExampleController">
20790 <pre>Sorting predicate = {{predicate}}; reverse = {{reverse}}</pre>
20791 <table class="friend">
20794 <button ng-click="order('name')">Name</button>
20795 <span class="sortorder" ng-show="predicate === 'name'" ng-class="{reverse:reverse}"></span>
20798 <button ng-click="order('phone')">Phone Number</button>
20799 <span class="sortorder" ng-show="predicate === 'phone'" ng-class="{reverse:reverse}"></span>
20802 <button ng-click="order('age')">Age</button>
20803 <span class="sortorder" ng-show="predicate === 'age'" ng-class="{reverse:reverse}"></span>
20806 <tr ng-repeat="friend in friends">
20807 <td>{{friend.name}}</td>
20808 <td>{{friend.phone}}</td>
20809 <td>{{friend.age}}</td>
20815 <file name="script.js">
20816 angular.module('orderByExample', [])
20817 .controller('ExampleController', ['$scope', '$filter', function($scope, $filter) {
20818 var orderBy = $filter('orderBy');
20820 { name: 'John', phone: '555-1212', age: 10 },
20821 { name: 'Mary', phone: '555-9876', age: 19 },
20822 { name: 'Mike', phone: '555-4321', age: 21 },
20823 { name: 'Adam', phone: '555-5678', age: 35 },
20824 { name: 'Julie', phone: '555-8765', age: 29 }
20826 $scope.order = function(predicate) {
20827 $scope.predicate = predicate;
20828 $scope.reverse = ($scope.predicate === predicate) ? !$scope.reverse : false;
20829 $scope.friends = orderBy($scope.friends, predicate, $scope.reverse);
20831 $scope.order('age', true);
20835 <file name="style.css">
20839 .sortorder.reverse:after {
20845 orderByFilter.$inject = ['$parse'];
20846 function orderByFilter($parse) {
20847 return function(array, sortPredicate, reverseOrder) {
20849 if (array == null) return array;
20850 if (!isArrayLike(array)) {
20851 throw minErr('orderBy')('notarray', 'Expected array but received: {0}', array);
20854 if (!isArray(sortPredicate)) { sortPredicate = [sortPredicate]; }
20855 if (sortPredicate.length === 0) { sortPredicate = ['+']; }
20857 var predicates = processPredicates(sortPredicate, reverseOrder);
20858 // Add a predicate at the end that evaluates to the element index. This makes the
20859 // sort stable as it works as a tie-breaker when all the input predicates cannot
20860 // distinguish between two elements.
20861 predicates.push({ get: function() { return {}; }, descending: reverseOrder ? -1 : 1});
20863 // The next three lines are a version of a Swartzian Transform idiom from Perl
20864 // (sometimes called the Decorate-Sort-Undecorate idiom)
20865 // See https://en.wikipedia.org/wiki/Schwartzian_transform
20866 var compareValues = Array.prototype.map.call(array, getComparisonObject);
20867 compareValues.sort(doComparison);
20868 array = compareValues.map(function(item) { return item.value; });
20872 function getComparisonObject(value, index) {
20875 predicateValues: predicates.map(function(predicate) {
20876 return getPredicateValue(predicate.get(value), index);
20881 function doComparison(v1, v2) {
20883 for (var index=0, length = predicates.length; index < length; ++index) {
20884 result = compare(v1.predicateValues[index], v2.predicateValues[index]) * predicates[index].descending;
20891 function processPredicates(sortPredicate, reverseOrder) {
20892 reverseOrder = reverseOrder ? -1 : 1;
20893 return sortPredicate.map(function(predicate) {
20894 var descending = 1, get = identity;
20896 if (isFunction(predicate)) {
20898 } else if (isString(predicate)) {
20899 if ((predicate.charAt(0) == '+' || predicate.charAt(0) == '-')) {
20900 descending = predicate.charAt(0) == '-' ? -1 : 1;
20901 predicate = predicate.substring(1);
20903 if (predicate !== '') {
20904 get = $parse(predicate);
20905 if (get.constant) {
20907 get = function(value) { return value[key]; };
20911 return { get: get, descending: descending * reverseOrder };
20915 function isPrimitive(value) {
20916 switch (typeof value) {
20917 case 'number': /* falls through */
20918 case 'boolean': /* falls through */
20926 function objectValue(value, index) {
20927 // If `valueOf` is a valid function use that
20928 if (typeof value.valueOf === 'function') {
20929 value = value.valueOf();
20930 if (isPrimitive(value)) return value;
20932 // If `toString` is a valid function and not the one from `Object.prototype` use that
20933 if (hasCustomToString(value)) {
20934 value = value.toString();
20935 if (isPrimitive(value)) return value;
20937 // We have a basic object so we use the position of the object in the collection
20941 function getPredicateValue(value, index) {
20942 var type = typeof value;
20943 if (value === null) {
20946 } else if (type === 'string') {
20947 value = value.toLowerCase();
20948 } else if (type === 'object') {
20949 value = objectValue(value, index);
20951 return { value: value, type: type };
20954 function compare(v1, v2) {
20956 if (v1.type === v2.type) {
20957 if (v1.value !== v2.value) {
20958 result = v1.value < v2.value ? -1 : 1;
20961 result = v1.type < v2.type ? -1 : 1;
20967 function ngDirective(directive) {
20968 if (isFunction(directive)) {
20973 directive.restrict = directive.restrict || 'AC';
20974 return valueFn(directive);
20983 * Modifies the default behavior of the html A tag so that the default action is prevented when
20984 * the href attribute is empty.
20986 * This change permits the easy creation of action links with the `ngClick` directive
20987 * without changing the location or causing page reloads, e.g.:
20988 * `<a href="" ng-click="list.addItem()">Add Item</a>`
20990 var htmlAnchorDirective = valueFn({
20992 compile: function(element, attr) {
20993 if (!attr.href && !attr.xlinkHref) {
20994 return function(scope, element) {
20995 // If the linked element is not an anchor tag anymore, do nothing
20996 if (element[0].nodeName.toLowerCase() !== 'a') return;
20998 // SVGAElement does not use the href attribute, but rather the 'xlinkHref' attribute.
20999 var href = toString.call(element.prop('href')) === '[object SVGAnimatedString]' ?
21000 'xlink:href' : 'href';
21001 element.on('click', function(event) {
21002 // if we have no href url, then don't navigate anywhere.
21003 if (!element.attr(href)) {
21004 event.preventDefault();
21019 * Using Angular markup like `{{hash}}` in an href attribute will
21020 * make the link go to the wrong URL if the user clicks it before
21021 * Angular has a chance to replace the `{{hash}}` markup with its
21022 * value. Until Angular replaces the markup the link will be broken
21023 * and will most likely return a 404 error. The `ngHref` directive
21024 * solves this problem.
21026 * The wrong way to write it:
21028 * <a href="http://www.gravatar.com/avatar/{{hash}}">link1</a>
21031 * The correct way to write it:
21033 * <a ng-href="http://www.gravatar.com/avatar/{{hash}}">link1</a>
21037 * @param {template} ngHref any string which can contain `{{}}` markup.
21040 * This example shows various combinations of `href`, `ng-href` and `ng-click` attributes
21041 * in links and their different behaviors:
21043 <file name="index.html">
21044 <input ng-model="value" /><br />
21045 <a id="link-1" href ng-click="value = 1">link 1</a> (link, don't reload)<br />
21046 <a id="link-2" href="" ng-click="value = 2">link 2</a> (link, don't reload)<br />
21047 <a id="link-3" ng-href="/{{'123'}}">link 3</a> (link, reload!)<br />
21048 <a id="link-4" href="" name="xx" ng-click="value = 4">anchor</a> (link, don't reload)<br />
21049 <a id="link-5" name="xxx" ng-click="value = 5">anchor</a> (no link)<br />
21050 <a id="link-6" ng-href="{{value}}">link</a> (link, change location)
21052 <file name="protractor.js" type="protractor">
21053 it('should execute ng-click but not reload when href without value', function() {
21054 element(by.id('link-1')).click();
21055 expect(element(by.model('value')).getAttribute('value')).toEqual('1');
21056 expect(element(by.id('link-1')).getAttribute('href')).toBe('');
21059 it('should execute ng-click but not reload when href empty string', function() {
21060 element(by.id('link-2')).click();
21061 expect(element(by.model('value')).getAttribute('value')).toEqual('2');
21062 expect(element(by.id('link-2')).getAttribute('href')).toBe('');
21065 it('should execute ng-click and change url when ng-href specified', function() {
21066 expect(element(by.id('link-3')).getAttribute('href')).toMatch(/\/123$/);
21068 element(by.id('link-3')).click();
21070 // At this point, we navigate away from an Angular page, so we need
21071 // to use browser.driver to get the base webdriver.
21073 browser.wait(function() {
21074 return browser.driver.getCurrentUrl().then(function(url) {
21075 return url.match(/\/123$/);
21077 }, 5000, 'page should navigate to /123');
21080 it('should execute ng-click but not reload when href empty string and name specified', function() {
21081 element(by.id('link-4')).click();
21082 expect(element(by.model('value')).getAttribute('value')).toEqual('4');
21083 expect(element(by.id('link-4')).getAttribute('href')).toBe('');
21086 it('should execute ng-click but not reload when no href but name specified', function() {
21087 element(by.id('link-5')).click();
21088 expect(element(by.model('value')).getAttribute('value')).toEqual('5');
21089 expect(element(by.id('link-5')).getAttribute('href')).toBe(null);
21092 it('should only change url when only ng-href', function() {
21093 element(by.model('value')).clear();
21094 element(by.model('value')).sendKeys('6');
21095 expect(element(by.id('link-6')).getAttribute('href')).toMatch(/\/6$/);
21097 element(by.id('link-6')).click();
21099 // At this point, we navigate away from an Angular page, so we need
21100 // to use browser.driver to get the base webdriver.
21101 browser.wait(function() {
21102 return browser.driver.getCurrentUrl().then(function(url) {
21103 return url.match(/\/6$/);
21105 }, 5000, 'page should navigate to /6');
21118 * Using Angular markup like `{{hash}}` in a `src` attribute doesn't
21119 * work right: The browser will fetch from the URL with the literal
21120 * text `{{hash}}` until Angular replaces the expression inside
21121 * `{{hash}}`. The `ngSrc` directive solves this problem.
21123 * The buggy way to write it:
21125 * <img src="http://www.gravatar.com/avatar/{{hash}}" alt="Description"/>
21128 * The correct way to write it:
21130 * <img ng-src="http://www.gravatar.com/avatar/{{hash}}" alt="Description" />
21134 * @param {template} ngSrc any string which can contain `{{}}` markup.
21144 * Using Angular markup like `{{hash}}` in a `srcset` attribute doesn't
21145 * work right: The browser will fetch from the URL with the literal
21146 * text `{{hash}}` until Angular replaces the expression inside
21147 * `{{hash}}`. The `ngSrcset` directive solves this problem.
21149 * The buggy way to write it:
21151 * <img srcset="http://www.gravatar.com/avatar/{{hash}} 2x" alt="Description"/>
21154 * The correct way to write it:
21156 * <img ng-srcset="http://www.gravatar.com/avatar/{{hash}} 2x" alt="Description" />
21160 * @param {template} ngSrcset any string which can contain `{{}}` markup.
21171 * This directive sets the `disabled` attribute on the element if the
21172 * {@link guide/expression expression} inside `ngDisabled` evaluates to truthy.
21174 * A special directive is necessary because we cannot use interpolation inside the `disabled`
21175 * attribute. See the {@link guide/interpolation interpolation guide} for more info.
21179 <file name="index.html">
21180 <label>Click me to toggle: <input type="checkbox" ng-model="checked"></label><br/>
21181 <button ng-model="button" ng-disabled="checked">Button</button>
21183 <file name="protractor.js" type="protractor">
21184 it('should toggle button', function() {
21185 expect(element(by.css('button')).getAttribute('disabled')).toBeFalsy();
21186 element(by.model('checked')).click();
21187 expect(element(by.css('button')).getAttribute('disabled')).toBeTruthy();
21193 * @param {expression} ngDisabled If the {@link guide/expression expression} is truthy,
21194 * then the `disabled` attribute will be set on the element
21205 * Sets the `checked` attribute on the element, if the expression inside `ngChecked` is truthy.
21207 * Note that this directive should not be used together with {@link ngModel `ngModel`},
21208 * as this can lead to unexpected behavior.
21210 * A special directive is necessary because we cannot use interpolation inside the `checked`
21211 * attribute. See the {@link guide/interpolation interpolation guide} for more info.
21215 <file name="index.html">
21216 <label>Check me to check both: <input type="checkbox" ng-model="master"></label><br/>
21217 <input id="checkSlave" type="checkbox" ng-checked="master" aria-label="Slave input">
21219 <file name="protractor.js" type="protractor">
21220 it('should check both checkBoxes', function() {
21221 expect(element(by.id('checkSlave')).getAttribute('checked')).toBeFalsy();
21222 element(by.model('master')).click();
21223 expect(element(by.id('checkSlave')).getAttribute('checked')).toBeTruthy();
21229 * @param {expression} ngChecked If the {@link guide/expression expression} is truthy,
21230 * then the `checked` attribute will be set on the element
21242 * Sets the `readOnly` attribute on the element, if the expression inside `ngReadonly` is truthy.
21244 * A special directive is necessary because we cannot use interpolation inside the `readOnly`
21245 * attribute. See the {@link guide/interpolation interpolation guide} for more info.
21249 <file name="index.html">
21250 <label>Check me to make text readonly: <input type="checkbox" ng-model="checked"></label><br/>
21251 <input type="text" ng-readonly="checked" value="I'm Angular" aria-label="Readonly field" />
21253 <file name="protractor.js" type="protractor">
21254 it('should toggle readonly attr', function() {
21255 expect(element(by.css('[type="text"]')).getAttribute('readonly')).toBeFalsy();
21256 element(by.model('checked')).click();
21257 expect(element(by.css('[type="text"]')).getAttribute('readonly')).toBeTruthy();
21263 * @param {expression} ngReadonly If the {@link guide/expression expression} is truthy,
21264 * then special attribute "readonly" will be set on the element
21276 * Sets the `selected` attribute on the element, if the expression inside `ngSelected` is truthy.
21278 * A special directive is necessary because we cannot use interpolation inside the `selected`
21279 * attribute. See the {@link guide/interpolation interpolation guide} for more info.
21283 <file name="index.html">
21284 <label>Check me to select: <input type="checkbox" ng-model="selected"></label><br/>
21285 <select aria-label="ngSelected demo">
21286 <option>Hello!</option>
21287 <option id="greet" ng-selected="selected">Greetings!</option>
21290 <file name="protractor.js" type="protractor">
21291 it('should select Greetings!', function() {
21292 expect(element(by.id('greet')).getAttribute('selected')).toBeFalsy();
21293 element(by.model('selected')).click();
21294 expect(element(by.id('greet')).getAttribute('selected')).toBeTruthy();
21300 * @param {expression} ngSelected If the {@link guide/expression expression} is truthy,
21301 * then special attribute "selected" will be set on the element
21312 * Sets the `open` attribute on the element, if the expression inside `ngOpen` is truthy.
21314 * A special directive is necessary because we cannot use interpolation inside the `open`
21315 * attribute. See the {@link guide/interpolation interpolation guide} for more info.
21319 <file name="index.html">
21320 <label>Check me check multiple: <input type="checkbox" ng-model="open"></label><br/>
21321 <details id="details" ng-open="open">
21322 <summary>Show/Hide me</summary>
21325 <file name="protractor.js" type="protractor">
21326 it('should toggle open', function() {
21327 expect(element(by.id('details')).getAttribute('open')).toBeFalsy();
21328 element(by.model('open')).click();
21329 expect(element(by.id('details')).getAttribute('open')).toBeTruthy();
21335 * @param {expression} ngOpen If the {@link guide/expression expression} is truthy,
21336 * then special attribute "open" will be set on the element
21339 var ngAttributeAliasDirectives = {};
21341 // boolean attrs are evaluated
21342 forEach(BOOLEAN_ATTR, function(propName, attrName) {
21343 // binding to multiple is not supported
21344 if (propName == "multiple") return;
21346 function defaultLinkFn(scope, element, attr) {
21347 scope.$watch(attr[normalized], function ngBooleanAttrWatchAction(value) {
21348 attr.$set(attrName, !!value);
21352 var normalized = directiveNormalize('ng-' + attrName);
21353 var linkFn = defaultLinkFn;
21355 if (propName === 'checked') {
21356 linkFn = function(scope, element, attr) {
21357 // ensuring ngChecked doesn't interfere with ngModel when both are set on the same input
21358 if (attr.ngModel !== attr[normalized]) {
21359 defaultLinkFn(scope, element, attr);
21364 ngAttributeAliasDirectives[normalized] = function() {
21373 // aliased input attrs are evaluated
21374 forEach(ALIASED_ATTR, function(htmlAttr, ngAttr) {
21375 ngAttributeAliasDirectives[ngAttr] = function() {
21378 link: function(scope, element, attr) {
21379 //special case ngPattern when a literal regular expression value
21380 //is used as the expression (this way we don't have to watch anything).
21381 if (ngAttr === "ngPattern" && attr.ngPattern.charAt(0) == "/") {
21382 var match = attr.ngPattern.match(REGEX_STRING_REGEXP);
21384 attr.$set("ngPattern", new RegExp(match[1], match[2]));
21389 scope.$watch(attr[ngAttr], function ngAttrAliasWatchAction(value) {
21390 attr.$set(ngAttr, value);
21397 // ng-src, ng-srcset, ng-href are interpolated
21398 forEach(['src', 'srcset', 'href'], function(attrName) {
21399 var normalized = directiveNormalize('ng-' + attrName);
21400 ngAttributeAliasDirectives[normalized] = function() {
21402 priority: 99, // it needs to run after the attributes are interpolated
21403 link: function(scope, element, attr) {
21404 var propName = attrName,
21407 if (attrName === 'href' &&
21408 toString.call(element.prop('href')) === '[object SVGAnimatedString]') {
21409 name = 'xlinkHref';
21410 attr.$attr[name] = 'xlink:href';
21414 attr.$observe(normalized, function(value) {
21416 if (attrName === 'href') {
21417 attr.$set(name, null);
21422 attr.$set(name, value);
21424 // on IE, if "ng:src" directive declaration is used and "src" attribute doesn't exist
21425 // then calling element.setAttribute('src', 'foo') doesn't do anything, so we need
21426 // to set the property as well to achieve the desired effect.
21427 // we use attr[attrName] value since $set can sanitize the url.
21428 if (msie && propName) element.prop(propName, attr[name]);
21435 /* global -nullFormCtrl, -SUBMITTED_CLASS, addSetValidityMethod: true
21437 var nullFormCtrl = {
21439 $$renameControl: nullFormRenameControl,
21440 $removeControl: noop,
21441 $setValidity: noop,
21443 $setPristine: noop,
21444 $setSubmitted: noop
21446 SUBMITTED_CLASS = 'ng-submitted';
21448 function nullFormRenameControl(control, name) {
21449 control.$name = name;
21454 * @name form.FormController
21456 * @property {boolean} $pristine True if user has not interacted with the form yet.
21457 * @property {boolean} $dirty True if user has already interacted with the form.
21458 * @property {boolean} $valid True if all of the containing forms and controls are valid.
21459 * @property {boolean} $invalid True if at least one containing control or form is invalid.
21460 * @property {boolean} $pending True if at least one containing control or form is pending.
21461 * @property {boolean} $submitted True if user has submitted the form even if its invalid.
21463 * @property {Object} $error Is an object hash, containing references to controls or
21464 * forms with failing validators, where:
21466 * - keys are validation tokens (error names),
21467 * - values are arrays of controls or forms that have a failing validator for given error name.
21469 * Built-in validation tokens:
21481 * - `datetimelocal`
21487 * `FormController` keeps track of all its controls and nested forms as well as the state of them,
21488 * such as being valid/invalid or dirty/pristine.
21490 * Each {@link ng.directive:form form} directive creates an instance
21491 * of `FormController`.
21494 //asks for $scope to fool the BC controller module
21495 FormController.$inject = ['$element', '$attrs', '$scope', '$animate', '$interpolate'];
21496 function FormController(element, attrs, $scope, $animate, $interpolate) {
21502 form.$$success = {};
21503 form.$pending = undefined;
21504 form.$name = $interpolate(attrs.name || attrs.ngForm || '')($scope);
21505 form.$dirty = false;
21506 form.$pristine = true;
21507 form.$valid = true;
21508 form.$invalid = false;
21509 form.$submitted = false;
21510 form.$$parentForm = nullFormCtrl;
21514 * @name form.FormController#$rollbackViewValue
21517 * Rollback all form controls pending updates to the `$modelValue`.
21519 * Updates may be pending by a debounced event or because the input is waiting for a some future
21520 * event defined in `ng-model-options`. This method is typically needed by the reset button of
21521 * a form that uses `ng-model-options` to pend updates.
21523 form.$rollbackViewValue = function() {
21524 forEach(controls, function(control) {
21525 control.$rollbackViewValue();
21531 * @name form.FormController#$commitViewValue
21534 * Commit all form controls pending updates to the `$modelValue`.
21536 * Updates may be pending by a debounced event or because the input is waiting for a some future
21537 * event defined in `ng-model-options`. This method is rarely needed as `NgModelController`
21538 * usually handles calling this in response to input events.
21540 form.$commitViewValue = function() {
21541 forEach(controls, function(control) {
21542 control.$commitViewValue();
21548 * @name form.FormController#$addControl
21549 * @param {object} control control object, either a {@link form.FormController} or an
21550 * {@link ngModel.NgModelController}
21553 * Register a control with the form. Input elements using ngModelController do this automatically
21554 * when they are linked.
21556 * Note that the current state of the control will not be reflected on the new parent form. This
21557 * is not an issue with normal use, as freshly compiled and linked controls are in a `$pristine`
21560 * However, if the method is used programmatically, for example by adding dynamically created controls,
21561 * or controls that have been previously removed without destroying their corresponding DOM element,
21562 * it's the developers responsibility to make sure the current state propagates to the parent form.
21564 * For example, if an input control is added that is already `$dirty` and has `$error` properties,
21565 * calling `$setDirty()` and `$validate()` afterwards will propagate the state to the parent form.
21567 form.$addControl = function(control) {
21568 // Breaking change - before, inputs whose name was "hasOwnProperty" were quietly ignored
21569 // and not added to the scope. Now we throw an error.
21570 assertNotHasOwnProperty(control.$name, 'input');
21571 controls.push(control);
21573 if (control.$name) {
21574 form[control.$name] = control;
21577 control.$$parentForm = form;
21580 // Private API: rename a form control
21581 form.$$renameControl = function(control, newName) {
21582 var oldName = control.$name;
21584 if (form[oldName] === control) {
21585 delete form[oldName];
21587 form[newName] = control;
21588 control.$name = newName;
21593 * @name form.FormController#$removeControl
21594 * @param {object} control control object, either a {@link form.FormController} or an
21595 * {@link ngModel.NgModelController}
21598 * Deregister a control from the form.
21600 * Input elements using ngModelController do this automatically when they are destroyed.
21602 * Note that only the removed control's validation state (`$errors`etc.) will be removed from the
21603 * form. `$dirty`, `$submitted` states will not be changed, because the expected behavior can be
21604 * different from case to case. For example, removing the only `$dirty` control from a form may or
21605 * may not mean that the form is still `$dirty`.
21607 form.$removeControl = function(control) {
21608 if (control.$name && form[control.$name] === control) {
21609 delete form[control.$name];
21611 forEach(form.$pending, function(value, name) {
21612 form.$setValidity(name, null, control);
21614 forEach(form.$error, function(value, name) {
21615 form.$setValidity(name, null, control);
21617 forEach(form.$$success, function(value, name) {
21618 form.$setValidity(name, null, control);
21621 arrayRemove(controls, control);
21622 control.$$parentForm = nullFormCtrl;
21628 * @name form.FormController#$setValidity
21631 * Sets the validity of a form control.
21633 * This method will also propagate to parent forms.
21635 addSetValidityMethod({
21638 set: function(object, property, controller) {
21639 var list = object[property];
21641 object[property] = [controller];
21643 var index = list.indexOf(controller);
21644 if (index === -1) {
21645 list.push(controller);
21649 unset: function(object, property, controller) {
21650 var list = object[property];
21654 arrayRemove(list, controller);
21655 if (list.length === 0) {
21656 delete object[property];
21664 * @name form.FormController#$setDirty
21667 * Sets the form to a dirty state.
21669 * This method can be called to add the 'ng-dirty' class and set the form to a dirty
21670 * state (ng-dirty class). This method will also propagate to parent forms.
21672 form.$setDirty = function() {
21673 $animate.removeClass(element, PRISTINE_CLASS);
21674 $animate.addClass(element, DIRTY_CLASS);
21675 form.$dirty = true;
21676 form.$pristine = false;
21677 form.$$parentForm.$setDirty();
21682 * @name form.FormController#$setPristine
21685 * Sets the form to its pristine state.
21687 * This method can be called to remove the 'ng-dirty' class and set the form to its pristine
21688 * state (ng-pristine class). This method will also propagate to all the controls contained
21691 * Setting a form back to a pristine state is often useful when we want to 'reuse' a form after
21692 * saving or resetting it.
21694 form.$setPristine = function() {
21695 $animate.setClass(element, PRISTINE_CLASS, DIRTY_CLASS + ' ' + SUBMITTED_CLASS);
21696 form.$dirty = false;
21697 form.$pristine = true;
21698 form.$submitted = false;
21699 forEach(controls, function(control) {
21700 control.$setPristine();
21706 * @name form.FormController#$setUntouched
21709 * Sets the form to its untouched state.
21711 * This method can be called to remove the 'ng-touched' class and set the form controls to their
21712 * untouched state (ng-untouched class).
21714 * Setting a form controls back to their untouched state is often useful when setting the form
21715 * back to its pristine state.
21717 form.$setUntouched = function() {
21718 forEach(controls, function(control) {
21719 control.$setUntouched();
21725 * @name form.FormController#$setSubmitted
21728 * Sets the form to its submitted state.
21730 form.$setSubmitted = function() {
21731 $animate.addClass(element, SUBMITTED_CLASS);
21732 form.$submitted = true;
21733 form.$$parentForm.$setSubmitted();
21743 * Nestable alias of {@link ng.directive:form `form`} directive. HTML
21744 * does not allow nesting of form elements. It is useful to nest forms, for example if the validity of a
21745 * sub-group of controls needs to be determined.
21747 * Note: the purpose of `ngForm` is to group controls,
21748 * but not to be a replacement for the `<form>` tag with all of its capabilities
21749 * (e.g. posting to the server, ...).
21751 * @param {string=} ngForm|name Name of the form. If specified, the form controller will be published into
21752 * related scope, under this name.
21762 * Directive that instantiates
21763 * {@link form.FormController FormController}.
21765 * If the `name` attribute is specified, the form controller is published onto the current scope under
21768 * # Alias: {@link ng.directive:ngForm `ngForm`}
21770 * In Angular, forms can be nested. This means that the outer form is valid when all of the child
21771 * forms are valid as well. However, browsers do not allow nesting of `<form>` elements, so
21772 * Angular provides the {@link ng.directive:ngForm `ngForm`} directive, which behaves identically to
21773 * `form` but can be nested. Nested forms can be useful, for example, if the validity of a sub-group
21774 * of controls needs to be determined.
21777 * - `ng-valid` is set if the form is valid.
21778 * - `ng-invalid` is set if the form is invalid.
21779 * - `ng-pending` is set if the form is pending.
21780 * - `ng-pristine` is set if the form is pristine.
21781 * - `ng-dirty` is set if the form is dirty.
21782 * - `ng-submitted` is set if the form was submitted.
21784 * Keep in mind that ngAnimate can detect each of these classes when added and removed.
21787 * # Submitting a form and preventing the default action
21789 * Since the role of forms in client-side Angular applications is different than in classical
21790 * roundtrip apps, it is desirable for the browser not to translate the form submission into a full
21791 * page reload that sends the data to the server. Instead some javascript logic should be triggered
21792 * to handle the form submission in an application-specific way.
21794 * For this reason, Angular prevents the default action (form submission to the server) unless the
21795 * `<form>` element has an `action` attribute specified.
21797 * You can use one of the following two ways to specify what javascript method should be called when
21798 * a form is submitted:
21800 * - {@link ng.directive:ngSubmit ngSubmit} directive on the form element
21801 * - {@link ng.directive:ngClick ngClick} directive on the first
21802 * button or input field of type submit (input[type=submit])
21804 * To prevent double execution of the handler, use only one of the {@link ng.directive:ngSubmit ngSubmit}
21805 * or {@link ng.directive:ngClick ngClick} directives.
21806 * This is because of the following form submission rules in the HTML specification:
21808 * - If a form has only one input field then hitting enter in this field triggers form submit
21810 * - if a form has 2+ input fields and no buttons or input[type=submit] then hitting enter
21811 * doesn't trigger submit
21812 * - if a form has one or more input fields and one or more buttons or input[type=submit] then
21813 * hitting enter in any of the input fields will trigger the click handler on the *first* button or
21814 * input[type=submit] (`ngClick`) *and* a submit handler on the enclosing form (`ngSubmit`)
21816 * Any pending `ngModelOptions` changes will take place immediately when an enclosing form is
21817 * submitted. Note that `ngClick` events will occur before the model is updated. Use `ngSubmit`
21818 * to have access to the updated model.
21820 * ## Animation Hooks
21822 * Animations in ngForm are triggered when any of the associated CSS classes are added and removed.
21823 * These classes are: `.ng-pristine`, `.ng-dirty`, `.ng-invalid` and `.ng-valid` as well as any
21824 * other validations that are performed within the form. Animations in ngForm are similar to how
21825 * they work in ngClass and animations can be hooked into using CSS transitions, keyframes as well
21826 * as JS animations.
21828 * The following example shows a simple way to utilize CSS transitions to style a form element
21829 * that has been rendered as invalid after it has been validated:
21832 * //be sure to include ngAnimate as a module to hook into more
21833 * //advanced animations
21835 * transition:0.5s linear all;
21836 * background: white;
21838 * .my-form.ng-invalid {
21845 <example deps="angular-animate.js" animations="true" fixBase="true" module="formExample">
21846 <file name="index.html">
21848 angular.module('formExample', [])
21849 .controller('FormController', ['$scope', function($scope) {
21850 $scope.userType = 'guest';
21855 transition:all linear 0.5s;
21856 background: transparent;
21858 .my-form.ng-invalid {
21862 <form name="myForm" ng-controller="FormController" class="my-form">
21863 userType: <input name="input" ng-model="userType" required>
21864 <span class="error" ng-show="myForm.input.$error.required">Required!</span><br>
21865 <code>userType = {{userType}}</code><br>
21866 <code>myForm.input.$valid = {{myForm.input.$valid}}</code><br>
21867 <code>myForm.input.$error = {{myForm.input.$error}}</code><br>
21868 <code>myForm.$valid = {{myForm.$valid}}</code><br>
21869 <code>myForm.$error.required = {{!!myForm.$error.required}}</code><br>
21872 <file name="protractor.js" type="protractor">
21873 it('should initialize to model', function() {
21874 var userType = element(by.binding('userType'));
21875 var valid = element(by.binding('myForm.input.$valid'));
21877 expect(userType.getText()).toContain('guest');
21878 expect(valid.getText()).toContain('true');
21881 it('should be invalid if empty', function() {
21882 var userType = element(by.binding('userType'));
21883 var valid = element(by.binding('myForm.input.$valid'));
21884 var userInput = element(by.model('userType'));
21887 userInput.sendKeys('');
21889 expect(userType.getText()).toEqual('userType =');
21890 expect(valid.getText()).toContain('false');
21895 * @param {string=} name Name of the form. If specified, the form controller will be published into
21896 * related scope, under this name.
21898 var formDirectiveFactory = function(isNgForm) {
21899 return ['$timeout', '$parse', function($timeout, $parse) {
21900 var formDirective = {
21902 restrict: isNgForm ? 'EAC' : 'E',
21903 require: ['form', '^^?form'], //first is the form's own ctrl, second is an optional parent form
21904 controller: FormController,
21905 compile: function ngFormCompile(formElement, attr) {
21906 // Setup initial state of the control
21907 formElement.addClass(PRISTINE_CLASS).addClass(VALID_CLASS);
21909 var nameAttr = attr.name ? 'name' : (isNgForm && attr.ngForm ? 'ngForm' : false);
21912 pre: function ngFormPreLink(scope, formElement, attr, ctrls) {
21913 var controller = ctrls[0];
21915 // if `action` attr is not present on the form, prevent the default action (submission)
21916 if (!('action' in attr)) {
21917 // we can't use jq events because if a form is destroyed during submission the default
21918 // action is not prevented. see #1238
21920 // IE 9 is not affected because it doesn't fire a submit event and try to do a full
21921 // page reload if the form was destroyed by submission of the form via a click handler
21922 // on a button in the form. Looks like an IE9 specific bug.
21923 var handleFormSubmission = function(event) {
21924 scope.$apply(function() {
21925 controller.$commitViewValue();
21926 controller.$setSubmitted();
21929 event.preventDefault();
21932 addEventListenerFn(formElement[0], 'submit', handleFormSubmission);
21934 // unregister the preventDefault listener so that we don't not leak memory but in a
21935 // way that will achieve the prevention of the default action.
21936 formElement.on('$destroy', function() {
21937 $timeout(function() {
21938 removeEventListenerFn(formElement[0], 'submit', handleFormSubmission);
21943 var parentFormCtrl = ctrls[1] || controller.$$parentForm;
21944 parentFormCtrl.$addControl(controller);
21946 var setter = nameAttr ? getSetter(controller.$name) : noop;
21949 setter(scope, controller);
21950 attr.$observe(nameAttr, function(newValue) {
21951 if (controller.$name === newValue) return;
21952 setter(scope, undefined);
21953 controller.$$parentForm.$$renameControl(controller, newValue);
21954 setter = getSetter(controller.$name);
21955 setter(scope, controller);
21958 formElement.on('$destroy', function() {
21959 controller.$$parentForm.$removeControl(controller);
21960 setter(scope, undefined);
21961 extend(controller, nullFormCtrl); //stop propagating child destruction handlers upwards
21968 return formDirective;
21970 function getSetter(expression) {
21971 if (expression === '') {
21972 //create an assignable expression, so forms with an empty name can be renamed later
21973 return $parse('this[""]').assign;
21975 return $parse(expression).assign || noop;
21980 var formDirective = formDirectiveFactory();
21981 var ngFormDirective = formDirectiveFactory(true);
21983 /* global VALID_CLASS: false,
21984 INVALID_CLASS: false,
21985 PRISTINE_CLASS: false,
21986 DIRTY_CLASS: false,
21987 UNTOUCHED_CLASS: false,
21988 TOUCHED_CLASS: false,
21989 ngModelMinErr: false,
21992 // Regex code was initially obtained from SO prior to modification: https://stackoverflow.com/questions/3143070/javascript-regex-iso-datetime#answer-3143231
21993 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)$/;
21994 // See valid URLs in RFC3987 (http://tools.ietf.org/html/rfc3987)
21995 // Note: We are being more lenient, because browsers are too.
22005 // 1111111111111111 222 333333 44444 555555555555555555555555 666 77777777 8888888 999
22006 var URL_REGEXP = /^[a-z][a-z\d.+-]*:\/*(?:[^:@]+(?::[^@]+)?@)?(?:[^\s:/?#]+|\[[a-f\d:]+\])(?::\d+)?(?:\/[^?#]*)?(?:\?[^#]*)?(?:#.*)?$/i;
22007 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;
22008 var NUMBER_REGEXP = /^\s*(\-|\+)?(\d+|(\d*(\.\d*)))([eE][+-]?\d+)?\s*$/;
22009 var DATE_REGEXP = /^(\d{4,})-(\d{2})-(\d{2})$/;
22010 var DATETIMELOCAL_REGEXP = /^(\d{4,})-(\d\d)-(\d\d)T(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
22011 var WEEK_REGEXP = /^(\d{4,})-W(\d\d)$/;
22012 var MONTH_REGEXP = /^(\d{4,})-(\d\d)$/;
22013 var TIME_REGEXP = /^(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
22015 var PARTIAL_VALIDATION_EVENTS = 'keydown wheel mousedown';
22016 var PARTIAL_VALIDATION_TYPES = createMap();
22017 forEach('date,datetime-local,month,time,week'.split(','), function(type) {
22018 PARTIAL_VALIDATION_TYPES[type] = true;
22025 * @name input[text]
22028 * Standard HTML text input with angular data binding, inherited by most of the `input` elements.
22031 * @param {string} ngModel Assignable angular expression to data-bind to.
22032 * @param {string=} name Property name of the form under which the control is published.
22033 * @param {string=} required Adds `required` validation error key if the value is not entered.
22034 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
22035 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
22036 * `required` when you want to data-bind to the `required` attribute.
22037 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
22039 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
22040 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
22042 * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
22043 * that contains the regular expression body that will be converted to a regular expression
22044 * as in the ngPattern directive.
22045 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
22046 * does not match a RegExp found by evaluating the Angular expression given in the attribute value.
22047 * If the expression evaluates to a RegExp object, then this is used directly.
22048 * If the expression evaluates to a string, then it will be converted to a RegExp
22049 * after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
22050 * `new RegExp('^abc$')`.<br />
22051 * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
22052 * start at the index of the last search's match, thus not taking the whole input value into
22054 * @param {string=} ngChange Angular expression to be executed when input changes due to user
22055 * interaction with the input element.
22056 * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
22057 * This parameter is ignored for input[type=password] controls, which will never trim the
22061 <example name="text-input-directive" module="textInputExample">
22062 <file name="index.html">
22064 angular.module('textInputExample', [])
22065 .controller('ExampleController', ['$scope', function($scope) {
22068 word: /^\s*\w*\s*$/
22072 <form name="myForm" ng-controller="ExampleController">
22073 <label>Single word:
22074 <input type="text" name="input" ng-model="example.text"
22075 ng-pattern="example.word" required ng-trim="false">
22078 <span class="error" ng-show="myForm.input.$error.required">
22080 <span class="error" ng-show="myForm.input.$error.pattern">
22081 Single word only!</span>
22083 <tt>text = {{example.text}}</tt><br/>
22084 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
22085 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
22086 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
22087 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
22090 <file name="protractor.js" type="protractor">
22091 var text = element(by.binding('example.text'));
22092 var valid = element(by.binding('myForm.input.$valid'));
22093 var input = element(by.model('example.text'));
22095 it('should initialize to model', function() {
22096 expect(text.getText()).toContain('guest');
22097 expect(valid.getText()).toContain('true');
22100 it('should be invalid if empty', function() {
22102 input.sendKeys('');
22104 expect(text.getText()).toEqual('text =');
22105 expect(valid.getText()).toContain('false');
22108 it('should be invalid if multi word', function() {
22110 input.sendKeys('hello world');
22112 expect(valid.getText()).toContain('false');
22117 'text': textInputType,
22121 * @name input[date]
22124 * Input with date validation and transformation. In browsers that do not yet support
22125 * the HTML5 date input, a text element will be used. In that case, text must be entered in a valid ISO-8601
22126 * date format (yyyy-MM-dd), for example: `2009-01-06`. Since many
22127 * modern browsers do not yet support this input type, it is important to provide cues to users on the
22128 * expected input format via a placeholder or label.
22130 * The model must always be a Date object, otherwise Angular will throw an error.
22131 * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
22133 * The timezone to be used to read/write the `Date` instance in the model can be defined using
22134 * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
22136 * @param {string} ngModel Assignable angular expression to data-bind to.
22137 * @param {string=} name Property name of the form under which the control is published.
22138 * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be a
22139 * valid ISO date string (yyyy-MM-dd). You can also use interpolation inside this attribute
22140 * (e.g. `min="{{minDate | date:'yyyy-MM-dd'}}"`). Note that `min` will also add native HTML5
22141 * constraint validation.
22142 * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must be
22143 * a valid ISO date string (yyyy-MM-dd). You can also use interpolation inside this attribute
22144 * (e.g. `max="{{maxDate | date:'yyyy-MM-dd'}}"`). Note that `max` will also add native HTML5
22145 * constraint validation.
22146 * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO date string
22147 * the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
22148 * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO date string
22149 * the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
22150 * @param {string=} required Sets `required` validation error key if the value is not entered.
22151 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
22152 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
22153 * `required` when you want to data-bind to the `required` attribute.
22154 * @param {string=} ngChange Angular expression to be executed when input changes due to user
22155 * interaction with the input element.
22158 <example name="date-input-directive" module="dateInputExample">
22159 <file name="index.html">
22161 angular.module('dateInputExample', [])
22162 .controller('DateController', ['$scope', function($scope) {
22164 value: new Date(2013, 9, 22)
22168 <form name="myForm" ng-controller="DateController as dateCtrl">
22169 <label for="exampleInput">Pick a date in 2013:</label>
22170 <input type="date" id="exampleInput" name="input" ng-model="example.value"
22171 placeholder="yyyy-MM-dd" min="2013-01-01" max="2013-12-31" required />
22173 <span class="error" ng-show="myForm.input.$error.required">
22175 <span class="error" ng-show="myForm.input.$error.date">
22176 Not a valid date!</span>
22178 <tt>value = {{example.value | date: "yyyy-MM-dd"}}</tt><br/>
22179 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
22180 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
22181 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
22182 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
22185 <file name="protractor.js" type="protractor">
22186 var value = element(by.binding('example.value | date: "yyyy-MM-dd"'));
22187 var valid = element(by.binding('myForm.input.$valid'));
22188 var input = element(by.model('example.value'));
22190 // currently protractor/webdriver does not support
22191 // sending keys to all known HTML5 input controls
22192 // for various browsers (see https://github.com/angular/protractor/issues/562).
22193 function setInput(val) {
22194 // set the value of the element and force validation.
22195 var scr = "var ipt = document.getElementById('exampleInput'); " +
22196 "ipt.value = '" + val + "';" +
22197 "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
22198 browser.executeScript(scr);
22201 it('should initialize to model', function() {
22202 expect(value.getText()).toContain('2013-10-22');
22203 expect(valid.getText()).toContain('myForm.input.$valid = true');
22206 it('should be invalid if empty', function() {
22208 expect(value.getText()).toEqual('value =');
22209 expect(valid.getText()).toContain('myForm.input.$valid = false');
22212 it('should be invalid if over max', function() {
22213 setInput('2015-01-01');
22214 expect(value.getText()).toContain('');
22215 expect(valid.getText()).toContain('myForm.input.$valid = false');
22220 'date': createDateInputType('date', DATE_REGEXP,
22221 createDateParser(DATE_REGEXP, ['yyyy', 'MM', 'dd']),
22226 * @name input[datetime-local]
22229 * Input with datetime validation and transformation. In browsers that do not yet support
22230 * the HTML5 date input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
22231 * local datetime format (yyyy-MM-ddTHH:mm:ss), for example: `2010-12-28T14:57:00`.
22233 * The model must always be a Date object, otherwise Angular will throw an error.
22234 * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
22236 * The timezone to be used to read/write the `Date` instance in the model can be defined using
22237 * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
22239 * @param {string} ngModel Assignable angular expression to data-bind to.
22240 * @param {string=} name Property name of the form under which the control is published.
22241 * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
22242 * This must be a valid ISO datetime format (yyyy-MM-ddTHH:mm:ss). You can also use interpolation
22243 * inside this attribute (e.g. `min="{{minDatetimeLocal | date:'yyyy-MM-ddTHH:mm:ss'}}"`).
22244 * Note that `min` will also add native HTML5 constraint validation.
22245 * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
22246 * This must be a valid ISO datetime format (yyyy-MM-ddTHH:mm:ss). You can also use interpolation
22247 * inside this attribute (e.g. `max="{{maxDatetimeLocal | date:'yyyy-MM-ddTHH:mm:ss'}}"`).
22248 * Note that `max` will also add native HTML5 constraint validation.
22249 * @param {(date|string)=} ngMin Sets the `min` validation error key to the Date / ISO datetime string
22250 * the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
22251 * @param {(date|string)=} ngMax Sets the `max` validation error key to the Date / ISO datetime string
22252 * the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
22253 * @param {string=} required Sets `required` validation error key if the value is not entered.
22254 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
22255 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
22256 * `required` when you want to data-bind to the `required` attribute.
22257 * @param {string=} ngChange Angular expression to be executed when input changes due to user
22258 * interaction with the input element.
22261 <example name="datetimelocal-input-directive" module="dateExample">
22262 <file name="index.html">
22264 angular.module('dateExample', [])
22265 .controller('DateController', ['$scope', function($scope) {
22267 value: new Date(2010, 11, 28, 14, 57)
22271 <form name="myForm" ng-controller="DateController as dateCtrl">
22272 <label for="exampleInput">Pick a date between in 2013:</label>
22273 <input type="datetime-local" id="exampleInput" name="input" ng-model="example.value"
22274 placeholder="yyyy-MM-ddTHH:mm:ss" min="2001-01-01T00:00:00" max="2013-12-31T00:00:00" required />
22276 <span class="error" ng-show="myForm.input.$error.required">
22278 <span class="error" ng-show="myForm.input.$error.datetimelocal">
22279 Not a valid date!</span>
22281 <tt>value = {{example.value | date: "yyyy-MM-ddTHH:mm:ss"}}</tt><br/>
22282 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
22283 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
22284 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
22285 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
22288 <file name="protractor.js" type="protractor">
22289 var value = element(by.binding('example.value | date: "yyyy-MM-ddTHH:mm:ss"'));
22290 var valid = element(by.binding('myForm.input.$valid'));
22291 var input = element(by.model('example.value'));
22293 // currently protractor/webdriver does not support
22294 // sending keys to all known HTML5 input controls
22295 // for various browsers (https://github.com/angular/protractor/issues/562).
22296 function setInput(val) {
22297 // set the value of the element and force validation.
22298 var scr = "var ipt = document.getElementById('exampleInput'); " +
22299 "ipt.value = '" + val + "';" +
22300 "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
22301 browser.executeScript(scr);
22304 it('should initialize to model', function() {
22305 expect(value.getText()).toContain('2010-12-28T14:57:00');
22306 expect(valid.getText()).toContain('myForm.input.$valid = true');
22309 it('should be invalid if empty', function() {
22311 expect(value.getText()).toEqual('value =');
22312 expect(valid.getText()).toContain('myForm.input.$valid = false');
22315 it('should be invalid if over max', function() {
22316 setInput('2015-01-01T23:59:00');
22317 expect(value.getText()).toContain('');
22318 expect(valid.getText()).toContain('myForm.input.$valid = false');
22323 'datetime-local': createDateInputType('datetimelocal', DATETIMELOCAL_REGEXP,
22324 createDateParser(DATETIMELOCAL_REGEXP, ['yyyy', 'MM', 'dd', 'HH', 'mm', 'ss', 'sss']),
22325 'yyyy-MM-ddTHH:mm:ss.sss'),
22329 * @name input[time]
22332 * Input with time validation and transformation. In browsers that do not yet support
22333 * the HTML5 time input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
22334 * local time format (HH:mm:ss), for example: `14:57:00`. Model must be a Date object. This binding will always output a
22335 * Date object to the model of January 1, 1970, or local date `new Date(1970, 0, 1, HH, mm, ss)`.
22337 * The model must always be a Date object, otherwise Angular will throw an error.
22338 * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
22340 * The timezone to be used to read/write the `Date` instance in the model can be defined using
22341 * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
22343 * @param {string} ngModel Assignable angular expression to data-bind to.
22344 * @param {string=} name Property name of the form under which the control is published.
22345 * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
22346 * This must be a valid ISO time format (HH:mm:ss). You can also use interpolation inside this
22347 * attribute (e.g. `min="{{minTime | date:'HH:mm:ss'}}"`). Note that `min` will also add
22348 * native HTML5 constraint validation.
22349 * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
22350 * This must be a valid ISO time format (HH:mm:ss). You can also use interpolation inside this
22351 * attribute (e.g. `max="{{maxTime | date:'HH:mm:ss'}}"`). Note that `max` will also add
22352 * native HTML5 constraint validation.
22353 * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO time string the
22354 * `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
22355 * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO time string the
22356 * `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
22357 * @param {string=} required Sets `required` validation error key if the value is not entered.
22358 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
22359 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
22360 * `required` when you want to data-bind to the `required` attribute.
22361 * @param {string=} ngChange Angular expression to be executed when input changes due to user
22362 * interaction with the input element.
22365 <example name="time-input-directive" module="timeExample">
22366 <file name="index.html">
22368 angular.module('timeExample', [])
22369 .controller('DateController', ['$scope', function($scope) {
22371 value: new Date(1970, 0, 1, 14, 57, 0)
22375 <form name="myForm" ng-controller="DateController as dateCtrl">
22376 <label for="exampleInput">Pick a time between 8am and 5pm:</label>
22377 <input type="time" id="exampleInput" name="input" ng-model="example.value"
22378 placeholder="HH:mm:ss" min="08:00:00" max="17:00:00" required />
22380 <span class="error" ng-show="myForm.input.$error.required">
22382 <span class="error" ng-show="myForm.input.$error.time">
22383 Not a valid date!</span>
22385 <tt>value = {{example.value | date: "HH:mm:ss"}}</tt><br/>
22386 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
22387 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
22388 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
22389 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
22392 <file name="protractor.js" type="protractor">
22393 var value = element(by.binding('example.value | date: "HH:mm:ss"'));
22394 var valid = element(by.binding('myForm.input.$valid'));
22395 var input = element(by.model('example.value'));
22397 // currently protractor/webdriver does not support
22398 // sending keys to all known HTML5 input controls
22399 // for various browsers (https://github.com/angular/protractor/issues/562).
22400 function setInput(val) {
22401 // set the value of the element and force validation.
22402 var scr = "var ipt = document.getElementById('exampleInput'); " +
22403 "ipt.value = '" + val + "';" +
22404 "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
22405 browser.executeScript(scr);
22408 it('should initialize to model', function() {
22409 expect(value.getText()).toContain('14:57:00');
22410 expect(valid.getText()).toContain('myForm.input.$valid = true');
22413 it('should be invalid if empty', function() {
22415 expect(value.getText()).toEqual('value =');
22416 expect(valid.getText()).toContain('myForm.input.$valid = false');
22419 it('should be invalid if over max', function() {
22420 setInput('23:59:00');
22421 expect(value.getText()).toContain('');
22422 expect(valid.getText()).toContain('myForm.input.$valid = false');
22427 'time': createDateInputType('time', TIME_REGEXP,
22428 createDateParser(TIME_REGEXP, ['HH', 'mm', 'ss', 'sss']),
22433 * @name input[week]
22436 * Input with week-of-the-year validation and transformation to Date. In browsers that do not yet support
22437 * the HTML5 week input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
22438 * week format (yyyy-W##), for example: `2013-W02`.
22440 * The model must always be a Date object, otherwise Angular will throw an error.
22441 * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
22443 * The timezone to be used to read/write the `Date` instance in the model can be defined using
22444 * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
22446 * @param {string} ngModel Assignable angular expression to data-bind to.
22447 * @param {string=} name Property name of the form under which the control is published.
22448 * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
22449 * This must be a valid ISO week format (yyyy-W##). You can also use interpolation inside this
22450 * attribute (e.g. `min="{{minWeek | date:'yyyy-Www'}}"`). Note that `min` will also add
22451 * native HTML5 constraint validation.
22452 * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
22453 * This must be a valid ISO week format (yyyy-W##). You can also use interpolation inside this
22454 * attribute (e.g. `max="{{maxWeek | date:'yyyy-Www'}}"`). Note that `max` will also add
22455 * native HTML5 constraint validation.
22456 * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO week string
22457 * the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
22458 * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO week string
22459 * the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
22460 * @param {string=} required Sets `required` validation error key if the value is not entered.
22461 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
22462 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
22463 * `required` when you want to data-bind to the `required` attribute.
22464 * @param {string=} ngChange Angular expression to be executed when input changes due to user
22465 * interaction with the input element.
22468 <example name="week-input-directive" module="weekExample">
22469 <file name="index.html">
22471 angular.module('weekExample', [])
22472 .controller('DateController', ['$scope', function($scope) {
22474 value: new Date(2013, 0, 3)
22478 <form name="myForm" ng-controller="DateController as dateCtrl">
22479 <label>Pick a date between in 2013:
22480 <input id="exampleInput" type="week" name="input" ng-model="example.value"
22481 placeholder="YYYY-W##" min="2012-W32"
22482 max="2013-W52" required />
22485 <span class="error" ng-show="myForm.input.$error.required">
22487 <span class="error" ng-show="myForm.input.$error.week">
22488 Not a valid date!</span>
22490 <tt>value = {{example.value | date: "yyyy-Www"}}</tt><br/>
22491 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
22492 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
22493 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
22494 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
22497 <file name="protractor.js" type="protractor">
22498 var value = element(by.binding('example.value | date: "yyyy-Www"'));
22499 var valid = element(by.binding('myForm.input.$valid'));
22500 var input = element(by.model('example.value'));
22502 // currently protractor/webdriver does not support
22503 // sending keys to all known HTML5 input controls
22504 // for various browsers (https://github.com/angular/protractor/issues/562).
22505 function setInput(val) {
22506 // set the value of the element and force validation.
22507 var scr = "var ipt = document.getElementById('exampleInput'); " +
22508 "ipt.value = '" + val + "';" +
22509 "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
22510 browser.executeScript(scr);
22513 it('should initialize to model', function() {
22514 expect(value.getText()).toContain('2013-W01');
22515 expect(valid.getText()).toContain('myForm.input.$valid = true');
22518 it('should be invalid if empty', function() {
22520 expect(value.getText()).toEqual('value =');
22521 expect(valid.getText()).toContain('myForm.input.$valid = false');
22524 it('should be invalid if over max', function() {
22525 setInput('2015-W01');
22526 expect(value.getText()).toContain('');
22527 expect(valid.getText()).toContain('myForm.input.$valid = false');
22532 'week': createDateInputType('week', WEEK_REGEXP, weekParser, 'yyyy-Www'),
22536 * @name input[month]
22539 * Input with month validation and transformation. In browsers that do not yet support
22540 * the HTML5 month input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
22541 * month format (yyyy-MM), for example: `2009-01`.
22543 * The model must always be a Date object, otherwise Angular will throw an error.
22544 * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
22545 * If the model is not set to the first of the month, the next view to model update will set it
22546 * to the first of the month.
22548 * The timezone to be used to read/write the `Date` instance in the model can be defined using
22549 * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
22551 * @param {string} ngModel Assignable angular expression to data-bind to.
22552 * @param {string=} name Property name of the form under which the control is published.
22553 * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
22554 * This must be a valid ISO month format (yyyy-MM). You can also use interpolation inside this
22555 * attribute (e.g. `min="{{minMonth | date:'yyyy-MM'}}"`). Note that `min` will also add
22556 * native HTML5 constraint validation.
22557 * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
22558 * This must be a valid ISO month format (yyyy-MM). You can also use interpolation inside this
22559 * attribute (e.g. `max="{{maxMonth | date:'yyyy-MM'}}"`). Note that `max` will also add
22560 * native HTML5 constraint validation.
22561 * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO week string
22562 * the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
22563 * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO week string
22564 * the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
22566 * @param {string=} required Sets `required` validation error key if the value is not entered.
22567 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
22568 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
22569 * `required` when you want to data-bind to the `required` attribute.
22570 * @param {string=} ngChange Angular expression to be executed when input changes due to user
22571 * interaction with the input element.
22574 <example name="month-input-directive" module="monthExample">
22575 <file name="index.html">
22577 angular.module('monthExample', [])
22578 .controller('DateController', ['$scope', function($scope) {
22580 value: new Date(2013, 9, 1)
22584 <form name="myForm" ng-controller="DateController as dateCtrl">
22585 <label for="exampleInput">Pick a month in 2013:</label>
22586 <input id="exampleInput" type="month" name="input" ng-model="example.value"
22587 placeholder="yyyy-MM" min="2013-01" max="2013-12" required />
22589 <span class="error" ng-show="myForm.input.$error.required">
22591 <span class="error" ng-show="myForm.input.$error.month">
22592 Not a valid month!</span>
22594 <tt>value = {{example.value | date: "yyyy-MM"}}</tt><br/>
22595 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
22596 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
22597 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
22598 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
22601 <file name="protractor.js" type="protractor">
22602 var value = element(by.binding('example.value | date: "yyyy-MM"'));
22603 var valid = element(by.binding('myForm.input.$valid'));
22604 var input = element(by.model('example.value'));
22606 // currently protractor/webdriver does not support
22607 // sending keys to all known HTML5 input controls
22608 // for various browsers (https://github.com/angular/protractor/issues/562).
22609 function setInput(val) {
22610 // set the value of the element and force validation.
22611 var scr = "var ipt = document.getElementById('exampleInput'); " +
22612 "ipt.value = '" + val + "';" +
22613 "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
22614 browser.executeScript(scr);
22617 it('should initialize to model', function() {
22618 expect(value.getText()).toContain('2013-10');
22619 expect(valid.getText()).toContain('myForm.input.$valid = true');
22622 it('should be invalid if empty', function() {
22624 expect(value.getText()).toEqual('value =');
22625 expect(valid.getText()).toContain('myForm.input.$valid = false');
22628 it('should be invalid if over max', function() {
22629 setInput('2015-01');
22630 expect(value.getText()).toContain('');
22631 expect(valid.getText()).toContain('myForm.input.$valid = false');
22636 'month': createDateInputType('month', MONTH_REGEXP,
22637 createDateParser(MONTH_REGEXP, ['yyyy', 'MM']),
22642 * @name input[number]
22645 * Text input with number validation and transformation. Sets the `number` validation
22646 * error if not a valid number.
22648 * <div class="alert alert-warning">
22649 * The model must always be of type `number` otherwise Angular will throw an error.
22650 * Be aware that a string containing a number is not enough. See the {@link ngModel:numfmt}
22651 * error docs for more information and an example of how to convert your model if necessary.
22654 * ## Issues with HTML5 constraint validation
22656 * In browsers that follow the
22657 * [HTML5 specification](https://html.spec.whatwg.org/multipage/forms.html#number-state-%28type=number%29),
22658 * `input[number]` does not work as expected with {@link ngModelOptions `ngModelOptions.allowInvalid`}.
22659 * If a non-number is entered in the input, the browser will report the value as an empty string,
22660 * which means the view / model values in `ngModel` and subsequently the scope value
22661 * will also be an empty string.
22664 * @param {string} ngModel Assignable angular expression to data-bind to.
22665 * @param {string=} name Property name of the form under which the control is published.
22666 * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
22667 * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
22668 * @param {string=} required Sets `required` validation error key if the value is not entered.
22669 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
22670 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
22671 * `required` when you want to data-bind to the `required` attribute.
22672 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
22674 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
22675 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
22677 * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
22678 * that contains the regular expression body that will be converted to a regular expression
22679 * as in the ngPattern directive.
22680 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
22681 * does not match a RegExp found by evaluating the Angular expression given in the attribute value.
22682 * If the expression evaluates to a RegExp object, then this is used directly.
22683 * If the expression evaluates to a string, then it will be converted to a RegExp
22684 * after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
22685 * `new RegExp('^abc$')`.<br />
22686 * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
22687 * start at the index of the last search's match, thus not taking the whole input value into
22689 * @param {string=} ngChange Angular expression to be executed when input changes due to user
22690 * interaction with the input element.
22693 <example name="number-input-directive" module="numberExample">
22694 <file name="index.html">
22696 angular.module('numberExample', [])
22697 .controller('ExampleController', ['$scope', function($scope) {
22703 <form name="myForm" ng-controller="ExampleController">
22705 <input type="number" name="input" ng-model="example.value"
22706 min="0" max="99" required>
22709 <span class="error" ng-show="myForm.input.$error.required">
22711 <span class="error" ng-show="myForm.input.$error.number">
22712 Not valid number!</span>
22714 <tt>value = {{example.value}}</tt><br/>
22715 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
22716 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
22717 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
22718 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
22721 <file name="protractor.js" type="protractor">
22722 var value = element(by.binding('example.value'));
22723 var valid = element(by.binding('myForm.input.$valid'));
22724 var input = element(by.model('example.value'));
22726 it('should initialize to model', function() {
22727 expect(value.getText()).toContain('12');
22728 expect(valid.getText()).toContain('true');
22731 it('should be invalid if empty', function() {
22733 input.sendKeys('');
22734 expect(value.getText()).toEqual('value =');
22735 expect(valid.getText()).toContain('false');
22738 it('should be invalid if over max', function() {
22740 input.sendKeys('123');
22741 expect(value.getText()).toEqual('value =');
22742 expect(valid.getText()).toContain('false');
22747 'number': numberInputType,
22755 * Text input with URL validation. Sets the `url` validation error key if the content is not a
22758 * <div class="alert alert-warning">
22759 * **Note:** `input[url]` uses a regex to validate urls that is derived from the regex
22760 * used in Chromium. If you need stricter validation, you can use `ng-pattern` or modify
22761 * the built-in validators (see the {@link guide/forms Forms guide})
22764 * @param {string} ngModel Assignable angular expression to data-bind to.
22765 * @param {string=} name Property name of the form under which the control is published.
22766 * @param {string=} required Sets `required` validation error key if the value is not entered.
22767 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
22768 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
22769 * `required` when you want to data-bind to the `required` attribute.
22770 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
22772 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
22773 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
22775 * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
22776 * that contains the regular expression body that will be converted to a regular expression
22777 * as in the ngPattern directive.
22778 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
22779 * does not match a RegExp found by evaluating the Angular expression given in the attribute value.
22780 * If the expression evaluates to a RegExp object, then this is used directly.
22781 * If the expression evaluates to a string, then it will be converted to a RegExp
22782 * after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
22783 * `new RegExp('^abc$')`.<br />
22784 * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
22785 * start at the index of the last search's match, thus not taking the whole input value into
22787 * @param {string=} ngChange Angular expression to be executed when input changes due to user
22788 * interaction with the input element.
22791 <example name="url-input-directive" module="urlExample">
22792 <file name="index.html">
22794 angular.module('urlExample', [])
22795 .controller('ExampleController', ['$scope', function($scope) {
22797 text: 'http://google.com'
22801 <form name="myForm" ng-controller="ExampleController">
22803 <input type="url" name="input" ng-model="url.text" required>
22806 <span class="error" ng-show="myForm.input.$error.required">
22808 <span class="error" ng-show="myForm.input.$error.url">
22809 Not valid url!</span>
22811 <tt>text = {{url.text}}</tt><br/>
22812 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
22813 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
22814 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
22815 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
22816 <tt>myForm.$error.url = {{!!myForm.$error.url}}</tt><br/>
22819 <file name="protractor.js" type="protractor">
22820 var text = element(by.binding('url.text'));
22821 var valid = element(by.binding('myForm.input.$valid'));
22822 var input = element(by.model('url.text'));
22824 it('should initialize to model', function() {
22825 expect(text.getText()).toContain('http://google.com');
22826 expect(valid.getText()).toContain('true');
22829 it('should be invalid if empty', function() {
22831 input.sendKeys('');
22833 expect(text.getText()).toEqual('text =');
22834 expect(valid.getText()).toContain('false');
22837 it('should be invalid if not url', function() {
22839 input.sendKeys('box');
22841 expect(valid.getText()).toContain('false');
22846 'url': urlInputType,
22851 * @name input[email]
22854 * Text input with email validation. Sets the `email` validation error key if not a valid email
22857 * <div class="alert alert-warning">
22858 * **Note:** `input[email]` uses a regex to validate email addresses that is derived from the regex
22859 * used in Chromium. If you need stricter validation (e.g. requiring a top-level domain), you can
22860 * use `ng-pattern` or modify the built-in validators (see the {@link guide/forms Forms guide})
22863 * @param {string} ngModel Assignable angular expression to data-bind to.
22864 * @param {string=} name Property name of the form under which the control is published.
22865 * @param {string=} required Sets `required` validation error key if the value is not entered.
22866 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
22867 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
22868 * `required` when you want to data-bind to the `required` attribute.
22869 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
22871 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
22872 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
22874 * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
22875 * that contains the regular expression body that will be converted to a regular expression
22876 * as in the ngPattern directive.
22877 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
22878 * does not match a RegExp found by evaluating the Angular expression given in the attribute value.
22879 * If the expression evaluates to a RegExp object, then this is used directly.
22880 * If the expression evaluates to a string, then it will be converted to a RegExp
22881 * after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
22882 * `new RegExp('^abc$')`.<br />
22883 * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
22884 * start at the index of the last search's match, thus not taking the whole input value into
22886 * @param {string=} ngChange Angular expression to be executed when input changes due to user
22887 * interaction with the input element.
22890 <example name="email-input-directive" module="emailExample">
22891 <file name="index.html">
22893 angular.module('emailExample', [])
22894 .controller('ExampleController', ['$scope', function($scope) {
22896 text: 'me@example.com'
22900 <form name="myForm" ng-controller="ExampleController">
22902 <input type="email" name="input" ng-model="email.text" required>
22905 <span class="error" ng-show="myForm.input.$error.required">
22907 <span class="error" ng-show="myForm.input.$error.email">
22908 Not valid email!</span>
22910 <tt>text = {{email.text}}</tt><br/>
22911 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
22912 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
22913 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
22914 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
22915 <tt>myForm.$error.email = {{!!myForm.$error.email}}</tt><br/>
22918 <file name="protractor.js" type="protractor">
22919 var text = element(by.binding('email.text'));
22920 var valid = element(by.binding('myForm.input.$valid'));
22921 var input = element(by.model('email.text'));
22923 it('should initialize to model', function() {
22924 expect(text.getText()).toContain('me@example.com');
22925 expect(valid.getText()).toContain('true');
22928 it('should be invalid if empty', function() {
22930 input.sendKeys('');
22931 expect(text.getText()).toEqual('text =');
22932 expect(valid.getText()).toContain('false');
22935 it('should be invalid if not email', function() {
22937 input.sendKeys('xxx');
22939 expect(valid.getText()).toContain('false');
22944 'email': emailInputType,
22949 * @name input[radio]
22952 * HTML radio button.
22954 * @param {string} ngModel Assignable angular expression to data-bind to.
22955 * @param {string} value The value to which the `ngModel` expression should be set when selected.
22956 * Note that `value` only supports `string` values, i.e. the scope model needs to be a string,
22957 * too. Use `ngValue` if you need complex models (`number`, `object`, ...).
22958 * @param {string=} name Property name of the form under which the control is published.
22959 * @param {string=} ngChange Angular expression to be executed when input changes due to user
22960 * interaction with the input element.
22961 * @param {string} ngValue Angular expression to which `ngModel` will be be set when the radio
22962 * is selected. Should be used instead of the `value` attribute if you need
22963 * a non-string `ngModel` (`boolean`, `array`, ...).
22966 <example name="radio-input-directive" module="radioExample">
22967 <file name="index.html">
22969 angular.module('radioExample', [])
22970 .controller('ExampleController', ['$scope', function($scope) {
22974 $scope.specialValue = {
22980 <form name="myForm" ng-controller="ExampleController">
22982 <input type="radio" ng-model="color.name" value="red">
22986 <input type="radio" ng-model="color.name" ng-value="specialValue">
22990 <input type="radio" ng-model="color.name" value="blue">
22993 <tt>color = {{color.name | json}}</tt><br/>
22995 Note that `ng-value="specialValue"` sets radio item's value to be the value of `$scope.specialValue`.
22997 <file name="protractor.js" type="protractor">
22998 it('should change state', function() {
22999 var color = element(by.binding('color.name'));
23001 expect(color.getText()).toContain('blue');
23003 element.all(by.model('color.name')).get(0).click();
23005 expect(color.getText()).toContain('red');
23010 'radio': radioInputType,
23015 * @name input[checkbox]
23020 * @param {string} ngModel Assignable angular expression to data-bind to.
23021 * @param {string=} name Property name of the form under which the control is published.
23022 * @param {expression=} ngTrueValue The value to which the expression should be set when selected.
23023 * @param {expression=} ngFalseValue The value to which the expression should be set when not selected.
23024 * @param {string=} ngChange Angular expression to be executed when input changes due to user
23025 * interaction with the input element.
23028 <example name="checkbox-input-directive" module="checkboxExample">
23029 <file name="index.html">
23031 angular.module('checkboxExample', [])
23032 .controller('ExampleController', ['$scope', function($scope) {
23033 $scope.checkboxModel = {
23039 <form name="myForm" ng-controller="ExampleController">
23041 <input type="checkbox" ng-model="checkboxModel.value1">
23044 <input type="checkbox" ng-model="checkboxModel.value2"
23045 ng-true-value="'YES'" ng-false-value="'NO'">
23047 <tt>value1 = {{checkboxModel.value1}}</tt><br/>
23048 <tt>value2 = {{checkboxModel.value2}}</tt><br/>
23051 <file name="protractor.js" type="protractor">
23052 it('should change state', function() {
23053 var value1 = element(by.binding('checkboxModel.value1'));
23054 var value2 = element(by.binding('checkboxModel.value2'));
23056 expect(value1.getText()).toContain('true');
23057 expect(value2.getText()).toContain('YES');
23059 element(by.model('checkboxModel.value1')).click();
23060 element(by.model('checkboxModel.value2')).click();
23062 expect(value1.getText()).toContain('false');
23063 expect(value2.getText()).toContain('NO');
23068 'checkbox': checkboxInputType,
23077 function stringBasedInputType(ctrl) {
23078 ctrl.$formatters.push(function(value) {
23079 return ctrl.$isEmpty(value) ? value : value.toString();
23083 function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
23084 baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
23085 stringBasedInputType(ctrl);
23088 function baseInputType(scope, element, attr, ctrl, $sniffer, $browser) {
23089 var type = lowercase(element[0].type);
23091 // In composition mode, users are still inputing intermediate text buffer,
23092 // hold the listener until composition is done.
23093 // More about composition events: https://developer.mozilla.org/en-US/docs/Web/API/CompositionEvent
23094 if (!$sniffer.android) {
23095 var composing = false;
23097 element.on('compositionstart', function() {
23101 element.on('compositionend', function() {
23109 var listener = function(ev) {
23111 $browser.defer.cancel(timeout);
23114 if (composing) return;
23115 var value = element.val(),
23116 event = ev && ev.type;
23118 // By default we will trim the value
23119 // If the attribute ng-trim exists we will avoid trimming
23120 // If input type is 'password', the value is never trimmed
23121 if (type !== 'password' && (!attr.ngTrim || attr.ngTrim !== 'false')) {
23122 value = trim(value);
23125 // If a control is suffering from bad input (due to native validators), browsers discard its
23126 // value, so it may be necessary to revalidate (by calling $setViewValue again) even if the
23127 // control's value is the same empty value twice in a row.
23128 if (ctrl.$viewValue !== value || (value === '' && ctrl.$$hasNativeValidators)) {
23129 ctrl.$setViewValue(value, event);
23133 // if the browser does support "input" event, we are fine - except on IE9 which doesn't fire the
23134 // input event on backspace, delete or cut
23135 if ($sniffer.hasEvent('input')) {
23136 element.on('input', listener);
23138 var deferListener = function(ev, input, origValue) {
23140 timeout = $browser.defer(function() {
23142 if (!input || input.value !== origValue) {
23149 element.on('keydown', function(event) {
23150 var key = event.keyCode;
23153 // command modifiers arrows
23154 if (key === 91 || (15 < key && key < 19) || (37 <= key && key <= 40)) return;
23156 deferListener(event, this, this.value);
23159 // if user modifies input value using context menu in IE, we need "paste" and "cut" events to catch it
23160 if ($sniffer.hasEvent('paste')) {
23161 element.on('paste cut', deferListener);
23165 // if user paste into input using mouse on older browser
23166 // or form autocomplete on newer browser, we need "change" event to catch it
23167 element.on('change', listener);
23169 // Some native input types (date-family) have the ability to change validity without
23170 // firing any input/change events.
23171 // For these event types, when native validators are present and the browser supports the type,
23172 // check for validity changes on various DOM events.
23173 if (PARTIAL_VALIDATION_TYPES[type] && ctrl.$$hasNativeValidators && type === attr.type) {
23174 element.on(PARTIAL_VALIDATION_EVENTS, function(ev) {
23176 var validity = this[VALIDITY_STATE_PROPERTY];
23177 var origBadInput = validity.badInput;
23178 var origTypeMismatch = validity.typeMismatch;
23179 timeout = $browser.defer(function() {
23181 if (validity.badInput !== origBadInput || validity.typeMismatch !== origTypeMismatch) {
23189 ctrl.$render = function() {
23190 // Workaround for Firefox validation #12102.
23191 var value = ctrl.$isEmpty(ctrl.$viewValue) ? '' : ctrl.$viewValue;
23192 if (element.val() !== value) {
23193 element.val(value);
23198 function weekParser(isoWeek, existingDate) {
23199 if (isDate(isoWeek)) {
23203 if (isString(isoWeek)) {
23204 WEEK_REGEXP.lastIndex = 0;
23205 var parts = WEEK_REGEXP.exec(isoWeek);
23207 var year = +parts[1],
23213 firstThurs = getFirstThursdayOfYear(year),
23214 addDays = (week - 1) * 7;
23216 if (existingDate) {
23217 hours = existingDate.getHours();
23218 minutes = existingDate.getMinutes();
23219 seconds = existingDate.getSeconds();
23220 milliseconds = existingDate.getMilliseconds();
23223 return new Date(year, 0, firstThurs.getDate() + addDays, hours, minutes, seconds, milliseconds);
23230 function createDateParser(regexp, mapping) {
23231 return function(iso, date) {
23238 if (isString(iso)) {
23239 // When a date is JSON'ified to wraps itself inside of an extra
23240 // set of double quotes. This makes the date parsing code unable
23241 // to match the date string and parse it as a date.
23242 if (iso.charAt(0) == '"' && iso.charAt(iso.length - 1) == '"') {
23243 iso = iso.substring(1, iso.length - 1);
23245 if (ISO_DATE_REGEXP.test(iso)) {
23246 return new Date(iso);
23248 regexp.lastIndex = 0;
23249 parts = regexp.exec(iso);
23255 yyyy: date.getFullYear(),
23256 MM: date.getMonth() + 1,
23257 dd: date.getDate(),
23258 HH: date.getHours(),
23259 mm: date.getMinutes(),
23260 ss: date.getSeconds(),
23261 sss: date.getMilliseconds() / 1000
23264 map = { yyyy: 1970, MM: 1, dd: 1, HH: 0, mm: 0, ss: 0, sss: 0 };
23267 forEach(parts, function(part, index) {
23268 if (index < mapping.length) {
23269 map[mapping[index]] = +part;
23272 return new Date(map.yyyy, map.MM - 1, map.dd, map.HH, map.mm, map.ss || 0, map.sss * 1000 || 0);
23280 function createDateInputType(type, regexp, parseDate, format) {
23281 return function dynamicDateInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter) {
23282 badInputChecker(scope, element, attr, ctrl);
23283 baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
23284 var timezone = ctrl && ctrl.$options && ctrl.$options.timezone;
23287 ctrl.$$parserName = type;
23288 ctrl.$parsers.push(function(value) {
23289 if (ctrl.$isEmpty(value)) return null;
23290 if (regexp.test(value)) {
23291 // Note: We cannot read ctrl.$modelValue, as there might be a different
23292 // parser/formatter in the processing chain so that the model
23293 // contains some different data format!
23294 var parsedDate = parseDate(value, previousDate);
23296 parsedDate = convertTimezoneToLocal(parsedDate, timezone);
23303 ctrl.$formatters.push(function(value) {
23304 if (value && !isDate(value)) {
23305 throw ngModelMinErr('datefmt', 'Expected `{0}` to be a date', value);
23307 if (isValidDate(value)) {
23308 previousDate = value;
23309 if (previousDate && timezone) {
23310 previousDate = convertTimezoneToLocal(previousDate, timezone, true);
23312 return $filter('date')(value, format, timezone);
23314 previousDate = null;
23319 if (isDefined(attr.min) || attr.ngMin) {
23321 ctrl.$validators.min = function(value) {
23322 return !isValidDate(value) || isUndefined(minVal) || parseDate(value) >= minVal;
23324 attr.$observe('min', function(val) {
23325 minVal = parseObservedDateValue(val);
23330 if (isDefined(attr.max) || attr.ngMax) {
23332 ctrl.$validators.max = function(value) {
23333 return !isValidDate(value) || isUndefined(maxVal) || parseDate(value) <= maxVal;
23335 attr.$observe('max', function(val) {
23336 maxVal = parseObservedDateValue(val);
23341 function isValidDate(value) {
23342 // Invalid Date: getTime() returns NaN
23343 return value && !(value.getTime && value.getTime() !== value.getTime());
23346 function parseObservedDateValue(val) {
23347 return isDefined(val) && !isDate(val) ? parseDate(val) || undefined : val;
23352 function badInputChecker(scope, element, attr, ctrl) {
23353 var node = element[0];
23354 var nativeValidation = ctrl.$$hasNativeValidators = isObject(node.validity);
23355 if (nativeValidation) {
23356 ctrl.$parsers.push(function(value) {
23357 var validity = element.prop(VALIDITY_STATE_PROPERTY) || {};
23358 return validity.badInput || validity.typeMismatch ? undefined : value;
23363 function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) {
23364 badInputChecker(scope, element, attr, ctrl);
23365 baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
23367 ctrl.$$parserName = 'number';
23368 ctrl.$parsers.push(function(value) {
23369 if (ctrl.$isEmpty(value)) return null;
23370 if (NUMBER_REGEXP.test(value)) return parseFloat(value);
23374 ctrl.$formatters.push(function(value) {
23375 if (!ctrl.$isEmpty(value)) {
23376 if (!isNumber(value)) {
23377 throw ngModelMinErr('numfmt', 'Expected `{0}` to be a number', value);
23379 value = value.toString();
23384 if (isDefined(attr.min) || attr.ngMin) {
23386 ctrl.$validators.min = function(value) {
23387 return ctrl.$isEmpty(value) || isUndefined(minVal) || value >= minVal;
23390 attr.$observe('min', function(val) {
23391 if (isDefined(val) && !isNumber(val)) {
23392 val = parseFloat(val, 10);
23394 minVal = isNumber(val) && !isNaN(val) ? val : undefined;
23395 // TODO(matsko): implement validateLater to reduce number of validations
23400 if (isDefined(attr.max) || attr.ngMax) {
23402 ctrl.$validators.max = function(value) {
23403 return ctrl.$isEmpty(value) || isUndefined(maxVal) || value <= maxVal;
23406 attr.$observe('max', function(val) {
23407 if (isDefined(val) && !isNumber(val)) {
23408 val = parseFloat(val, 10);
23410 maxVal = isNumber(val) && !isNaN(val) ? val : undefined;
23411 // TODO(matsko): implement validateLater to reduce number of validations
23417 function urlInputType(scope, element, attr, ctrl, $sniffer, $browser) {
23418 // Note: no badInputChecker here by purpose as `url` is only a validation
23419 // in browsers, i.e. we can always read out input.value even if it is not valid!
23420 baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
23421 stringBasedInputType(ctrl);
23423 ctrl.$$parserName = 'url';
23424 ctrl.$validators.url = function(modelValue, viewValue) {
23425 var value = modelValue || viewValue;
23426 return ctrl.$isEmpty(value) || URL_REGEXP.test(value);
23430 function emailInputType(scope, element, attr, ctrl, $sniffer, $browser) {
23431 // Note: no badInputChecker here by purpose as `url` is only a validation
23432 // in browsers, i.e. we can always read out input.value even if it is not valid!
23433 baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
23434 stringBasedInputType(ctrl);
23436 ctrl.$$parserName = 'email';
23437 ctrl.$validators.email = function(modelValue, viewValue) {
23438 var value = modelValue || viewValue;
23439 return ctrl.$isEmpty(value) || EMAIL_REGEXP.test(value);
23443 function radioInputType(scope, element, attr, ctrl) {
23444 // make the name unique, if not defined
23445 if (isUndefined(attr.name)) {
23446 element.attr('name', nextUid());
23449 var listener = function(ev) {
23450 if (element[0].checked) {
23451 ctrl.$setViewValue(attr.value, ev && ev.type);
23455 element.on('click', listener);
23457 ctrl.$render = function() {
23458 var value = attr.value;
23459 element[0].checked = (value == ctrl.$viewValue);
23462 attr.$observe('value', ctrl.$render);
23465 function parseConstantExpr($parse, context, name, expression, fallback) {
23467 if (isDefined(expression)) {
23468 parseFn = $parse(expression);
23469 if (!parseFn.constant) {
23470 throw ngModelMinErr('constexpr', 'Expected constant expression for `{0}`, but saw ' +
23471 '`{1}`.', name, expression);
23473 return parseFn(context);
23478 function checkboxInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter, $parse) {
23479 var trueValue = parseConstantExpr($parse, scope, 'ngTrueValue', attr.ngTrueValue, true);
23480 var falseValue = parseConstantExpr($parse, scope, 'ngFalseValue', attr.ngFalseValue, false);
23482 var listener = function(ev) {
23483 ctrl.$setViewValue(element[0].checked, ev && ev.type);
23486 element.on('click', listener);
23488 ctrl.$render = function() {
23489 element[0].checked = ctrl.$viewValue;
23492 // Override the standard `$isEmpty` because the $viewValue of an empty checkbox is always set to `false`
23493 // This is because of the parser below, which compares the `$modelValue` with `trueValue` to convert
23494 // it to a boolean.
23495 ctrl.$isEmpty = function(value) {
23496 return value === false;
23499 ctrl.$formatters.push(function(value) {
23500 return equals(value, trueValue);
23503 ctrl.$parsers.push(function(value) {
23504 return value ? trueValue : falseValue;
23515 * HTML textarea element control with angular data-binding. The data-binding and validation
23516 * properties of this element are exactly the same as those of the
23517 * {@link ng.directive:input input element}.
23519 * @param {string} ngModel Assignable angular expression to data-bind to.
23520 * @param {string=} name Property name of the form under which the control is published.
23521 * @param {string=} required Sets `required` validation error key if the value is not entered.
23522 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
23523 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
23524 * `required` when you want to data-bind to the `required` attribute.
23525 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
23527 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
23528 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any
23530 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
23531 * does not match a RegExp found by evaluating the Angular expression given in the attribute value.
23532 * If the expression evaluates to a RegExp object, then this is used directly.
23533 * If the expression evaluates to a string, then it will be converted to a RegExp
23534 * after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
23535 * `new RegExp('^abc$')`.<br />
23536 * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
23537 * start at the index of the last search's match, thus not taking the whole input value into
23539 * @param {string=} ngChange Angular expression to be executed when input changes due to user
23540 * interaction with the input element.
23541 * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
23551 * HTML input element control. When used together with {@link ngModel `ngModel`}, it provides data-binding,
23552 * input state control, and validation.
23553 * Input control follows HTML5 input types and polyfills the HTML5 validation behavior for older browsers.
23555 * <div class="alert alert-warning">
23556 * **Note:** Not every feature offered is available for all input types.
23557 * Specifically, data binding and event handling via `ng-model` is unsupported for `input[file]`.
23560 * @param {string} ngModel Assignable angular expression to data-bind to.
23561 * @param {string=} name Property name of the form under which the control is published.
23562 * @param {string=} required Sets `required` validation error key if the value is not entered.
23563 * @param {boolean=} ngRequired Sets `required` attribute if set to true
23564 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
23566 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
23567 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any
23569 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
23570 * value does not match a RegExp found by evaluating the Angular expression given in the attribute value.
23571 * If the expression evaluates to a RegExp object, then this is used directly.
23572 * If the expression evaluates to a string, then it will be converted to a RegExp
23573 * after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
23574 * `new RegExp('^abc$')`.<br />
23575 * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
23576 * start at the index of the last search's match, thus not taking the whole input value into
23578 * @param {string=} ngChange Angular expression to be executed when input changes due to user
23579 * interaction with the input element.
23580 * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
23581 * This parameter is ignored for input[type=password] controls, which will never trim the
23585 <example name="input-directive" module="inputExample">
23586 <file name="index.html">
23588 angular.module('inputExample', [])
23589 .controller('ExampleController', ['$scope', function($scope) {
23590 $scope.user = {name: 'guest', last: 'visitor'};
23593 <div ng-controller="ExampleController">
23594 <form name="myForm">
23597 <input type="text" name="userName" ng-model="user.name" required>
23600 <span class="error" ng-show="myForm.userName.$error.required">
23605 <input type="text" name="lastName" ng-model="user.last"
23606 ng-minlength="3" ng-maxlength="10">
23609 <span class="error" ng-show="myForm.lastName.$error.minlength">
23611 <span class="error" ng-show="myForm.lastName.$error.maxlength">
23616 <tt>user = {{user}}</tt><br/>
23617 <tt>myForm.userName.$valid = {{myForm.userName.$valid}}</tt><br/>
23618 <tt>myForm.userName.$error = {{myForm.userName.$error}}</tt><br/>
23619 <tt>myForm.lastName.$valid = {{myForm.lastName.$valid}}</tt><br/>
23620 <tt>myForm.lastName.$error = {{myForm.lastName.$error}}</tt><br/>
23621 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
23622 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
23623 <tt>myForm.$error.minlength = {{!!myForm.$error.minlength}}</tt><br/>
23624 <tt>myForm.$error.maxlength = {{!!myForm.$error.maxlength}}</tt><br/>
23627 <file name="protractor.js" type="protractor">
23628 var user = element(by.exactBinding('user'));
23629 var userNameValid = element(by.binding('myForm.userName.$valid'));
23630 var lastNameValid = element(by.binding('myForm.lastName.$valid'));
23631 var lastNameError = element(by.binding('myForm.lastName.$error'));
23632 var formValid = element(by.binding('myForm.$valid'));
23633 var userNameInput = element(by.model('user.name'));
23634 var userLastInput = element(by.model('user.last'));
23636 it('should initialize to model', function() {
23637 expect(user.getText()).toContain('{"name":"guest","last":"visitor"}');
23638 expect(userNameValid.getText()).toContain('true');
23639 expect(formValid.getText()).toContain('true');
23642 it('should be invalid if empty when required', function() {
23643 userNameInput.clear();
23644 userNameInput.sendKeys('');
23646 expect(user.getText()).toContain('{"last":"visitor"}');
23647 expect(userNameValid.getText()).toContain('false');
23648 expect(formValid.getText()).toContain('false');
23651 it('should be valid if empty when min length is set', function() {
23652 userLastInput.clear();
23653 userLastInput.sendKeys('');
23655 expect(user.getText()).toContain('{"name":"guest","last":""}');
23656 expect(lastNameValid.getText()).toContain('true');
23657 expect(formValid.getText()).toContain('true');
23660 it('should be invalid if less than required min length', function() {
23661 userLastInput.clear();
23662 userLastInput.sendKeys('xx');
23664 expect(user.getText()).toContain('{"name":"guest"}');
23665 expect(lastNameValid.getText()).toContain('false');
23666 expect(lastNameError.getText()).toContain('minlength');
23667 expect(formValid.getText()).toContain('false');
23670 it('should be invalid if longer than max length', function() {
23671 userLastInput.clear();
23672 userLastInput.sendKeys('some ridiculously long name');
23674 expect(user.getText()).toContain('{"name":"guest"}');
23675 expect(lastNameValid.getText()).toContain('false');
23676 expect(lastNameError.getText()).toContain('maxlength');
23677 expect(formValid.getText()).toContain('false');
23682 var inputDirective = ['$browser', '$sniffer', '$filter', '$parse',
23683 function($browser, $sniffer, $filter, $parse) {
23686 require: ['?ngModel'],
23688 pre: function(scope, element, attr, ctrls) {
23690 (inputType[lowercase(attr.type)] || inputType.text)(scope, element, attr, ctrls[0], $sniffer,
23691 $browser, $filter, $parse);
23700 var CONSTANT_VALUE_REGEXP = /^(true|false|\d+)$/;
23706 * Binds the given expression to the value of `<option>` or {@link input[radio] `input[radio]`},
23707 * so that when the element is selected, the {@link ngModel `ngModel`} of that element is set to
23710 * `ngValue` is useful when dynamically generating lists of radio buttons using
23711 * {@link ngRepeat `ngRepeat`}, as shown below.
23713 * Likewise, `ngValue` can be used to generate `<option>` elements for
23714 * the {@link select `select`} element. In that case however, only strings are supported
23715 * for the `value `attribute, so the resulting `ngModel` will always be a string.
23716 * Support for `select` models with non-string values is available via `ngOptions`.
23719 * @param {string=} ngValue angular expression, whose value will be bound to the `value` attribute
23720 * of the `input` element
23723 <example name="ngValue-directive" module="valueExample">
23724 <file name="index.html">
23726 angular.module('valueExample', [])
23727 .controller('ExampleController', ['$scope', function($scope) {
23728 $scope.names = ['pizza', 'unicorns', 'robots'];
23729 $scope.my = { favorite: 'unicorns' };
23732 <form ng-controller="ExampleController">
23733 <h2>Which is your favorite?</h2>
23734 <label ng-repeat="name in names" for="{{name}}">
23736 <input type="radio"
23737 ng-model="my.favorite"
23742 <div>You chose {{my.favorite}}</div>
23745 <file name="protractor.js" type="protractor">
23746 var favorite = element(by.binding('my.favorite'));
23748 it('should initialize to model', function() {
23749 expect(favorite.getText()).toContain('unicorns');
23751 it('should bind the values to the inputs', function() {
23752 element.all(by.model('my.favorite')).get(0).click();
23753 expect(favorite.getText()).toContain('pizza');
23758 var ngValueDirective = function() {
23762 compile: function(tpl, tplAttr) {
23763 if (CONSTANT_VALUE_REGEXP.test(tplAttr.ngValue)) {
23764 return function ngValueConstantLink(scope, elm, attr) {
23765 attr.$set('value', scope.$eval(attr.ngValue));
23768 return function ngValueLink(scope, elm, attr) {
23769 scope.$watch(attr.ngValue, function valueWatchAction(value) {
23770 attr.$set('value', value);
23784 * The `ngBind` attribute tells Angular to replace the text content of the specified HTML element
23785 * with the value of a given expression, and to update the text content when the value of that
23786 * expression changes.
23788 * Typically, you don't use `ngBind` directly, but instead you use the double curly markup like
23789 * `{{ expression }}` which is similar but less verbose.
23791 * It is preferable to use `ngBind` instead of `{{ expression }}` if a template is momentarily
23792 * displayed by the browser in its raw state before Angular compiles it. Since `ngBind` is an
23793 * element attribute, it makes the bindings invisible to the user while the page is loading.
23795 * An alternative solution to this problem would be using the
23796 * {@link ng.directive:ngCloak ngCloak} directive.
23800 * @param {expression} ngBind {@link guide/expression Expression} to evaluate.
23803 * Enter a name in the Live Preview text box; the greeting below the text box changes instantly.
23804 <example module="bindExample">
23805 <file name="index.html">
23807 angular.module('bindExample', [])
23808 .controller('ExampleController', ['$scope', function($scope) {
23809 $scope.name = 'Whirled';
23812 <div ng-controller="ExampleController">
23813 <label>Enter name: <input type="text" ng-model="name"></label><br>
23814 Hello <span ng-bind="name"></span>!
23817 <file name="protractor.js" type="protractor">
23818 it('should check ng-bind', function() {
23819 var nameInput = element(by.model('name'));
23821 expect(element(by.binding('name')).getText()).toBe('Whirled');
23823 nameInput.sendKeys('world');
23824 expect(element(by.binding('name')).getText()).toBe('world');
23829 var ngBindDirective = ['$compile', function($compile) {
23832 compile: function ngBindCompile(templateElement) {
23833 $compile.$$addBindingClass(templateElement);
23834 return function ngBindLink(scope, element, attr) {
23835 $compile.$$addBindingInfo(element, attr.ngBind);
23836 element = element[0];
23837 scope.$watch(attr.ngBind, function ngBindWatchAction(value) {
23838 element.textContent = isUndefined(value) ? '' : value;
23848 * @name ngBindTemplate
23851 * The `ngBindTemplate` directive specifies that the element
23852 * text content should be replaced with the interpolation of the template
23853 * in the `ngBindTemplate` attribute.
23854 * Unlike `ngBind`, the `ngBindTemplate` can contain multiple `{{` `}}`
23855 * expressions. This directive is needed since some HTML elements
23856 * (such as TITLE and OPTION) cannot contain SPAN elements.
23859 * @param {string} ngBindTemplate template of form
23860 * <tt>{{</tt> <tt>expression</tt> <tt>}}</tt> to eval.
23863 * Try it here: enter text in text box and watch the greeting change.
23864 <example module="bindExample">
23865 <file name="index.html">
23867 angular.module('bindExample', [])
23868 .controller('ExampleController', ['$scope', function($scope) {
23869 $scope.salutation = 'Hello';
23870 $scope.name = 'World';
23873 <div ng-controller="ExampleController">
23874 <label>Salutation: <input type="text" ng-model="salutation"></label><br>
23875 <label>Name: <input type="text" ng-model="name"></label><br>
23876 <pre ng-bind-template="{{salutation}} {{name}}!"></pre>
23879 <file name="protractor.js" type="protractor">
23880 it('should check ng-bind', function() {
23881 var salutationElem = element(by.binding('salutation'));
23882 var salutationInput = element(by.model('salutation'));
23883 var nameInput = element(by.model('name'));
23885 expect(salutationElem.getText()).toBe('Hello World!');
23887 salutationInput.clear();
23888 salutationInput.sendKeys('Greetings');
23890 nameInput.sendKeys('user');
23892 expect(salutationElem.getText()).toBe('Greetings user!');
23897 var ngBindTemplateDirective = ['$interpolate', '$compile', function($interpolate, $compile) {
23899 compile: function ngBindTemplateCompile(templateElement) {
23900 $compile.$$addBindingClass(templateElement);
23901 return function ngBindTemplateLink(scope, element, attr) {
23902 var interpolateFn = $interpolate(element.attr(attr.$attr.ngBindTemplate));
23903 $compile.$$addBindingInfo(element, interpolateFn.expressions);
23904 element = element[0];
23905 attr.$observe('ngBindTemplate', function(value) {
23906 element.textContent = isUndefined(value) ? '' : value;
23919 * Evaluates the expression and inserts the resulting HTML into the element in a secure way. By default,
23920 * the resulting HTML content will be sanitized using the {@link ngSanitize.$sanitize $sanitize} service.
23921 * To utilize this functionality, ensure that `$sanitize` is available, for example, by including {@link
23922 * ngSanitize} in your module's dependencies (not in core Angular). In order to use {@link ngSanitize}
23923 * in your module's dependencies, you need to include "angular-sanitize.js" in your application.
23925 * You may also bypass sanitization for values you know are safe. To do so, bind to
23926 * an explicitly trusted value via {@link ng.$sce#trustAsHtml $sce.trustAsHtml}. See the example
23927 * under {@link ng.$sce#show-me-an-example-using-sce- Strict Contextual Escaping (SCE)}.
23929 * Note: If a `$sanitize` service is unavailable and the bound value isn't explicitly trusted, you
23930 * will have an exception (instead of an exploit.)
23933 * @param {expression} ngBindHtml {@link guide/expression Expression} to evaluate.
23937 <example module="bindHtmlExample" deps="angular-sanitize.js">
23938 <file name="index.html">
23939 <div ng-controller="ExampleController">
23940 <p ng-bind-html="myHTML"></p>
23944 <file name="script.js">
23945 angular.module('bindHtmlExample', ['ngSanitize'])
23946 .controller('ExampleController', ['$scope', function($scope) {
23948 'I am an <code>HTML</code>string with ' +
23949 '<a href="#">links!</a> and other <em>stuff</em>';
23953 <file name="protractor.js" type="protractor">
23954 it('should check ng-bind-html', function() {
23955 expect(element(by.binding('myHTML')).getText()).toBe(
23956 'I am an HTMLstring with links! and other stuff');
23961 var ngBindHtmlDirective = ['$sce', '$parse', '$compile', function($sce, $parse, $compile) {
23964 compile: function ngBindHtmlCompile(tElement, tAttrs) {
23965 var ngBindHtmlGetter = $parse(tAttrs.ngBindHtml);
23966 var ngBindHtmlWatch = $parse(tAttrs.ngBindHtml, function getStringValue(value) {
23967 return (value || '').toString();
23969 $compile.$$addBindingClass(tElement);
23971 return function ngBindHtmlLink(scope, element, attr) {
23972 $compile.$$addBindingInfo(element, attr.ngBindHtml);
23974 scope.$watch(ngBindHtmlWatch, function ngBindHtmlWatchAction() {
23975 // we re-evaluate the expr because we want a TrustedValueHolderType
23976 // for $sce, not a string
23977 element.html($sce.getTrustedHtml(ngBindHtmlGetter(scope)) || '');
23989 * Evaluate the given expression when the user changes the input.
23990 * The expression is evaluated immediately, unlike the JavaScript onchange event
23991 * which only triggers at the end of a change (usually, when the user leaves the
23992 * form element or presses the return key).
23994 * The `ngChange` expression is only evaluated when a change in the input value causes
23995 * a new value to be committed to the model.
23997 * It will not be evaluated:
23998 * * if the value returned from the `$parsers` transformation pipeline has not changed
23999 * * if the input has continued to be invalid since the model will stay `null`
24000 * * if the model is changed programmatically and not by a change to the input value
24003 * Note, this directive requires `ngModel` to be present.
24006 * @param {expression} ngChange {@link guide/expression Expression} to evaluate upon change
24010 * <example name="ngChange-directive" module="changeExample">
24011 * <file name="index.html">
24013 * angular.module('changeExample', [])
24014 * .controller('ExampleController', ['$scope', function($scope) {
24015 * $scope.counter = 0;
24016 * $scope.change = function() {
24017 * $scope.counter++;
24021 * <div ng-controller="ExampleController">
24022 * <input type="checkbox" ng-model="confirmed" ng-change="change()" id="ng-change-example1" />
24023 * <input type="checkbox" ng-model="confirmed" id="ng-change-example2" />
24024 * <label for="ng-change-example2">Confirmed</label><br />
24025 * <tt>debug = {{confirmed}}</tt><br/>
24026 * <tt>counter = {{counter}}</tt><br/>
24029 * <file name="protractor.js" type="protractor">
24030 * var counter = element(by.binding('counter'));
24031 * var debug = element(by.binding('confirmed'));
24033 * it('should evaluate the expression if changing from view', function() {
24034 * expect(counter.getText()).toContain('0');
24036 * element(by.id('ng-change-example1')).click();
24038 * expect(counter.getText()).toContain('1');
24039 * expect(debug.getText()).toContain('true');
24042 * it('should not evaluate the expression if changing from model', function() {
24043 * element(by.id('ng-change-example2')).click();
24045 * expect(counter.getText()).toContain('0');
24046 * expect(debug.getText()).toContain('true');
24051 var ngChangeDirective = valueFn({
24053 require: 'ngModel',
24054 link: function(scope, element, attr, ctrl) {
24055 ctrl.$viewChangeListeners.push(function() {
24056 scope.$eval(attr.ngChange);
24061 function classDirective(name, selector) {
24062 name = 'ngClass' + name;
24063 return ['$animate', function($animate) {
24066 link: function(scope, element, attr) {
24069 scope.$watch(attr[name], ngClassWatchAction, true);
24071 attr.$observe('class', function(value) {
24072 ngClassWatchAction(scope.$eval(attr[name]));
24076 if (name !== 'ngClass') {
24077 scope.$watch('$index', function($index, old$index) {
24078 // jshint bitwise: false
24079 var mod = $index & 1;
24080 if (mod !== (old$index & 1)) {
24081 var classes = arrayClasses(scope.$eval(attr[name]));
24083 addClasses(classes) :
24084 removeClasses(classes);
24089 function addClasses(classes) {
24090 var newClasses = digestClassCounts(classes, 1);
24091 attr.$addClass(newClasses);
24094 function removeClasses(classes) {
24095 var newClasses = digestClassCounts(classes, -1);
24096 attr.$removeClass(newClasses);
24099 function digestClassCounts(classes, count) {
24100 // Use createMap() to prevent class assumptions involving property
24101 // names in Object.prototype
24102 var classCounts = element.data('$classCounts') || createMap();
24103 var classesToUpdate = [];
24104 forEach(classes, function(className) {
24105 if (count > 0 || classCounts[className]) {
24106 classCounts[className] = (classCounts[className] || 0) + count;
24107 if (classCounts[className] === +(count > 0)) {
24108 classesToUpdate.push(className);
24112 element.data('$classCounts', classCounts);
24113 return classesToUpdate.join(' ');
24116 function updateClasses(oldClasses, newClasses) {
24117 var toAdd = arrayDifference(newClasses, oldClasses);
24118 var toRemove = arrayDifference(oldClasses, newClasses);
24119 toAdd = digestClassCounts(toAdd, 1);
24120 toRemove = digestClassCounts(toRemove, -1);
24121 if (toAdd && toAdd.length) {
24122 $animate.addClass(element, toAdd);
24124 if (toRemove && toRemove.length) {
24125 $animate.removeClass(element, toRemove);
24129 function ngClassWatchAction(newVal) {
24130 if (selector === true || scope.$index % 2 === selector) {
24131 var newClasses = arrayClasses(newVal || []);
24133 addClasses(newClasses);
24134 } else if (!equals(newVal,oldVal)) {
24135 var oldClasses = arrayClasses(oldVal);
24136 updateClasses(oldClasses, newClasses);
24139 if (isArray(newVal)) {
24140 oldVal = newVal.map(function(v) { return shallowCopy(v); });
24142 oldVal = shallowCopy(newVal);
24148 function arrayDifference(tokens1, tokens2) {
24152 for (var i = 0; i < tokens1.length; i++) {
24153 var token = tokens1[i];
24154 for (var j = 0; j < tokens2.length; j++) {
24155 if (token == tokens2[j]) continue outer;
24157 values.push(token);
24162 function arrayClasses(classVal) {
24164 if (isArray(classVal)) {
24165 forEach(classVal, function(v) {
24166 classes = classes.concat(arrayClasses(v));
24169 } else if (isString(classVal)) {
24170 return classVal.split(' ');
24171 } else if (isObject(classVal)) {
24172 forEach(classVal, function(v, k) {
24174 classes = classes.concat(k.split(' '));
24190 * The `ngClass` directive allows you to dynamically set CSS classes on an HTML element by databinding
24191 * an expression that represents all classes to be added.
24193 * The directive operates in three different ways, depending on which of three types the expression
24196 * 1. If the expression evaluates to a string, the string should be one or more space-delimited class
24199 * 2. If the expression evaluates to an object, then for each key-value pair of the
24200 * object with a truthy value the corresponding key is used as a class name.
24202 * 3. If the expression evaluates to an array, each element of the array should either be a string as in
24203 * type 1 or an object as in type 2. This means that you can mix strings and objects together in an array
24204 * to give you more control over what CSS classes appear. See the code below for an example of this.
24207 * The directive won't add duplicate classes if a particular class was already set.
24209 * When the expression changes, the previously added classes are removed and only then are the
24210 * new classes added.
24213 * | Animation | Occurs |
24214 * |----------------------------------|-------------------------------------|
24215 * | {@link ng.$animate#addClass addClass} | just before the class is applied to the element |
24216 * | {@link ng.$animate#removeClass removeClass} | just before the class is removed from the element |
24219 * @param {expression} ngClass {@link guide/expression Expression} to eval. The result
24220 * of the evaluation can be a string representing space delimited class
24221 * names, an array, or a map of class names to boolean values. In the case of a map, the
24222 * names of the properties whose values are truthy will be added as css classes to the
24225 * @example Example that demonstrates basic bindings via ngClass directive.
24227 <file name="index.html">
24228 <p ng-class="{strike: deleted, bold: important, 'has-error': error}">Map Syntax Example</p>
24230 <input type="checkbox" ng-model="deleted">
24231 deleted (apply "strike" class)
24234 <input type="checkbox" ng-model="important">
24235 important (apply "bold" class)
24238 <input type="checkbox" ng-model="error">
24239 error (apply "has-error" class)
24242 <p ng-class="style">Using String Syntax</p>
24243 <input type="text" ng-model="style"
24244 placeholder="Type: bold strike red" aria-label="Type: bold strike red">
24246 <p ng-class="[style1, style2, style3]">Using Array Syntax</p>
24247 <input ng-model="style1"
24248 placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red"><br>
24249 <input ng-model="style2"
24250 placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red 2"><br>
24251 <input ng-model="style3"
24252 placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red 3"><br>
24254 <p ng-class="[style4, {orange: warning}]">Using Array and Map Syntax</p>
24255 <input ng-model="style4" placeholder="Type: bold, strike" aria-label="Type: bold, strike"><br>
24256 <label><input type="checkbox" ng-model="warning"> warning (apply "orange" class)</label>
24258 <file name="style.css">
24260 text-decoration: line-through;
24270 background-color: yellow;
24276 <file name="protractor.js" type="protractor">
24277 var ps = element.all(by.css('p'));
24279 it('should let you toggle the class', function() {
24281 expect(ps.first().getAttribute('class')).not.toMatch(/bold/);
24282 expect(ps.first().getAttribute('class')).not.toMatch(/has-error/);
24284 element(by.model('important')).click();
24285 expect(ps.first().getAttribute('class')).toMatch(/bold/);
24287 element(by.model('error')).click();
24288 expect(ps.first().getAttribute('class')).toMatch(/has-error/);
24291 it('should let you toggle string example', function() {
24292 expect(ps.get(1).getAttribute('class')).toBe('');
24293 element(by.model('style')).clear();
24294 element(by.model('style')).sendKeys('red');
24295 expect(ps.get(1).getAttribute('class')).toBe('red');
24298 it('array example should have 3 classes', function() {
24299 expect(ps.get(2).getAttribute('class')).toBe('');
24300 element(by.model('style1')).sendKeys('bold');
24301 element(by.model('style2')).sendKeys('strike');
24302 element(by.model('style3')).sendKeys('red');
24303 expect(ps.get(2).getAttribute('class')).toBe('bold strike red');
24306 it('array with map example should have 2 classes', function() {
24307 expect(ps.last().getAttribute('class')).toBe('');
24308 element(by.model('style4')).sendKeys('bold');
24309 element(by.model('warning')).click();
24310 expect(ps.last().getAttribute('class')).toBe('bold orange');
24317 The example below demonstrates how to perform animations using ngClass.
24319 <example module="ngAnimate" deps="angular-animate.js" animations="true">
24320 <file name="index.html">
24321 <input id="setbtn" type="button" value="set" ng-click="myVar='my-class'">
24322 <input id="clearbtn" type="button" value="clear" ng-click="myVar=''">
24324 <span class="base-class" ng-class="myVar">Sample Text</span>
24326 <file name="style.css">
24328 transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
24331 .base-class.my-class {
24336 <file name="protractor.js" type="protractor">
24337 it('should check ng-class', function() {
24338 expect(element(by.css('.base-class')).getAttribute('class')).not.
24339 toMatch(/my-class/);
24341 element(by.id('setbtn')).click();
24343 expect(element(by.css('.base-class')).getAttribute('class')).
24344 toMatch(/my-class/);
24346 element(by.id('clearbtn')).click();
24348 expect(element(by.css('.base-class')).getAttribute('class')).not.
24349 toMatch(/my-class/);
24355 ## ngClass and pre-existing CSS3 Transitions/Animations
24356 The ngClass directive still supports CSS3 Transitions/Animations even if they do not follow the ngAnimate CSS naming structure.
24357 Upon animation ngAnimate will apply supplementary CSS classes to track the start and end of an animation, but this will not hinder
24358 any pre-existing CSS transitions already on the element. To get an idea of what happens during a class-based animation, be sure
24359 to view the step by step details of {@link $animate#addClass $animate.addClass} and
24360 {@link $animate#removeClass $animate.removeClass}.
24362 var ngClassDirective = classDirective('', true);
24370 * The `ngClassOdd` and `ngClassEven` directives work exactly as
24371 * {@link ng.directive:ngClass ngClass}, except they work in
24372 * conjunction with `ngRepeat` and take effect only on odd (even) rows.
24374 * This directive can be applied only within the scope of an
24375 * {@link ng.directive:ngRepeat ngRepeat}.
24378 * @param {expression} ngClassOdd {@link guide/expression Expression} to eval. The result
24379 * of the evaluation can be a string representing space delimited class names or an array.
24383 <file name="index.html">
24384 <ol ng-init="names=['John', 'Mary', 'Cate', 'Suz']">
24385 <li ng-repeat="name in names">
24386 <span ng-class-odd="'odd'" ng-class-even="'even'">
24392 <file name="style.css">
24400 <file name="protractor.js" type="protractor">
24401 it('should check ng-class-odd and ng-class-even', function() {
24402 expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')).
24404 expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')).
24410 var ngClassOddDirective = classDirective('Odd', 0);
24414 * @name ngClassEven
24418 * The `ngClassOdd` and `ngClassEven` directives work exactly as
24419 * {@link ng.directive:ngClass ngClass}, except they work in
24420 * conjunction with `ngRepeat` and take effect only on odd (even) rows.
24422 * This directive can be applied only within the scope of an
24423 * {@link ng.directive:ngRepeat ngRepeat}.
24426 * @param {expression} ngClassEven {@link guide/expression Expression} to eval. The
24427 * result of the evaluation can be a string representing space delimited class names or an array.
24431 <file name="index.html">
24432 <ol ng-init="names=['John', 'Mary', 'Cate', 'Suz']">
24433 <li ng-repeat="name in names">
24434 <span ng-class-odd="'odd'" ng-class-even="'even'">
24435 {{name}}
24440 <file name="style.css">
24448 <file name="protractor.js" type="protractor">
24449 it('should check ng-class-odd and ng-class-even', function() {
24450 expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')).
24452 expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')).
24458 var ngClassEvenDirective = classDirective('Even', 1);
24466 * The `ngCloak` directive is used to prevent the Angular html template from being briefly
24467 * displayed by the browser in its raw (uncompiled) form while your application is loading. Use this
24468 * directive to avoid the undesirable flicker effect caused by the html template display.
24470 * The directive can be applied to the `<body>` element, but the preferred usage is to apply
24471 * multiple `ngCloak` directives to small portions of the page to permit progressive rendering
24472 * of the browser view.
24474 * `ngCloak` works in cooperation with the following css rule embedded within `angular.js` and
24475 * `angular.min.js`.
24476 * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
24479 * [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
24480 * display: none !important;
24484 * When this css rule is loaded by the browser, all html elements (including their children) that
24485 * are tagged with the `ngCloak` directive are hidden. When Angular encounters this directive
24486 * during the compilation of the template it deletes the `ngCloak` element attribute, making
24487 * the compiled element visible.
24489 * For the best result, the `angular.js` script must be loaded in the head section of the html
24490 * document; alternatively, the css rule above must be included in the external stylesheet of the
24497 <file name="index.html">
24498 <div id="template1" ng-cloak>{{ 'hello' }}</div>
24499 <div id="template2" class="ng-cloak">{{ 'world' }}</div>
24501 <file name="protractor.js" type="protractor">
24502 it('should remove the template directive and css class', function() {
24503 expect($('#template1').getAttribute('ng-cloak')).
24505 expect($('#template2').getAttribute('ng-cloak')).
24512 var ngCloakDirective = ngDirective({
24513 compile: function(element, attr) {
24514 attr.$set('ngCloak', undefined);
24515 element.removeClass('ng-cloak');
24521 * @name ngController
24524 * The `ngController` directive attaches a controller class to the view. This is a key aspect of how angular
24525 * supports the principles behind the Model-View-Controller design pattern.
24527 * MVC components in angular:
24529 * * Model — Models are the properties of a scope; scopes are attached to the DOM where scope properties
24530 * are accessed through bindings.
24531 * * View — The template (HTML with data bindings) that is rendered into the View.
24532 * * Controller — The `ngController` directive specifies a Controller class; the class contains business
24533 * logic behind the application to decorate the scope with functions and values
24535 * Note that you can also attach controllers to the DOM by declaring it in a route definition
24536 * via the {@link ngRoute.$route $route} service. A common mistake is to declare the controller
24537 * again using `ng-controller` in the template itself. This will cause the controller to be attached
24538 * and executed twice.
24543 * @param {expression} ngController Name of a constructor function registered with the current
24544 * {@link ng.$controllerProvider $controllerProvider} or an {@link guide/expression expression}
24545 * that on the current scope evaluates to a constructor function.
24547 * The controller instance can be published into a scope property by specifying
24548 * `ng-controller="as propertyName"`.
24550 * If the current `$controllerProvider` is configured to use globals (via
24551 * {@link ng.$controllerProvider#allowGlobals `$controllerProvider.allowGlobals()` }), this may
24552 * also be the name of a globally accessible constructor function (not recommended).
24555 * Here is a simple form for editing user contact information. Adding, removing, clearing, and
24556 * greeting are methods declared on the controller (see source tab). These methods can
24557 * easily be called from the angular markup. Any changes to the data are automatically reflected
24558 * in the View without the need for a manual update.
24560 * Two different declaration styles are included below:
24562 * * one binds methods and properties directly onto the controller using `this`:
24563 * `ng-controller="SettingsController1 as settings"`
24564 * * one injects `$scope` into the controller:
24565 * `ng-controller="SettingsController2"`
24567 * The second option is more common in the Angular community, and is generally used in boilerplates
24568 * and in this guide. However, there are advantages to binding properties directly to the controller
24569 * and avoiding scope.
24571 * * Using `controller as` makes it obvious which controller you are accessing in the template when
24572 * multiple controllers apply to an element.
24573 * * If you are writing your controllers as classes you have easier access to the properties and
24574 * methods, which will appear on the scope, from inside the controller code.
24575 * * Since there is always a `.` in the bindings, you don't have to worry about prototypal
24576 * inheritance masking primitives.
24578 * This example demonstrates the `controller as` syntax.
24580 * <example name="ngControllerAs" module="controllerAsExample">
24581 * <file name="index.html">
24582 * <div id="ctrl-as-exmpl" ng-controller="SettingsController1 as settings">
24583 * <label>Name: <input type="text" ng-model="settings.name"/></label>
24584 * <button ng-click="settings.greet()">greet</button><br/>
24587 * <li ng-repeat="contact in settings.contacts">
24588 * <select ng-model="contact.type" aria-label="Contact method" id="select_{{$index}}">
24589 * <option>phone</option>
24590 * <option>email</option>
24592 * <input type="text" ng-model="contact.value" aria-labelledby="select_{{$index}}" />
24593 * <button ng-click="settings.clearContact(contact)">clear</button>
24594 * <button ng-click="settings.removeContact(contact)" aria-label="Remove">X</button>
24596 * <li><button ng-click="settings.addContact()">add</button></li>
24600 * <file name="app.js">
24601 * angular.module('controllerAsExample', [])
24602 * .controller('SettingsController1', SettingsController1);
24604 * function SettingsController1() {
24605 * this.name = "John Smith";
24606 * this.contacts = [
24607 * {type: 'phone', value: '408 555 1212'},
24608 * {type: 'email', value: 'john.smith@example.org'} ];
24611 * SettingsController1.prototype.greet = function() {
24612 * alert(this.name);
24615 * SettingsController1.prototype.addContact = function() {
24616 * this.contacts.push({type: 'email', value: 'yourname@example.org'});
24619 * SettingsController1.prototype.removeContact = function(contactToRemove) {
24620 * var index = this.contacts.indexOf(contactToRemove);
24621 * this.contacts.splice(index, 1);
24624 * SettingsController1.prototype.clearContact = function(contact) {
24625 * contact.type = 'phone';
24626 * contact.value = '';
24629 * <file name="protractor.js" type="protractor">
24630 * it('should check controller as', function() {
24631 * var container = element(by.id('ctrl-as-exmpl'));
24632 * expect(container.element(by.model('settings.name'))
24633 * .getAttribute('value')).toBe('John Smith');
24635 * var firstRepeat =
24636 * container.element(by.repeater('contact in settings.contacts').row(0));
24637 * var secondRepeat =
24638 * container.element(by.repeater('contact in settings.contacts').row(1));
24640 * expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
24641 * .toBe('408 555 1212');
24643 * expect(secondRepeat.element(by.model('contact.value')).getAttribute('value'))
24644 * .toBe('john.smith@example.org');
24646 * firstRepeat.element(by.buttonText('clear')).click();
24648 * expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
24651 * container.element(by.buttonText('add')).click();
24653 * expect(container.element(by.repeater('contact in settings.contacts').row(2))
24654 * .element(by.model('contact.value'))
24655 * .getAttribute('value'))
24656 * .toBe('yourname@example.org');
24661 * This example demonstrates the "attach to `$scope`" style of controller.
24663 * <example name="ngController" module="controllerExample">
24664 * <file name="index.html">
24665 * <div id="ctrl-exmpl" ng-controller="SettingsController2">
24666 * <label>Name: <input type="text" ng-model="name"/></label>
24667 * <button ng-click="greet()">greet</button><br/>
24670 * <li ng-repeat="contact in contacts">
24671 * <select ng-model="contact.type" id="select_{{$index}}">
24672 * <option>phone</option>
24673 * <option>email</option>
24675 * <input type="text" ng-model="contact.value" aria-labelledby="select_{{$index}}" />
24676 * <button ng-click="clearContact(contact)">clear</button>
24677 * <button ng-click="removeContact(contact)">X</button>
24679 * <li>[ <button ng-click="addContact()">add</button> ]</li>
24683 * <file name="app.js">
24684 * angular.module('controllerExample', [])
24685 * .controller('SettingsController2', ['$scope', SettingsController2]);
24687 * function SettingsController2($scope) {
24688 * $scope.name = "John Smith";
24689 * $scope.contacts = [
24690 * {type:'phone', value:'408 555 1212'},
24691 * {type:'email', value:'john.smith@example.org'} ];
24693 * $scope.greet = function() {
24694 * alert($scope.name);
24697 * $scope.addContact = function() {
24698 * $scope.contacts.push({type:'email', value:'yourname@example.org'});
24701 * $scope.removeContact = function(contactToRemove) {
24702 * var index = $scope.contacts.indexOf(contactToRemove);
24703 * $scope.contacts.splice(index, 1);
24706 * $scope.clearContact = function(contact) {
24707 * contact.type = 'phone';
24708 * contact.value = '';
24712 * <file name="protractor.js" type="protractor">
24713 * it('should check controller', function() {
24714 * var container = element(by.id('ctrl-exmpl'));
24716 * expect(container.element(by.model('name'))
24717 * .getAttribute('value')).toBe('John Smith');
24719 * var firstRepeat =
24720 * container.element(by.repeater('contact in contacts').row(0));
24721 * var secondRepeat =
24722 * container.element(by.repeater('contact in contacts').row(1));
24724 * expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
24725 * .toBe('408 555 1212');
24726 * expect(secondRepeat.element(by.model('contact.value')).getAttribute('value'))
24727 * .toBe('john.smith@example.org');
24729 * firstRepeat.element(by.buttonText('clear')).click();
24731 * expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
24734 * container.element(by.buttonText('add')).click();
24736 * expect(container.element(by.repeater('contact in contacts').row(2))
24737 * .element(by.model('contact.value'))
24738 * .getAttribute('value'))
24739 * .toBe('yourname@example.org');
24745 var ngControllerDirective = [function() {
24761 * Angular has some features that can break certain
24762 * [CSP (Content Security Policy)](https://developer.mozilla.org/en/Security/CSP) rules.
24764 * If you intend to implement these rules then you must tell Angular not to use these features.
24766 * This is necessary when developing things like Google Chrome Extensions or Universal Windows Apps.
24769 * The following rules affect Angular:
24771 * * `unsafe-eval`: this rule forbids apps to use `eval` or `Function(string)` generated functions
24772 * (among other things). Angular makes use of this in the {@link $parse} service to provide a 30%
24773 * increase in the speed of evaluating Angular expressions.
24775 * * `unsafe-inline`: this rule forbids apps from inject custom styles into the document. Angular
24776 * makes use of this to include some CSS rules (e.g. {@link ngCloak} and {@link ngHide}).
24777 * To make these directives work when a CSP rule is blocking inline styles, you must link to the
24778 * `angular-csp.css` in your HTML manually.
24780 * If you do not provide `ngCsp` then Angular tries to autodetect if CSP is blocking unsafe-eval
24781 * and automatically deactivates this feature in the {@link $parse} service. This autodetection,
24782 * however, triggers a CSP error to be logged in the console:
24785 * Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of
24786 * script in the following Content Security Policy directive: "default-src 'self'". Note that
24787 * 'script-src' was not explicitly set, so 'default-src' is used as a fallback.
24790 * This error is harmless but annoying. To prevent the error from showing up, put the `ngCsp`
24791 * directive on an element of the HTML document that appears before the `<script>` tag that loads
24792 * the `angular.js` file.
24794 * *Note: This directive is only available in the `ng-csp` and `data-ng-csp` attribute form.*
24796 * You can specify which of the CSP related Angular features should be deactivated by providing
24797 * a value for the `ng-csp` attribute. The options are as follows:
24799 * * no-inline-style: this stops Angular from injecting CSS styles into the DOM
24801 * * no-unsafe-eval: this stops Angular from optimizing $parse with unsafe eval of strings
24803 * You can use these values in the following combinations:
24806 * * No declaration means that Angular will assume that you can do inline styles, but it will do
24807 * a runtime check for unsafe-eval. E.g. `<body>`. This is backwardly compatible with previous versions
24810 * * A simple `ng-csp` (or `data-ng-csp`) attribute will tell Angular to deactivate both inline
24811 * styles and unsafe eval. E.g. `<body ng-csp>`. This is backwardly compatible with previous versions
24814 * * Specifying only `no-unsafe-eval` tells Angular that we must not use eval, but that we can inject
24815 * inline styles. E.g. `<body ng-csp="no-unsafe-eval">`.
24817 * * Specifying only `no-inline-style` tells Angular that we must not inject styles, but that we can
24818 * run eval - no automatic check for unsafe eval will occur. E.g. `<body ng-csp="no-inline-style">`
24820 * * Specifying both `no-unsafe-eval` and `no-inline-style` tells Angular that we must not inject
24821 * styles nor use eval, which is the same as an empty: ng-csp.
24822 * E.g.`<body ng-csp="no-inline-style;no-unsafe-eval">`
24825 * This example shows how to apply the `ngCsp` directive to the `html` tag.
24828 <html ng-app ng-csp>
24834 // Note: the suffix `.csp` in the example name triggers
24835 // csp mode in our http server!
24836 <example name="example.csp" module="cspExample" ng-csp="true">
24837 <file name="index.html">
24838 <div ng-controller="MainController as ctrl">
24840 <button ng-click="ctrl.inc()" id="inc">Increment</button>
24841 <span id="counter">
24847 <button ng-click="ctrl.evil()" id="evil">Evil</button>
24848 <span id="evilError">
24854 <file name="script.js">
24855 angular.module('cspExample', [])
24856 .controller('MainController', function() {
24858 this.inc = function() {
24861 this.evil = function() {
24862 // jshint evil:true
24866 this.evilError = e.message;
24871 <file name="protractor.js" type="protractor">
24872 var util, webdriver;
24874 var incBtn = element(by.id('inc'));
24875 var counter = element(by.id('counter'));
24876 var evilBtn = element(by.id('evil'));
24877 var evilError = element(by.id('evilError'));
24879 function getAndClearSevereErrors() {
24880 return browser.manage().logs().get('browser').then(function(browserLog) {
24881 return browserLog.filter(function(logEntry) {
24882 return logEntry.level.value > webdriver.logging.Level.WARNING.value;
24887 function clearErrors() {
24888 getAndClearSevereErrors();
24891 function expectNoErrors() {
24892 getAndClearSevereErrors().then(function(filteredLog) {
24893 expect(filteredLog.length).toEqual(0);
24894 if (filteredLog.length) {
24895 console.log('browser console errors: ' + util.inspect(filteredLog));
24900 function expectError(regex) {
24901 getAndClearSevereErrors().then(function(filteredLog) {
24903 filteredLog.forEach(function(log) {
24904 if (log.message.match(regex)) {
24909 throw new Error('expected an error that matches ' + regex);
24914 beforeEach(function() {
24915 util = require('util');
24916 webdriver = require('protractor/node_modules/selenium-webdriver');
24919 // For now, we only test on Chrome,
24920 // as Safari does not load the page with Protractor's injected scripts,
24921 // and Firefox webdriver always disables content security policy (#6358)
24922 if (browser.params.browser !== 'chrome') {
24926 it('should not report errors when the page is loaded', function() {
24927 // clear errors so we are not dependent on previous tests
24929 // Need to reload the page as the page is already loaded when
24931 browser.driver.getCurrentUrl().then(function(url) {
24937 it('should evaluate expressions', function() {
24938 expect(counter.getText()).toEqual('0');
24940 expect(counter.getText()).toEqual('1');
24944 it('should throw and report an error when using "eval"', function() {
24946 expect(evilError.getText()).toMatch(/Content Security Policy/);
24947 expectError(/Content Security Policy/);
24953 // ngCsp is not implemented as a proper directive any more, because we need it be processed while we
24954 // bootstrap the system (before $parse is instantiated), for this reason we just have
24955 // the csp() fn that looks for the `ng-csp` attribute anywhere in the current doc
24962 * The ngClick directive allows you to specify custom behavior when
24963 * an element is clicked.
24967 * @param {expression} ngClick {@link guide/expression Expression} to evaluate upon
24968 * click. ({@link guide/expression#-event- Event object is available as `$event`})
24972 <file name="index.html">
24973 <button ng-click="count = count + 1" ng-init="count=0">
24980 <file name="protractor.js" type="protractor">
24981 it('should check ng-click', function() {
24982 expect(element(by.binding('count')).getText()).toMatch('0');
24983 element(by.css('button')).click();
24984 expect(element(by.binding('count')).getText()).toMatch('1');
24990 * A collection of directives that allows creation of custom event handlers that are defined as
24991 * angular expressions and are compiled and executed within the current scope.
24993 var ngEventDirectives = {};
24995 // For events that might fire synchronously during DOM manipulation
24996 // we need to execute their event handlers asynchronously using $evalAsync,
24997 // so that they are not executed in an inconsistent state.
24998 var forceAsyncEvents = {
25003 'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste'.split(' '),
25004 function(eventName) {
25005 var directiveName = directiveNormalize('ng-' + eventName);
25006 ngEventDirectives[directiveName] = ['$parse', '$rootScope', function($parse, $rootScope) {
25009 compile: function($element, attr) {
25010 // We expose the powerful $event object on the scope that provides access to the Window,
25011 // etc. that isn't protected by the fast paths in $parse. We explicitly request better
25012 // checks at the cost of speed since event handler expressions are not executed as
25013 // frequently as regular change detection.
25014 var fn = $parse(attr[directiveName], /* interceptorFn */ null, /* expensiveChecks */ true);
25015 return function ngEventHandler(scope, element) {
25016 element.on(eventName, function(event) {
25017 var callback = function() {
25018 fn(scope, {$event:event});
25020 if (forceAsyncEvents[eventName] && $rootScope.$$phase) {
25021 scope.$evalAsync(callback);
25023 scope.$apply(callback);
25038 * The `ngDblclick` directive allows you to specify custom behavior on a dblclick event.
25042 * @param {expression} ngDblclick {@link guide/expression Expression} to evaluate upon
25043 * a dblclick. (The Event object is available as `$event`)
25047 <file name="index.html">
25048 <button ng-dblclick="count = count + 1" ng-init="count=0">
25049 Increment (on double click)
25059 * @name ngMousedown
25062 * The ngMousedown directive allows you to specify custom behavior on mousedown event.
25066 * @param {expression} ngMousedown {@link guide/expression Expression} to evaluate upon
25067 * mousedown. ({@link guide/expression#-event- Event object is available as `$event`})
25071 <file name="index.html">
25072 <button ng-mousedown="count = count + 1" ng-init="count=0">
25073 Increment (on mouse down)
25086 * Specify custom behavior on mouseup event.
25090 * @param {expression} ngMouseup {@link guide/expression Expression} to evaluate upon
25091 * mouseup. ({@link guide/expression#-event- Event object is available as `$event`})
25095 <file name="index.html">
25096 <button ng-mouseup="count = count + 1" ng-init="count=0">
25097 Increment (on mouse up)
25106 * @name ngMouseover
25109 * Specify custom behavior on mouseover event.
25113 * @param {expression} ngMouseover {@link guide/expression Expression} to evaluate upon
25114 * mouseover. ({@link guide/expression#-event- Event object is available as `$event`})
25118 <file name="index.html">
25119 <button ng-mouseover="count = count + 1" ng-init="count=0">
25120 Increment (when mouse is over)
25130 * @name ngMouseenter
25133 * Specify custom behavior on mouseenter event.
25137 * @param {expression} ngMouseenter {@link guide/expression Expression} to evaluate upon
25138 * mouseenter. ({@link guide/expression#-event- Event object is available as `$event`})
25142 <file name="index.html">
25143 <button ng-mouseenter="count = count + 1" ng-init="count=0">
25144 Increment (when mouse enters)
25154 * @name ngMouseleave
25157 * Specify custom behavior on mouseleave event.
25161 * @param {expression} ngMouseleave {@link guide/expression Expression} to evaluate upon
25162 * mouseleave. ({@link guide/expression#-event- Event object is available as `$event`})
25166 <file name="index.html">
25167 <button ng-mouseleave="count = count + 1" ng-init="count=0">
25168 Increment (when mouse leaves)
25178 * @name ngMousemove
25181 * Specify custom behavior on mousemove event.
25185 * @param {expression} ngMousemove {@link guide/expression Expression} to evaluate upon
25186 * mousemove. ({@link guide/expression#-event- Event object is available as `$event`})
25190 <file name="index.html">
25191 <button ng-mousemove="count = count + 1" ng-init="count=0">
25192 Increment (when mouse moves)
25205 * Specify custom behavior on keydown event.
25209 * @param {expression} ngKeydown {@link guide/expression Expression} to evaluate upon
25210 * keydown. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
25214 <file name="index.html">
25215 <input ng-keydown="count = count + 1" ng-init="count=0">
25216 key down count: {{count}}
25227 * Specify custom behavior on keyup event.
25231 * @param {expression} ngKeyup {@link guide/expression Expression} to evaluate upon
25232 * keyup. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
25236 <file name="index.html">
25237 <p>Typing in the input box below updates the key count</p>
25238 <input ng-keyup="count = count + 1" ng-init="count=0"> key up count: {{count}}
25240 <p>Typing in the input box below updates the keycode</p>
25241 <input ng-keyup="event=$event">
25242 <p>event keyCode: {{ event.keyCode }}</p>
25243 <p>event altKey: {{ event.altKey }}</p>
25254 * Specify custom behavior on keypress event.
25257 * @param {expression} ngKeypress {@link guide/expression Expression} to evaluate upon
25258 * keypress. ({@link guide/expression#-event- Event object is available as `$event`}
25259 * and can be interrogated for keyCode, altKey, etc.)
25263 <file name="index.html">
25264 <input ng-keypress="count = count + 1" ng-init="count=0">
25265 key press count: {{count}}
25276 * Enables binding angular expressions to onsubmit events.
25278 * Additionally it prevents the default action (which for form means sending the request to the
25279 * server and reloading the current page), but only if the form does not contain `action`,
25280 * `data-action`, or `x-action` attributes.
25282 * <div class="alert alert-warning">
25283 * **Warning:** Be careful not to cause "double-submission" by using both the `ngClick` and
25284 * `ngSubmit` handlers together. See the
25285 * {@link form#submitting-a-form-and-preventing-the-default-action `form` directive documentation}
25286 * for a detailed discussion of when `ngSubmit` may be triggered.
25291 * @param {expression} ngSubmit {@link guide/expression Expression} to eval.
25292 * ({@link guide/expression#-event- Event object is available as `$event`})
25295 <example module="submitExample">
25296 <file name="index.html">
25298 angular.module('submitExample', [])
25299 .controller('ExampleController', ['$scope', function($scope) {
25301 $scope.text = 'hello';
25302 $scope.submit = function() {
25304 $scope.list.push(this.text);
25310 <form ng-submit="submit()" ng-controller="ExampleController">
25311 Enter text and hit enter:
25312 <input type="text" ng-model="text" name="text" />
25313 <input type="submit" id="submit" value="Submit" />
25314 <pre>list={{list}}</pre>
25317 <file name="protractor.js" type="protractor">
25318 it('should check ng-submit', function() {
25319 expect(element(by.binding('list')).getText()).toBe('list=[]');
25320 element(by.css('#submit')).click();
25321 expect(element(by.binding('list')).getText()).toContain('hello');
25322 expect(element(by.model('text')).getAttribute('value')).toBe('');
25324 it('should ignore empty strings', function() {
25325 expect(element(by.binding('list')).getText()).toBe('list=[]');
25326 element(by.css('#submit')).click();
25327 element(by.css('#submit')).click();
25328 expect(element(by.binding('list')).getText()).toContain('hello');
25339 * Specify custom behavior on focus event.
25341 * Note: As the `focus` event is executed synchronously when calling `input.focus()`
25342 * AngularJS executes the expression using `scope.$evalAsync` if the event is fired
25343 * during an `$apply` to ensure a consistent state.
25345 * @element window, input, select, textarea, a
25347 * @param {expression} ngFocus {@link guide/expression Expression} to evaluate upon
25348 * focus. ({@link guide/expression#-event- Event object is available as `$event`})
25351 * See {@link ng.directive:ngClick ngClick}
25359 * Specify custom behavior on blur event.
25361 * A [blur event](https://developer.mozilla.org/en-US/docs/Web/Events/blur) fires when
25362 * an element has lost focus.
25364 * Note: As the `blur` event is executed synchronously also during DOM manipulations
25365 * (e.g. removing a focussed input),
25366 * AngularJS executes the expression using `scope.$evalAsync` if the event is fired
25367 * during an `$apply` to ensure a consistent state.
25369 * @element window, input, select, textarea, a
25371 * @param {expression} ngBlur {@link guide/expression Expression} to evaluate upon
25372 * blur. ({@link guide/expression#-event- Event object is available as `$event`})
25375 * See {@link ng.directive:ngClick ngClick}
25383 * Specify custom behavior on copy event.
25385 * @element window, input, select, textarea, a
25387 * @param {expression} ngCopy {@link guide/expression Expression} to evaluate upon
25388 * copy. ({@link guide/expression#-event- Event object is available as `$event`})
25392 <file name="index.html">
25393 <input ng-copy="copied=true" ng-init="copied=false; value='copy me'" ng-model="value">
25404 * Specify custom behavior on cut event.
25406 * @element window, input, select, textarea, a
25408 * @param {expression} ngCut {@link guide/expression Expression} to evaluate upon
25409 * cut. ({@link guide/expression#-event- Event object is available as `$event`})
25413 <file name="index.html">
25414 <input ng-cut="cut=true" ng-init="cut=false; value='cut me'" ng-model="value">
25425 * Specify custom behavior on paste event.
25427 * @element window, input, select, textarea, a
25429 * @param {expression} ngPaste {@link guide/expression Expression} to evaluate upon
25430 * paste. ({@link guide/expression#-event- Event object is available as `$event`})
25434 <file name="index.html">
25435 <input ng-paste="paste=true" ng-init="paste=false" placeholder='paste here'>
25448 * The `ngIf` directive removes or recreates a portion of the DOM tree based on an
25449 * {expression}. If the expression assigned to `ngIf` evaluates to a false
25450 * value then the element is removed from the DOM, otherwise a clone of the
25451 * element is reinserted into the DOM.
25453 * `ngIf` differs from `ngShow` and `ngHide` in that `ngIf` completely removes and recreates the
25454 * element in the DOM rather than changing its visibility via the `display` css property. A common
25455 * case when this difference is significant is when using css selectors that rely on an element's
25456 * position within the DOM, such as the `:first-child` or `:last-child` pseudo-classes.
25458 * Note that when an element is removed using `ngIf` its scope is destroyed and a new scope
25459 * is created when the element is restored. The scope created within `ngIf` inherits from
25460 * its parent scope using
25461 * [prototypal inheritance](https://github.com/angular/angular.js/wiki/Understanding-Scopes#javascript-prototypal-inheritance).
25462 * An important implication of this is if `ngModel` is used within `ngIf` to bind to
25463 * a javascript primitive defined in the parent scope. In this case any modifications made to the
25464 * variable within the child scope will override (hide) the value in the parent scope.
25466 * Also, `ngIf` recreates elements using their compiled state. An example of this behavior
25467 * is if an element's class attribute is directly modified after it's compiled, using something like
25468 * jQuery's `.addClass()` method, and the element is later removed. When `ngIf` recreates the element
25469 * the added class will be lost because the original compiled state is used to regenerate the element.
25471 * Additionally, you can provide animations via the `ngAnimate` module to animate the `enter`
25472 * and `leave` effects.
25475 * | Animation | Occurs |
25476 * |----------------------------------|-------------------------------------|
25477 * | {@link ng.$animate#enter enter} | just after the `ngIf` contents change and a new DOM element is created and injected into the `ngIf` container |
25478 * | {@link ng.$animate#leave leave} | just before the `ngIf` contents are removed from the DOM |
25483 * @param {expression} ngIf If the {@link guide/expression expression} is falsy then
25484 * the element is removed from the DOM tree. If it is truthy a copy of the compiled
25485 * element is added to the DOM tree.
25488 <example module="ngAnimate" deps="angular-animate.js" animations="true">
25489 <file name="index.html">
25490 <label>Click me: <input type="checkbox" ng-model="checked" ng-init="checked=true" /></label><br/>
25492 <span ng-if="checked" class="animate-if">
25493 This is removed when the checkbox is unchecked.
25496 <file name="animations.css">
25499 border:1px solid black;
25503 .animate-if.ng-enter, .animate-if.ng-leave {
25504 transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
25507 .animate-if.ng-enter,
25508 .animate-if.ng-leave.ng-leave-active {
25512 .animate-if.ng-leave,
25513 .animate-if.ng-enter.ng-enter-active {
25519 var ngIfDirective = ['$animate', '$compile', function($animate, $compile) {
25521 multiElement: true,
25522 transclude: 'element',
25527 link: function($scope, $element, $attr, ctrl, $transclude) {
25528 var block, childScope, previousElements;
25529 $scope.$watch($attr.ngIf, function ngIfWatchAction(value) {
25533 $transclude(function(clone, newScope) {
25534 childScope = newScope;
25535 clone[clone.length++] = $compile.$$createComment('end ngIf', $attr.ngIf);
25536 // Note: We only need the first/last node of the cloned nodes.
25537 // However, we need to keep the reference to the jqlite wrapper as it might be changed later
25538 // by a directive with templateUrl when its template arrives.
25542 $animate.enter(clone, $element.parent(), $element);
25546 if (previousElements) {
25547 previousElements.remove();
25548 previousElements = null;
25551 childScope.$destroy();
25555 previousElements = getBlockNodes(block.clone);
25556 $animate.leave(previousElements).then(function() {
25557 previousElements = null;
25573 * Fetches, compiles and includes an external HTML fragment.
25575 * By default, the template URL is restricted to the same domain and protocol as the
25576 * application document. This is done by calling {@link $sce#getTrustedResourceUrl
25577 * $sce.getTrustedResourceUrl} on it. To load templates from other domains or protocols
25578 * you may either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist them} or
25579 * {@link $sce#trustAsResourceUrl wrap them} as trusted values. Refer to Angular's {@link
25580 * ng.$sce Strict Contextual Escaping}.
25582 * In addition, the browser's
25583 * [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest)
25584 * and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/)
25585 * policy may further restrict whether the template is successfully loaded.
25586 * For example, `ngInclude` won't work for cross-domain requests on all browsers and for `file://`
25587 * access on some browsers.
25590 * | Animation | Occurs |
25591 * |----------------------------------|-------------------------------------|
25592 * | {@link ng.$animate#enter enter} | when the expression changes, on the new include |
25593 * | {@link ng.$animate#leave leave} | when the expression changes, on the old include |
25595 * The enter and leave animation occur concurrently.
25600 * @param {string} ngInclude|src angular expression evaluating to URL. If the source is a string constant,
25601 * make sure you wrap it in **single** quotes, e.g. `src="'myPartialTemplate.html'"`.
25602 * @param {string=} onload Expression to evaluate when a new partial is loaded.
25603 * <div class="alert alert-warning">
25604 * **Note:** When using onload on SVG elements in IE11, the browser will try to call
25605 * a function with the name on the window element, which will usually throw a
25606 * "function is undefined" error. To fix this, you can instead use `data-onload` or a
25607 * different form that {@link guide/directive#normalization matches} `onload`.
25610 * @param {string=} autoscroll Whether `ngInclude` should call {@link ng.$anchorScroll
25611 * $anchorScroll} to scroll the viewport after the content is loaded.
25613 * - If the attribute is not set, disable scrolling.
25614 * - If the attribute is set without value, enable scrolling.
25615 * - Otherwise enable scrolling only if the expression evaluates to truthy value.
25618 <example module="includeExample" deps="angular-animate.js" animations="true">
25619 <file name="index.html">
25620 <div ng-controller="ExampleController">
25621 <select ng-model="template" ng-options="t.name for t in templates">
25622 <option value="">(blank)</option>
25624 url of the template: <code>{{template.url}}</code>
25626 <div class="slide-animate-container">
25627 <div class="slide-animate" ng-include="template.url"></div>
25631 <file name="script.js">
25632 angular.module('includeExample', ['ngAnimate'])
25633 .controller('ExampleController', ['$scope', function($scope) {
25635 [ { name: 'template1.html', url: 'template1.html'},
25636 { name: 'template2.html', url: 'template2.html'} ];
25637 $scope.template = $scope.templates[0];
25640 <file name="template1.html">
25641 Content of template1.html
25643 <file name="template2.html">
25644 Content of template2.html
25646 <file name="animations.css">
25647 .slide-animate-container {
25650 border:1px solid black;
25659 .slide-animate.ng-enter, .slide-animate.ng-leave {
25660 transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
25671 .slide-animate.ng-enter {
25674 .slide-animate.ng-enter.ng-enter-active {
25678 .slide-animate.ng-leave {
25681 .slide-animate.ng-leave.ng-leave-active {
25685 <file name="protractor.js" type="protractor">
25686 var templateSelect = element(by.model('template'));
25687 var includeElem = element(by.css('[ng-include]'));
25689 it('should load template1.html', function() {
25690 expect(includeElem.getText()).toMatch(/Content of template1.html/);
25693 it('should load template2.html', function() {
25694 if (browser.params.browser == 'firefox') {
25695 // Firefox can't handle using selects
25696 // See https://github.com/angular/protractor/issues/480
25699 templateSelect.click();
25700 templateSelect.all(by.css('option')).get(2).click();
25701 expect(includeElem.getText()).toMatch(/Content of template2.html/);
25704 it('should change to blank', function() {
25705 if (browser.params.browser == 'firefox') {
25706 // Firefox can't handle using selects
25709 templateSelect.click();
25710 templateSelect.all(by.css('option')).get(0).click();
25711 expect(includeElem.isPresent()).toBe(false);
25720 * @name ngInclude#$includeContentRequested
25721 * @eventType emit on the scope ngInclude was declared in
25723 * Emitted every time the ngInclude content is requested.
25725 * @param {Object} angularEvent Synthetic event object.
25726 * @param {String} src URL of content to load.
25732 * @name ngInclude#$includeContentLoaded
25733 * @eventType emit on the current ngInclude scope
25735 * Emitted every time the ngInclude content is reloaded.
25737 * @param {Object} angularEvent Synthetic event object.
25738 * @param {String} src URL of content to load.
25744 * @name ngInclude#$includeContentError
25745 * @eventType emit on the scope ngInclude was declared in
25747 * Emitted when a template HTTP request yields an erroneous response (status < 200 || status > 299)
25749 * @param {Object} angularEvent Synthetic event object.
25750 * @param {String} src URL of content to load.
25752 var ngIncludeDirective = ['$templateRequest', '$anchorScroll', '$animate',
25753 function($templateRequest, $anchorScroll, $animate) {
25758 transclude: 'element',
25759 controller: angular.noop,
25760 compile: function(element, attr) {
25761 var srcExp = attr.ngInclude || attr.src,
25762 onloadExp = attr.onload || '',
25763 autoScrollExp = attr.autoscroll;
25765 return function(scope, $element, $attr, ctrl, $transclude) {
25766 var changeCounter = 0,
25771 var cleanupLastIncludeContent = function() {
25772 if (previousElement) {
25773 previousElement.remove();
25774 previousElement = null;
25776 if (currentScope) {
25777 currentScope.$destroy();
25778 currentScope = null;
25780 if (currentElement) {
25781 $animate.leave(currentElement).then(function() {
25782 previousElement = null;
25784 previousElement = currentElement;
25785 currentElement = null;
25789 scope.$watch(srcExp, function ngIncludeWatchAction(src) {
25790 var afterAnimation = function() {
25791 if (isDefined(autoScrollExp) && (!autoScrollExp || scope.$eval(autoScrollExp))) {
25795 var thisChangeId = ++changeCounter;
25798 //set the 2nd param to true to ignore the template request error so that the inner
25799 //contents and scope can be cleaned up.
25800 $templateRequest(src, true).then(function(response) {
25801 if (scope.$$destroyed) return;
25803 if (thisChangeId !== changeCounter) return;
25804 var newScope = scope.$new();
25805 ctrl.template = response;
25807 // Note: This will also link all children of ng-include that were contained in the original
25808 // html. If that content contains controllers, ... they could pollute/change the scope.
25809 // However, using ng-include on an element with additional content does not make sense...
25810 // Note: We can't remove them in the cloneAttchFn of $transclude as that
25811 // function is called before linking the content, which would apply child
25812 // directives to non existing elements.
25813 var clone = $transclude(newScope, function(clone) {
25814 cleanupLastIncludeContent();
25815 $animate.enter(clone, null, $element).then(afterAnimation);
25818 currentScope = newScope;
25819 currentElement = clone;
25821 currentScope.$emit('$includeContentLoaded', src);
25822 scope.$eval(onloadExp);
25824 if (scope.$$destroyed) return;
25826 if (thisChangeId === changeCounter) {
25827 cleanupLastIncludeContent();
25828 scope.$emit('$includeContentError', src);
25831 scope.$emit('$includeContentRequested', src);
25833 cleanupLastIncludeContent();
25834 ctrl.template = null;
25842 // This directive is called during the $transclude call of the first `ngInclude` directive.
25843 // It will replace and compile the content of the element with the loaded template.
25844 // We need this directive so that the element content is already filled when
25845 // the link function of another directive on the same element as ngInclude
25847 var ngIncludeFillContentDirective = ['$compile',
25848 function($compile) {
25852 require: 'ngInclude',
25853 link: function(scope, $element, $attr, ctrl) {
25854 if (toString.call($element[0]).match(/SVG/)) {
25855 // WebKit: https://bugs.webkit.org/show_bug.cgi?id=135698 --- SVG elements do not
25856 // support innerHTML, so detect this here and try to generate the contents
25859 $compile(jqLiteBuildFragment(ctrl.template, window.document).childNodes)(scope,
25860 function namespaceAdaptedClone(clone) {
25861 $element.append(clone);
25862 }, {futureParentElement: $element});
25866 $element.html(ctrl.template);
25867 $compile($element.contents())(scope);
25878 * The `ngInit` directive allows you to evaluate an expression in the
25881 * <div class="alert alert-danger">
25882 * This directive can be abused to add unnecessary amounts of logic into your templates.
25883 * There are only a few appropriate uses of `ngInit`, such as for aliasing special properties of
25884 * {@link ng.directive:ngRepeat `ngRepeat`}, as seen in the demo below; and for injecting data via
25885 * server side scripting. Besides these few cases, you should use {@link guide/controller controllers}
25886 * rather than `ngInit` to initialize values on a scope.
25889 * <div class="alert alert-warning">
25890 * **Note**: If you have assignment in `ngInit` along with a {@link ng.$filter `filter`}, make
25891 * sure you have parentheses to ensure correct operator precedence:
25892 * <pre class="prettyprint">
25893 * `<div ng-init="test1 = ($index | toString)"></div>`
25900 * @param {expression} ngInit {@link guide/expression Expression} to eval.
25903 <example module="initExample">
25904 <file name="index.html">
25906 angular.module('initExample', [])
25907 .controller('ExampleController', ['$scope', function($scope) {
25908 $scope.list = [['a', 'b'], ['c', 'd']];
25911 <div ng-controller="ExampleController">
25912 <div ng-repeat="innerList in list" ng-init="outerIndex = $index">
25913 <div ng-repeat="value in innerList" ng-init="innerIndex = $index">
25914 <span class="example-init">list[ {{outerIndex}} ][ {{innerIndex}} ] = {{value}};</span>
25919 <file name="protractor.js" type="protractor">
25920 it('should alias index positions', function() {
25921 var elements = element.all(by.css('.example-init'));
25922 expect(elements.get(0).getText()).toBe('list[ 0 ][ 0 ] = a;');
25923 expect(elements.get(1).getText()).toBe('list[ 0 ][ 1 ] = b;');
25924 expect(elements.get(2).getText()).toBe('list[ 1 ][ 0 ] = c;');
25925 expect(elements.get(3).getText()).toBe('list[ 1 ][ 1 ] = d;');
25930 var ngInitDirective = ngDirective({
25932 compile: function() {
25934 pre: function(scope, element, attrs) {
25935 scope.$eval(attrs.ngInit);
25946 * Text input that converts between a delimited string and an array of strings. The default
25947 * delimiter is a comma followed by a space - equivalent to `ng-list=", "`. You can specify a custom
25948 * delimiter as the value of the `ngList` attribute - for example, `ng-list=" | "`.
25950 * The behaviour of the directive is affected by the use of the `ngTrim` attribute.
25951 * * If `ngTrim` is set to `"false"` then whitespace around both the separator and each
25952 * list item is respected. This implies that the user of the directive is responsible for
25953 * dealing with whitespace but also allows you to use whitespace as a delimiter, such as a
25954 * tab or newline character.
25955 * * Otherwise whitespace around the delimiter is ignored when splitting (although it is respected
25956 * when joining the list items back together) and whitespace around each list item is stripped
25957 * before it is added to the model.
25959 * ### Example with Validation
25961 * <example name="ngList-directive" module="listExample">
25962 * <file name="app.js">
25963 * angular.module('listExample', [])
25964 * .controller('ExampleController', ['$scope', function($scope) {
25965 * $scope.names = ['morpheus', 'neo', 'trinity'];
25968 * <file name="index.html">
25969 * <form name="myForm" ng-controller="ExampleController">
25970 * <label>List: <input name="namesInput" ng-model="names" ng-list required></label>
25971 * <span role="alert">
25972 * <span class="error" ng-show="myForm.namesInput.$error.required">
25976 * <tt>names = {{names}}</tt><br/>
25977 * <tt>myForm.namesInput.$valid = {{myForm.namesInput.$valid}}</tt><br/>
25978 * <tt>myForm.namesInput.$error = {{myForm.namesInput.$error}}</tt><br/>
25979 * <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
25980 * <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
25983 * <file name="protractor.js" type="protractor">
25984 * var listInput = element(by.model('names'));
25985 * var names = element(by.exactBinding('names'));
25986 * var valid = element(by.binding('myForm.namesInput.$valid'));
25987 * var error = element(by.css('span.error'));
25989 * it('should initialize to model', function() {
25990 * expect(names.getText()).toContain('["morpheus","neo","trinity"]');
25991 * expect(valid.getText()).toContain('true');
25992 * expect(error.getCssValue('display')).toBe('none');
25995 * it('should be invalid if empty', function() {
25996 * listInput.clear();
25997 * listInput.sendKeys('');
25999 * expect(names.getText()).toContain('');
26000 * expect(valid.getText()).toContain('false');
26001 * expect(error.getCssValue('display')).not.toBe('none');
26006 * ### Example - splitting on newline
26007 * <example name="ngList-directive-newlines">
26008 * <file name="index.html">
26009 * <textarea ng-model="list" ng-list=" " ng-trim="false"></textarea>
26010 * <pre>{{ list | json }}</pre>
26012 * <file name="protractor.js" type="protractor">
26013 * it("should split the text by newlines", function() {
26014 * var listInput = element(by.model('list'));
26015 * var output = element(by.binding('list | json'));
26016 * listInput.sendKeys('abc\ndef\nghi');
26017 * expect(output.getText()).toContain('[\n "abc",\n "def",\n "ghi"\n]');
26023 * @param {string=} ngList optional delimiter that should be used to split the value.
26025 var ngListDirective = function() {
26029 require: 'ngModel',
26030 link: function(scope, element, attr, ctrl) {
26031 // We want to control whitespace trimming so we use this convoluted approach
26032 // to access the ngList attribute, which doesn't pre-trim the attribute
26033 var ngList = element.attr(attr.$attr.ngList) || ', ';
26034 var trimValues = attr.ngTrim !== 'false';
26035 var separator = trimValues ? trim(ngList) : ngList;
26037 var parse = function(viewValue) {
26038 // If the viewValue is invalid (say required but empty) it will be `undefined`
26039 if (isUndefined(viewValue)) return;
26044 forEach(viewValue.split(separator), function(value) {
26045 if (value) list.push(trimValues ? trim(value) : value);
26052 ctrl.$parsers.push(parse);
26053 ctrl.$formatters.push(function(value) {
26054 if (isArray(value)) {
26055 return value.join(ngList);
26061 // Override the standard $isEmpty because an empty array means the input is empty.
26062 ctrl.$isEmpty = function(value) {
26063 return !value || !value.length;
26069 /* global VALID_CLASS: true,
26070 INVALID_CLASS: true,
26071 PRISTINE_CLASS: true,
26073 UNTOUCHED_CLASS: true,
26074 TOUCHED_CLASS: true,
26077 var VALID_CLASS = 'ng-valid',
26078 INVALID_CLASS = 'ng-invalid',
26079 PRISTINE_CLASS = 'ng-pristine',
26080 DIRTY_CLASS = 'ng-dirty',
26081 UNTOUCHED_CLASS = 'ng-untouched',
26082 TOUCHED_CLASS = 'ng-touched',
26083 PENDING_CLASS = 'ng-pending',
26084 EMPTY_CLASS = 'ng-empty',
26085 NOT_EMPTY_CLASS = 'ng-not-empty';
26087 var ngModelMinErr = minErr('ngModel');
26091 * @name ngModel.NgModelController
26093 * @property {*} $viewValue The actual value from the control's view. For `input` elements, this is a
26094 * String. See {@link ngModel.NgModelController#$setViewValue} for information about when the $viewValue
26096 * @property {*} $modelValue The value in the model that the control is bound to.
26097 * @property {Array.<Function>} $parsers Array of functions to execute, as a pipeline, whenever
26098 the control reads value from the DOM. The functions are called in array order, each passing
26099 its return value through to the next. The last return value is forwarded to the
26100 {@link ngModel.NgModelController#$validators `$validators`} collection.
26102 Parsers are used to sanitize / convert the {@link ngModel.NgModelController#$viewValue
26105 Returning `undefined` from a parser means a parse error occurred. In that case,
26106 no {@link ngModel.NgModelController#$validators `$validators`} will run and the `ngModel`
26107 will be set to `undefined` unless {@link ngModelOptions `ngModelOptions.allowInvalid`}
26108 is set to `true`. The parse error is stored in `ngModel.$error.parse`.
26111 * @property {Array.<Function>} $formatters Array of functions to execute, as a pipeline, whenever
26112 the model value changes. The functions are called in reverse array order, each passing the value through to the
26113 next. The last return value is used as the actual DOM value.
26114 Used to format / convert values for display in the control.
26116 * function formatter(value) {
26118 * return value.toUpperCase();
26121 * ngModel.$formatters.push(formatter);
26124 * @property {Object.<string, function>} $validators A collection of validators that are applied
26125 * whenever the model value changes. The key value within the object refers to the name of the
26126 * validator while the function refers to the validation operation. The validation operation is
26127 * provided with the model value as an argument and must return a true or false value depending
26128 * on the response of that validation.
26131 * ngModel.$validators.validCharacters = function(modelValue, viewValue) {
26132 * var value = modelValue || viewValue;
26133 * return /[0-9]+/.test(value) &&
26134 * /[a-z]+/.test(value) &&
26135 * /[A-Z]+/.test(value) &&
26136 * /\W+/.test(value);
26140 * @property {Object.<string, function>} $asyncValidators A collection of validations that are expected to
26141 * perform an asynchronous validation (e.g. a HTTP request). The validation function that is provided
26142 * is expected to return a promise when it is run during the model validation process. Once the promise
26143 * is delivered then the validation status will be set to true when fulfilled and false when rejected.
26144 * When the asynchronous validators are triggered, each of the validators will run in parallel and the model
26145 * value will only be updated once all validators have been fulfilled. As long as an asynchronous validator
26146 * is unfulfilled, its key will be added to the controllers `$pending` property. Also, all asynchronous validators
26147 * will only run once all synchronous validators have passed.
26149 * Please note that if $http is used then it is important that the server returns a success HTTP response code
26150 * in order to fulfill the validation and a status level of `4xx` in order to reject the validation.
26153 * ngModel.$asyncValidators.uniqueUsername = function(modelValue, viewValue) {
26154 * var value = modelValue || viewValue;
26156 * // Lookup user by username
26157 * return $http.get('/api/users/' + value).
26158 * then(function resolved() {
26159 * //username exists, this means validation fails
26160 * return $q.reject('exists');
26161 * }, function rejected() {
26162 * //username does not exist, therefore this validation passes
26168 * @property {Array.<Function>} $viewChangeListeners Array of functions to execute whenever the
26169 * view value has changed. It is called with no arguments, and its return value is ignored.
26170 * This can be used in place of additional $watches against the model value.
26172 * @property {Object} $error An object hash with all failing validator ids as keys.
26173 * @property {Object} $pending An object hash with all pending validator ids as keys.
26175 * @property {boolean} $untouched True if control has not lost focus yet.
26176 * @property {boolean} $touched True if control has lost focus.
26177 * @property {boolean} $pristine True if user has not interacted with the control yet.
26178 * @property {boolean} $dirty True if user has already interacted with the control.
26179 * @property {boolean} $valid True if there is no error.
26180 * @property {boolean} $invalid True if at least one error on the control.
26181 * @property {string} $name The name attribute of the control.
26185 * `NgModelController` provides API for the {@link ngModel `ngModel`} directive.
26186 * The controller contains services for data-binding, validation, CSS updates, and value formatting
26187 * and parsing. It purposefully does not contain any logic which deals with DOM rendering or
26188 * listening to DOM events.
26189 * Such DOM related logic should be provided by other directives which make use of
26190 * `NgModelController` for data-binding to control elements.
26191 * Angular provides this DOM logic for most {@link input `input`} elements.
26192 * At the end of this page you can find a {@link ngModel.NgModelController#custom-control-example
26193 * custom control example} that uses `ngModelController` to bind to `contenteditable` elements.
26196 * ### Custom Control Example
26197 * This example shows how to use `NgModelController` with a custom control to achieve
26198 * data-binding. Notice how different directives (`contenteditable`, `ng-model`, and `required`)
26199 * collaborate together to achieve the desired result.
26201 * `contenteditable` is an HTML5 attribute, which tells the browser to let the element
26202 * contents be edited in place by the user.
26204 * We are using the {@link ng.service:$sce $sce} service here and include the {@link ngSanitize $sanitize}
26205 * module to automatically remove "bad" content like inline event listener (e.g. `<span onclick="...">`).
26206 * However, as we are using `$sce` the model can still decide to provide unsafe content if it marks
26207 * that content using the `$sce` service.
26209 * <example name="NgModelController" module="customControl" deps="angular-sanitize.js">
26210 <file name="style.css">
26211 [contenteditable] {
26212 border: 1px solid black;
26213 background-color: white;
26218 border: 1px solid red;
26222 <file name="script.js">
26223 angular.module('customControl', ['ngSanitize']).
26224 directive('contenteditable', ['$sce', function($sce) {
26226 restrict: 'A', // only activate on element attribute
26227 require: '?ngModel', // get a hold of NgModelController
26228 link: function(scope, element, attrs, ngModel) {
26229 if (!ngModel) return; // do nothing if no ng-model
26231 // Specify how UI should be updated
26232 ngModel.$render = function() {
26233 element.html($sce.getTrustedHtml(ngModel.$viewValue || ''));
26236 // Listen for change events to enable binding
26237 element.on('blur keyup change', function() {
26238 scope.$evalAsync(read);
26240 read(); // initialize
26242 // Write data to the model
26244 var html = element.html();
26245 // When we clear the content editable the browser leaves a <br> behind
26246 // If strip-br attribute is provided then we strip this out
26247 if ( attrs.stripBr && html == '<br>' ) {
26250 ngModel.$setViewValue(html);
26256 <file name="index.html">
26257 <form name="myForm">
26258 <div contenteditable
26259 name="myWidget" ng-model="userContent"
26261 required>Change me!</div>
26262 <span ng-show="myForm.myWidget.$error.required">Required!</span>
26264 <textarea ng-model="userContent" aria-label="Dynamic textarea"></textarea>
26267 <file name="protractor.js" type="protractor">
26268 it('should data-bind and become invalid', function() {
26269 if (browser.params.browser == 'safari' || browser.params.browser == 'firefox') {
26270 // SafariDriver can't handle contenteditable
26271 // and Firefox driver can't clear contenteditables very well
26274 var contentEditable = element(by.css('[contenteditable]'));
26275 var content = 'Change me!';
26277 expect(contentEditable.getText()).toEqual(content);
26279 contentEditable.clear();
26280 contentEditable.sendKeys(protractor.Key.BACK_SPACE);
26281 expect(contentEditable.getText()).toEqual('');
26282 expect(contentEditable.getAttribute('class')).toMatch(/ng-invalid-required/);
26289 var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$parse', '$animate', '$timeout', '$rootScope', '$q', '$interpolate',
26290 function($scope, $exceptionHandler, $attr, $element, $parse, $animate, $timeout, $rootScope, $q, $interpolate) {
26291 this.$viewValue = Number.NaN;
26292 this.$modelValue = Number.NaN;
26293 this.$$rawModelValue = undefined; // stores the parsed modelValue / model set from scope regardless of validity.
26294 this.$validators = {};
26295 this.$asyncValidators = {};
26296 this.$parsers = [];
26297 this.$formatters = [];
26298 this.$viewChangeListeners = [];
26299 this.$untouched = true;
26300 this.$touched = false;
26301 this.$pristine = true;
26302 this.$dirty = false;
26303 this.$valid = true;
26304 this.$invalid = false;
26305 this.$error = {}; // keep invalid keys here
26306 this.$$success = {}; // keep valid keys here
26307 this.$pending = undefined; // keep pending keys here
26308 this.$name = $interpolate($attr.name || '', false)($scope);
26309 this.$$parentForm = nullFormCtrl;
26311 var parsedNgModel = $parse($attr.ngModel),
26312 parsedNgModelAssign = parsedNgModel.assign,
26313 ngModelGet = parsedNgModel,
26314 ngModelSet = parsedNgModelAssign,
26315 pendingDebounce = null,
26319 this.$$setOptions = function(options) {
26320 ctrl.$options = options;
26321 if (options && options.getterSetter) {
26322 var invokeModelGetter = $parse($attr.ngModel + '()'),
26323 invokeModelSetter = $parse($attr.ngModel + '($$$p)');
26325 ngModelGet = function($scope) {
26326 var modelValue = parsedNgModel($scope);
26327 if (isFunction(modelValue)) {
26328 modelValue = invokeModelGetter($scope);
26332 ngModelSet = function($scope, newValue) {
26333 if (isFunction(parsedNgModel($scope))) {
26334 invokeModelSetter($scope, {$$$p: newValue});
26336 parsedNgModelAssign($scope, newValue);
26339 } else if (!parsedNgModel.assign) {
26340 throw ngModelMinErr('nonassign', "Expression '{0}' is non-assignable. Element: {1}",
26341 $attr.ngModel, startingTag($element));
26347 * @name ngModel.NgModelController#$render
26350 * Called when the view needs to be updated. It is expected that the user of the ng-model
26351 * directive will implement this method.
26353 * The `$render()` method is invoked in the following situations:
26355 * * `$rollbackViewValue()` is called. If we are rolling back the view value to the last
26356 * committed value then `$render()` is called to update the input control.
26357 * * The value referenced by `ng-model` is changed programmatically and both the `$modelValue` and
26358 * the `$viewValue` are different from last time.
26360 * Since `ng-model` does not do a deep watch, `$render()` is only invoked if the values of
26361 * `$modelValue` and `$viewValue` are actually different from their previous values. If `$modelValue`
26362 * or `$viewValue` are objects (rather than a string or number) then `$render()` will not be
26363 * invoked if you only change a property on the objects.
26365 this.$render = noop;
26369 * @name ngModel.NgModelController#$isEmpty
26372 * This is called when we need to determine if the value of an input is empty.
26374 * For instance, the required directive does this to work out if the input has data or not.
26376 * The default `$isEmpty` function checks whether the value is `undefined`, `''`, `null` or `NaN`.
26378 * You can override this for input directives whose concept of being empty is different from the
26379 * default. The `checkboxInputType` directive does this because in its case a value of `false`
26382 * @param {*} value The value of the input to check for emptiness.
26383 * @returns {boolean} True if `value` is "empty".
26385 this.$isEmpty = function(value) {
26386 return isUndefined(value) || value === '' || value === null || value !== value;
26389 this.$$updateEmptyClasses = function(value) {
26390 if (ctrl.$isEmpty(value)) {
26391 $animate.removeClass($element, NOT_EMPTY_CLASS);
26392 $animate.addClass($element, EMPTY_CLASS);
26394 $animate.removeClass($element, EMPTY_CLASS);
26395 $animate.addClass($element, NOT_EMPTY_CLASS);
26400 var currentValidationRunId = 0;
26404 * @name ngModel.NgModelController#$setValidity
26407 * Change the validity state, and notify the form.
26409 * This method can be called within $parsers/$formatters or a custom validation implementation.
26410 * However, in most cases it should be sufficient to use the `ngModel.$validators` and
26411 * `ngModel.$asyncValidators` collections which will call `$setValidity` automatically.
26413 * @param {string} validationErrorKey Name of the validator. The `validationErrorKey` will be assigned
26414 * to either `$error[validationErrorKey]` or `$pending[validationErrorKey]`
26415 * (for unfulfilled `$asyncValidators`), so that it is available for data-binding.
26416 * The `validationErrorKey` should be in camelCase and will get converted into dash-case
26417 * for class name. Example: `myError` will result in `ng-valid-my-error` and `ng-invalid-my-error`
26418 * class and can be bound to as `{{someForm.someControl.$error.myError}}` .
26419 * @param {boolean} isValid Whether the current state is valid (true), invalid (false), pending (undefined),
26420 * or skipped (null). Pending is used for unfulfilled `$asyncValidators`.
26421 * Skipped is used by Angular when validators do not run because of parse errors and
26422 * when `$asyncValidators` do not run because any of the `$validators` failed.
26424 addSetValidityMethod({
26426 $element: $element,
26427 set: function(object, property) {
26428 object[property] = true;
26430 unset: function(object, property) {
26431 delete object[property];
26438 * @name ngModel.NgModelController#$setPristine
26441 * Sets the control to its pristine state.
26443 * This method can be called to remove the `ng-dirty` class and set the control to its pristine
26444 * state (`ng-pristine` class). A model is considered to be pristine when the control
26445 * has not been changed from when first compiled.
26447 this.$setPristine = function() {
26448 ctrl.$dirty = false;
26449 ctrl.$pristine = true;
26450 $animate.removeClass($element, DIRTY_CLASS);
26451 $animate.addClass($element, PRISTINE_CLASS);
26456 * @name ngModel.NgModelController#$setDirty
26459 * Sets the control to its dirty state.
26461 * This method can be called to remove the `ng-pristine` class and set the control to its dirty
26462 * state (`ng-dirty` class). A model is considered to be dirty when the control has been changed
26463 * from when first compiled.
26465 this.$setDirty = function() {
26466 ctrl.$dirty = true;
26467 ctrl.$pristine = false;
26468 $animate.removeClass($element, PRISTINE_CLASS);
26469 $animate.addClass($element, DIRTY_CLASS);
26470 ctrl.$$parentForm.$setDirty();
26475 * @name ngModel.NgModelController#$setUntouched
26478 * Sets the control to its untouched state.
26480 * This method can be called to remove the `ng-touched` class and set the control to its
26481 * untouched state (`ng-untouched` class). Upon compilation, a model is set as untouched
26482 * by default, however this function can be used to restore that state if the model has
26483 * already been touched by the user.
26485 this.$setUntouched = function() {
26486 ctrl.$touched = false;
26487 ctrl.$untouched = true;
26488 $animate.setClass($element, UNTOUCHED_CLASS, TOUCHED_CLASS);
26493 * @name ngModel.NgModelController#$setTouched
26496 * Sets the control to its touched state.
26498 * This method can be called to remove the `ng-untouched` class and set the control to its
26499 * touched state (`ng-touched` class). A model is considered to be touched when the user has
26500 * first focused the control element and then shifted focus away from the control (blur event).
26502 this.$setTouched = function() {
26503 ctrl.$touched = true;
26504 ctrl.$untouched = false;
26505 $animate.setClass($element, TOUCHED_CLASS, UNTOUCHED_CLASS);
26510 * @name ngModel.NgModelController#$rollbackViewValue
26513 * Cancel an update and reset the input element's value to prevent an update to the `$modelValue`,
26514 * which may be caused by a pending debounced event or because the input is waiting for a some
26517 * If you have an input that uses `ng-model-options` to set up debounced updates or updates that
26518 * depend on special events such as blur, you can have a situation where there is a period when
26519 * the `$viewValue` is out of sync with the ngModel's `$modelValue`.
26521 * In this case, you can use `$rollbackViewValue()` to manually cancel the debounced / future update
26522 * and reset the input to the last committed view value.
26524 * It is also possible that you run into difficulties if you try to update the ngModel's `$modelValue`
26525 * programmatically before these debounced/future events have resolved/occurred, because Angular's
26526 * dirty checking mechanism is not able to tell whether the model has actually changed or not.
26528 * The `$rollbackViewValue()` method should be called before programmatically changing the model of an
26529 * input which may have such events pending. This is important in order to make sure that the
26530 * input field will be updated with the new model value and any pending operations are cancelled.
26532 * <example name="ng-model-cancel-update" module="cancel-update-example">
26533 * <file name="app.js">
26534 * angular.module('cancel-update-example', [])
26536 * .controller('CancelUpdateController', ['$scope', function($scope) {
26537 * $scope.model = {};
26539 * $scope.setEmpty = function(e, value, rollback) {
26540 * if (e.keyCode == 27) {
26541 * e.preventDefault();
26543 * $scope.myForm[value].$rollbackViewValue();
26545 * $scope.model[value] = '';
26550 * <file name="index.html">
26551 * <div ng-controller="CancelUpdateController">
26552 * <p>Both of these inputs are only updated if they are blurred. Hitting escape should
26553 * empty them. Follow these steps and observe the difference:</p>
26555 * <li>Type something in the input. You will see that the model is not yet updated</li>
26556 * <li>Press the Escape key.
26558 * <li> In the first example, nothing happens, because the model is already '', and no
26559 * update is detected. If you blur the input, the model will be set to the current view.
26561 * <li> In the second example, the pending update is cancelled, and the input is set back
26562 * to the last committed view value (''). Blurring the input does nothing.
26568 * <form name="myForm" ng-model-options="{ updateOn: 'blur' }">
26570 * <p id="inputDescription1">Without $rollbackViewValue():</p>
26571 * <input name="value1" aria-describedby="inputDescription1" ng-model="model.value1"
26572 * ng-keydown="setEmpty($event, 'value1')">
26573 * value1: "{{ model.value1 }}"
26577 * <p id="inputDescription2">With $rollbackViewValue():</p>
26578 * <input name="value2" aria-describedby="inputDescription2" ng-model="model.value2"
26579 * ng-keydown="setEmpty($event, 'value2', true)">
26580 * value2: "{{ model.value2 }}"
26585 <file name="style.css">
26587 display: table-cell;
26590 padding-right: 30px;
26596 this.$rollbackViewValue = function() {
26597 $timeout.cancel(pendingDebounce);
26598 ctrl.$viewValue = ctrl.$$lastCommittedViewValue;
26604 * @name ngModel.NgModelController#$validate
26607 * Runs each of the registered validators (first synchronous validators and then
26608 * asynchronous validators).
26609 * If the validity changes to invalid, the model will be set to `undefined`,
26610 * unless {@link ngModelOptions `ngModelOptions.allowInvalid`} is `true`.
26611 * If the validity changes to valid, it will set the model to the last available valid
26612 * `$modelValue`, i.e. either the last parsed value or the last value set from the scope.
26614 this.$validate = function() {
26615 // ignore $validate before model is initialized
26616 if (isNumber(ctrl.$modelValue) && isNaN(ctrl.$modelValue)) {
26620 var viewValue = ctrl.$$lastCommittedViewValue;
26621 // Note: we use the $$rawModelValue as $modelValue might have been
26622 // set to undefined during a view -> model update that found validation
26623 // errors. We can't parse the view here, since that could change
26624 // the model although neither viewValue nor the model on the scope changed
26625 var modelValue = ctrl.$$rawModelValue;
26627 var prevValid = ctrl.$valid;
26628 var prevModelValue = ctrl.$modelValue;
26630 var allowInvalid = ctrl.$options && ctrl.$options.allowInvalid;
26632 ctrl.$$runValidators(modelValue, viewValue, function(allValid) {
26633 // If there was no change in validity, don't update the model
26634 // This prevents changing an invalid modelValue to undefined
26635 if (!allowInvalid && prevValid !== allValid) {
26636 // Note: Don't check ctrl.$valid here, as we could have
26637 // external validators (e.g. calculated on the server),
26638 // that just call $setValidity and need the model value
26639 // to calculate their validity.
26640 ctrl.$modelValue = allValid ? modelValue : undefined;
26642 if (ctrl.$modelValue !== prevModelValue) {
26643 ctrl.$$writeModelToScope();
26650 this.$$runValidators = function(modelValue, viewValue, doneCallback) {
26651 currentValidationRunId++;
26652 var localValidationRunId = currentValidationRunId;
26654 // check parser error
26655 if (!processParseErrors()) {
26656 validationDone(false);
26659 if (!processSyncValidators()) {
26660 validationDone(false);
26663 processAsyncValidators();
26665 function processParseErrors() {
26666 var errorKey = ctrl.$$parserName || 'parse';
26667 if (isUndefined(parserValid)) {
26668 setValidity(errorKey, null);
26670 if (!parserValid) {
26671 forEach(ctrl.$validators, function(v, name) {
26672 setValidity(name, null);
26674 forEach(ctrl.$asyncValidators, function(v, name) {
26675 setValidity(name, null);
26678 // Set the parse error last, to prevent unsetting it, should a $validators key == parserName
26679 setValidity(errorKey, parserValid);
26680 return parserValid;
26685 function processSyncValidators() {
26686 var syncValidatorsValid = true;
26687 forEach(ctrl.$validators, function(validator, name) {
26688 var result = validator(modelValue, viewValue);
26689 syncValidatorsValid = syncValidatorsValid && result;
26690 setValidity(name, result);
26692 if (!syncValidatorsValid) {
26693 forEach(ctrl.$asyncValidators, function(v, name) {
26694 setValidity(name, null);
26701 function processAsyncValidators() {
26702 var validatorPromises = [];
26703 var allValid = true;
26704 forEach(ctrl.$asyncValidators, function(validator, name) {
26705 var promise = validator(modelValue, viewValue);
26706 if (!isPromiseLike(promise)) {
26707 throw ngModelMinErr('nopromise',
26708 "Expected asynchronous validator to return a promise but got '{0}' instead.", promise);
26710 setValidity(name, undefined);
26711 validatorPromises.push(promise.then(function() {
26712 setValidity(name, true);
26715 setValidity(name, false);
26718 if (!validatorPromises.length) {
26719 validationDone(true);
26721 $q.all(validatorPromises).then(function() {
26722 validationDone(allValid);
26727 function setValidity(name, isValid) {
26728 if (localValidationRunId === currentValidationRunId) {
26729 ctrl.$setValidity(name, isValid);
26733 function validationDone(allValid) {
26734 if (localValidationRunId === currentValidationRunId) {
26736 doneCallback(allValid);
26743 * @name ngModel.NgModelController#$commitViewValue
26746 * Commit a pending update to the `$modelValue`.
26748 * Updates may be pending by a debounced event or because the input is waiting for a some future
26749 * event defined in `ng-model-options`. this method is rarely needed as `NgModelController`
26750 * usually handles calling this in response to input events.
26752 this.$commitViewValue = function() {
26753 var viewValue = ctrl.$viewValue;
26755 $timeout.cancel(pendingDebounce);
26757 // If the view value has not changed then we should just exit, except in the case where there is
26758 // a native validator on the element. In this case the validation state may have changed even though
26759 // the viewValue has stayed empty.
26760 if (ctrl.$$lastCommittedViewValue === viewValue && (viewValue !== '' || !ctrl.$$hasNativeValidators)) {
26763 ctrl.$$updateEmptyClasses(viewValue);
26764 ctrl.$$lastCommittedViewValue = viewValue;
26767 if (ctrl.$pristine) {
26770 this.$$parseAndValidate();
26773 this.$$parseAndValidate = function() {
26774 var viewValue = ctrl.$$lastCommittedViewValue;
26775 var modelValue = viewValue;
26776 parserValid = isUndefined(modelValue) ? undefined : true;
26779 for (var i = 0; i < ctrl.$parsers.length; i++) {
26780 modelValue = ctrl.$parsers[i](modelValue);
26781 if (isUndefined(modelValue)) {
26782 parserValid = false;
26787 if (isNumber(ctrl.$modelValue) && isNaN(ctrl.$modelValue)) {
26788 // ctrl.$modelValue has not been touched yet...
26789 ctrl.$modelValue = ngModelGet($scope);
26791 var prevModelValue = ctrl.$modelValue;
26792 var allowInvalid = ctrl.$options && ctrl.$options.allowInvalid;
26793 ctrl.$$rawModelValue = modelValue;
26795 if (allowInvalid) {
26796 ctrl.$modelValue = modelValue;
26797 writeToModelIfNeeded();
26800 // Pass the $$lastCommittedViewValue here, because the cached viewValue might be out of date.
26801 // This can happen if e.g. $setViewValue is called from inside a parser
26802 ctrl.$$runValidators(modelValue, ctrl.$$lastCommittedViewValue, function(allValid) {
26803 if (!allowInvalid) {
26804 // Note: Don't check ctrl.$valid here, as we could have
26805 // external validators (e.g. calculated on the server),
26806 // that just call $setValidity and need the model value
26807 // to calculate their validity.
26808 ctrl.$modelValue = allValid ? modelValue : undefined;
26809 writeToModelIfNeeded();
26813 function writeToModelIfNeeded() {
26814 if (ctrl.$modelValue !== prevModelValue) {
26815 ctrl.$$writeModelToScope();
26820 this.$$writeModelToScope = function() {
26821 ngModelSet($scope, ctrl.$modelValue);
26822 forEach(ctrl.$viewChangeListeners, function(listener) {
26826 $exceptionHandler(e);
26833 * @name ngModel.NgModelController#$setViewValue
26836 * Update the view value.
26838 * This method should be called when a control wants to change the view value; typically,
26839 * this is done from within a DOM event handler. For example, the {@link ng.directive:input input}
26840 * directive calls it when the value of the input changes and {@link ng.directive:select select}
26841 * calls it when an option is selected.
26843 * When `$setViewValue` is called, the new `value` will be staged for committing through the `$parsers`
26844 * and `$validators` pipelines. If there are no special {@link ngModelOptions} specified then the staged
26845 * value sent directly for processing, finally to be applied to `$modelValue` and then the
26846 * **expression** specified in the `ng-model` attribute. Lastly, all the registered change listeners,
26847 * in the `$viewChangeListeners` list, are called.
26849 * In case the {@link ng.directive:ngModelOptions ngModelOptions} directive is used with `updateOn`
26850 * and the `default` trigger is not listed, all those actions will remain pending until one of the
26851 * `updateOn` events is triggered on the DOM element.
26852 * All these actions will be debounced if the {@link ng.directive:ngModelOptions ngModelOptions}
26853 * directive is used with a custom debounce for this particular event.
26854 * Note that a `$digest` is only triggered once the `updateOn` events are fired, or if `debounce`
26855 * is specified, once the timer runs out.
26857 * When used with standard inputs, the view value will always be a string (which is in some cases
26858 * parsed into another type, such as a `Date` object for `input[date]`.)
26859 * However, custom controls might also pass objects to this method. In this case, we should make
26860 * a copy of the object before passing it to `$setViewValue`. This is because `ngModel` does not
26861 * perform a deep watch of objects, it only looks for a change of identity. If you only change
26862 * the property of the object then ngModel will not realize that the object has changed and
26863 * will not invoke the `$parsers` and `$validators` pipelines. For this reason, you should
26864 * not change properties of the copy once it has been passed to `$setViewValue`.
26865 * Otherwise you may cause the model value on the scope to change incorrectly.
26867 * <div class="alert alert-info">
26868 * In any case, the value passed to the method should always reflect the current value
26869 * of the control. For example, if you are calling `$setViewValue` for an input element,
26870 * you should pass the input DOM value. Otherwise, the control and the scope model become
26871 * out of sync. It's also important to note that `$setViewValue` does not call `$render` or change
26872 * the control's DOM value in any way. If we want to change the control's DOM value
26873 * programmatically, we should update the `ngModel` scope expression. Its new value will be
26874 * picked up by the model controller, which will run it through the `$formatters`, `$render` it
26875 * to update the DOM, and finally call `$validate` on it.
26878 * @param {*} value value from the view.
26879 * @param {string} trigger Event that triggered the update.
26881 this.$setViewValue = function(value, trigger) {
26882 ctrl.$viewValue = value;
26883 if (!ctrl.$options || ctrl.$options.updateOnDefault) {
26884 ctrl.$$debounceViewValueCommit(trigger);
26888 this.$$debounceViewValueCommit = function(trigger) {
26889 var debounceDelay = 0,
26890 options = ctrl.$options,
26893 if (options && isDefined(options.debounce)) {
26894 debounce = options.debounce;
26895 if (isNumber(debounce)) {
26896 debounceDelay = debounce;
26897 } else if (isNumber(debounce[trigger])) {
26898 debounceDelay = debounce[trigger];
26899 } else if (isNumber(debounce['default'])) {
26900 debounceDelay = debounce['default'];
26904 $timeout.cancel(pendingDebounce);
26905 if (debounceDelay) {
26906 pendingDebounce = $timeout(function() {
26907 ctrl.$commitViewValue();
26909 } else if ($rootScope.$$phase) {
26910 ctrl.$commitViewValue();
26912 $scope.$apply(function() {
26913 ctrl.$commitViewValue();
26919 // Note: we cannot use a normal scope.$watch as we want to detect the following:
26920 // 1. scope value is 'a'
26921 // 2. user enters 'b'
26922 // 3. ng-change kicks in and reverts scope value to 'a'
26923 // -> scope value did not change since the last digest as
26924 // ng-change executes in apply phase
26925 // 4. view should be changed back to 'a'
26926 $scope.$watch(function ngModelWatch() {
26927 var modelValue = ngModelGet($scope);
26929 // if scope model value and ngModel value are out of sync
26930 // TODO(perf): why not move this to the action fn?
26931 if (modelValue !== ctrl.$modelValue &&
26932 // checks for NaN is needed to allow setting the model to NaN when there's an asyncValidator
26933 (ctrl.$modelValue === ctrl.$modelValue || modelValue === modelValue)
26935 ctrl.$modelValue = ctrl.$$rawModelValue = modelValue;
26936 parserValid = undefined;
26938 var formatters = ctrl.$formatters,
26939 idx = formatters.length;
26941 var viewValue = modelValue;
26943 viewValue = formatters[idx](viewValue);
26945 if (ctrl.$viewValue !== viewValue) {
26946 ctrl.$$updateEmptyClasses(viewValue);
26947 ctrl.$viewValue = ctrl.$$lastCommittedViewValue = viewValue;
26950 ctrl.$$runValidators(modelValue, viewValue, noop);
26967 * The `ngModel` directive binds an `input`,`select`, `textarea` (or custom form control) to a
26968 * property on the scope using {@link ngModel.NgModelController NgModelController},
26969 * which is created and exposed by this directive.
26971 * `ngModel` is responsible for:
26973 * - Binding the view into the model, which other directives such as `input`, `textarea` or `select`
26975 * - Providing validation behavior (i.e. required, number, email, url).
26976 * - Keeping the state of the control (valid/invalid, dirty/pristine, touched/untouched, validation errors).
26977 * - Setting related css classes on the element (`ng-valid`, `ng-invalid`, `ng-dirty`, `ng-pristine`, `ng-touched`,
26978 * `ng-untouched`, `ng-empty`, `ng-not-empty`) including animations.
26979 * - Registering the control with its parent {@link ng.directive:form form}.
26981 * Note: `ngModel` will try to bind to the property given by evaluating the expression on the
26982 * current scope. If the property doesn't already exist on this scope, it will be created
26983 * implicitly and added to the scope.
26985 * For best practices on using `ngModel`, see:
26987 * - [Understanding Scopes](https://github.com/angular/angular.js/wiki/Understanding-Scopes)
26989 * For basic examples, how to use `ngModel`, see:
26991 * - {@link ng.directive:input input}
26992 * - {@link input[text] text}
26993 * - {@link input[checkbox] checkbox}
26994 * - {@link input[radio] radio}
26995 * - {@link input[number] number}
26996 * - {@link input[email] email}
26997 * - {@link input[url] url}
26998 * - {@link input[date] date}
26999 * - {@link input[datetime-local] datetime-local}
27000 * - {@link input[time] time}
27001 * - {@link input[month] month}
27002 * - {@link input[week] week}
27003 * - {@link ng.directive:select select}
27004 * - {@link ng.directive:textarea textarea}
27006 * # Complex Models (objects or collections)
27008 * By default, `ngModel` watches the model by reference, not value. This is important to know when
27009 * binding inputs to models that are objects (e.g. `Date`) or collections (e.g. arrays). If only properties of the
27010 * object or collection change, `ngModel` will not be notified and so the input will not be re-rendered.
27012 * The model must be assigned an entirely new object or collection before a re-rendering will occur.
27014 * Some directives have options that will cause them to use a custom `$watchCollection` on the model expression
27015 * - for example, `ngOptions` will do so when a `track by` clause is included in the comprehension expression or
27016 * if the select is given the `multiple` attribute.
27018 * The `$watchCollection()` method only does a shallow comparison, meaning that changing properties deeper than the
27019 * first level of the object (or only changing the properties of an item in the collection if it's an array) will still
27020 * not trigger a re-rendering of the model.
27023 * The following CSS classes are added and removed on the associated input/select/textarea element
27024 * depending on the validity of the model.
27026 * - `ng-valid`: the model is valid
27027 * - `ng-invalid`: the model is invalid
27028 * - `ng-valid-[key]`: for each valid key added by `$setValidity`
27029 * - `ng-invalid-[key]`: for each invalid key added by `$setValidity`
27030 * - `ng-pristine`: the control hasn't been interacted with yet
27031 * - `ng-dirty`: the control has been interacted with
27032 * - `ng-touched`: the control has been blurred
27033 * - `ng-untouched`: the control hasn't been blurred
27034 * - `ng-pending`: any `$asyncValidators` are unfulfilled
27035 * - `ng-empty`: the view does not contain a value or the value is deemed "empty", as defined
27036 * by the {@link ngModel.NgModelController#$isEmpty} method
27037 * - `ng-not-empty`: the view contains a non-empty value
27039 * Keep in mind that ngAnimate can detect each of these classes when added and removed.
27041 * ## Animation Hooks
27043 * Animations within models are triggered when any of the associated CSS classes are added and removed
27044 * on the input element which is attached to the model. These classes include: `.ng-pristine`, `.ng-dirty`,
27045 * `.ng-invalid` and `.ng-valid` as well as any other validations that are performed on the model itself.
27046 * The animations that are triggered within ngModel are similar to how they work in ngClass and
27047 * animations can be hooked into using CSS transitions, keyframes as well as JS animations.
27049 * The following example shows a simple way to utilize CSS transitions to style an input element
27050 * that has been rendered as invalid after it has been validated:
27053 * //be sure to include ngAnimate as a module to hook into more
27054 * //advanced animations
27056 * transition:0.5s linear all;
27057 * background: white;
27059 * .my-input.ng-invalid {
27066 * <example deps="angular-animate.js" animations="true" fixBase="true" module="inputExample">
27067 <file name="index.html">
27069 angular.module('inputExample', [])
27070 .controller('ExampleController', ['$scope', function($scope) {
27076 transition:all linear 0.5s;
27077 background: transparent;
27079 .my-input.ng-invalid {
27084 <p id="inputDescription">
27085 Update input to see transitions when valid/invalid.
27086 Integer is a valid value.
27088 <form name="testForm" ng-controller="ExampleController">
27089 <input ng-model="val" ng-pattern="/^\d+$/" name="anim" class="my-input"
27090 aria-describedby="inputDescription" />
27095 * ## Binding to a getter/setter
27097 * Sometimes it's helpful to bind `ngModel` to a getter/setter function. A getter/setter is a
27098 * function that returns a representation of the model when called with zero arguments, and sets
27099 * the internal state of a model when called with an argument. It's sometimes useful to use this
27100 * for models that have an internal representation that's different from what the model exposes
27103 * <div class="alert alert-success">
27104 * **Best Practice:** It's best to keep getters fast because Angular is likely to call them more
27105 * frequently than other parts of your code.
27108 * You use this behavior by adding `ng-model-options="{ getterSetter: true }"` to an element that
27109 * has `ng-model` attached to it. You can also add `ng-model-options="{ getterSetter: true }"` to
27110 * a `<form>`, which will enable this behavior for all `<input>`s within it. See
27111 * {@link ng.directive:ngModelOptions `ngModelOptions`} for more.
27113 * The following example shows how to use `ngModel` with a getter/setter:
27116 * <example name="ngModel-getter-setter" module="getterSetterExample">
27117 <file name="index.html">
27118 <div ng-controller="ExampleController">
27119 <form name="userForm">
27121 <input type="text" name="userName"
27122 ng-model="user.name"
27123 ng-model-options="{ getterSetter: true }" />
27126 <pre>user.name = <span ng-bind="user.name()"></span></pre>
27129 <file name="app.js">
27130 angular.module('getterSetterExample', [])
27131 .controller('ExampleController', ['$scope', function($scope) {
27132 var _name = 'Brian';
27134 name: function(newName) {
27135 // Note that newName can be undefined for two reasons:
27136 // 1. Because it is called as a getter and thus called with no arguments
27137 // 2. Because the property should actually be set to undefined. This happens e.g. if the
27138 // input is invalid
27139 return arguments.length ? (_name = newName) : _name;
27146 var ngModelDirective = ['$rootScope', function($rootScope) {
27149 require: ['ngModel', '^?form', '^?ngModelOptions'],
27150 controller: NgModelController,
27151 // Prelink needs to run before any input directive
27152 // so that we can set the NgModelOptions in NgModelController
27153 // before anyone else uses it.
27155 compile: function ngModelCompile(element) {
27156 // Setup initial state of the control
27157 element.addClass(PRISTINE_CLASS).addClass(UNTOUCHED_CLASS).addClass(VALID_CLASS);
27160 pre: function ngModelPreLink(scope, element, attr, ctrls) {
27161 var modelCtrl = ctrls[0],
27162 formCtrl = ctrls[1] || modelCtrl.$$parentForm;
27164 modelCtrl.$$setOptions(ctrls[2] && ctrls[2].$options);
27166 // notify others, especially parent forms
27167 formCtrl.$addControl(modelCtrl);
27169 attr.$observe('name', function(newValue) {
27170 if (modelCtrl.$name !== newValue) {
27171 modelCtrl.$$parentForm.$$renameControl(modelCtrl, newValue);
27175 scope.$on('$destroy', function() {
27176 modelCtrl.$$parentForm.$removeControl(modelCtrl);
27179 post: function ngModelPostLink(scope, element, attr, ctrls) {
27180 var modelCtrl = ctrls[0];
27181 if (modelCtrl.$options && modelCtrl.$options.updateOn) {
27182 element.on(modelCtrl.$options.updateOn, function(ev) {
27183 modelCtrl.$$debounceViewValueCommit(ev && ev.type);
27187 element.on('blur', function() {
27188 if (modelCtrl.$touched) return;
27190 if ($rootScope.$$phase) {
27191 scope.$evalAsync(modelCtrl.$setTouched);
27193 scope.$apply(modelCtrl.$setTouched);
27202 var DEFAULT_REGEXP = /(\s+|^)default(\s+|$)/;
27206 * @name ngModelOptions
27209 * Allows tuning how model updates are done. Using `ngModelOptions` you can specify a custom list of
27210 * events that will trigger a model update and/or a debouncing delay so that the actual update only
27211 * takes place when a timer expires; this timer will be reset after another change takes place.
27213 * Given the nature of `ngModelOptions`, the value displayed inside input fields in the view might
27214 * be different from the value in the actual model. This means that if you update the model you
27215 * should also invoke {@link ngModel.NgModelController `$rollbackViewValue`} on the relevant input field in
27216 * order to make sure it is synchronized with the model and that any debounced action is canceled.
27218 * The easiest way to reference the control's {@link ngModel.NgModelController `$rollbackViewValue`}
27219 * method is by making sure the input is placed inside a form that has a `name` attribute. This is
27220 * important because `form` controllers are published to the related scope under the name in their
27221 * `name` attribute.
27223 * Any pending changes will take place immediately when an enclosing form is submitted via the
27224 * `submit` event. Note that `ngClick` events will occur before the model is updated. Use `ngSubmit`
27225 * to have access to the updated model.
27227 * `ngModelOptions` has an effect on the element it's declared on and its descendants.
27229 * @param {Object} ngModelOptions options to apply to the current model. Valid keys are:
27230 * - `updateOn`: string specifying which event should the input be bound to. You can set several
27231 * events using an space delimited list. There is a special event called `default` that
27232 * matches the default events belonging of the control.
27233 * - `debounce`: integer value which contains the debounce model update value in milliseconds. A
27234 * value of 0 triggers an immediate update. If an object is supplied instead, you can specify a
27235 * custom value for each event. For example:
27236 * `ng-model-options="{ updateOn: 'default blur', debounce: { 'default': 500, 'blur': 0 } }"`
27237 * - `allowInvalid`: boolean value which indicates that the model can be set with values that did
27238 * not validate correctly instead of the default behavior of setting the model to undefined.
27239 * - `getterSetter`: boolean value which determines whether or not to treat functions bound to
27240 `ngModel` as getters/setters.
27241 * - `timezone`: Defines the timezone to be used to read/write the `Date` instance in the model for
27242 * `<input type="date">`, `<input type="time">`, ... . It understands UTC/GMT and the
27243 * continental US time zone abbreviations, but for general use, use a time zone offset, for
27244 * example, `'+0430'` (4 hours, 30 minutes east of the Greenwich meridian)
27245 * If not specified, the timezone of the browser will be used.
27249 The following example shows how to override immediate updates. Changes on the inputs within the
27250 form will update the model only when the control loses focus (blur event). If `escape` key is
27251 pressed while the input field is focused, the value is reset to the value in the current model.
27253 <example name="ngModelOptions-directive-blur" module="optionsExample">
27254 <file name="index.html">
27255 <div ng-controller="ExampleController">
27256 <form name="userForm">
27258 <input type="text" name="userName"
27259 ng-model="user.name"
27260 ng-model-options="{ updateOn: 'blur' }"
27261 ng-keyup="cancel($event)" />
27264 <input type="text" ng-model="user.data" />
27267 <pre>user.name = <span ng-bind="user.name"></span></pre>
27268 <pre>user.data = <span ng-bind="user.data"></span></pre>
27271 <file name="app.js">
27272 angular.module('optionsExample', [])
27273 .controller('ExampleController', ['$scope', function($scope) {
27274 $scope.user = { name: 'John', data: '' };
27276 $scope.cancel = function(e) {
27277 if (e.keyCode == 27) {
27278 $scope.userForm.userName.$rollbackViewValue();
27283 <file name="protractor.js" type="protractor">
27284 var model = element(by.binding('user.name'));
27285 var input = element(by.model('user.name'));
27286 var other = element(by.model('user.data'));
27288 it('should allow custom events', function() {
27289 input.sendKeys(' Doe');
27291 expect(model.getText()).toEqual('John');
27293 expect(model.getText()).toEqual('John Doe');
27296 it('should $rollbackViewValue when model changes', function() {
27297 input.sendKeys(' Doe');
27298 expect(input.getAttribute('value')).toEqual('John Doe');
27299 input.sendKeys(protractor.Key.ESCAPE);
27300 expect(input.getAttribute('value')).toEqual('John');
27302 expect(model.getText()).toEqual('John');
27307 This one shows how to debounce model changes. Model will be updated only 1 sec after last change.
27308 If the `Clear` button is pressed, any debounced action is canceled and the value becomes empty.
27310 <example name="ngModelOptions-directive-debounce" module="optionsExample">
27311 <file name="index.html">
27312 <div ng-controller="ExampleController">
27313 <form name="userForm">
27315 <input type="text" name="userName"
27316 ng-model="user.name"
27317 ng-model-options="{ debounce: 1000 }" />
27319 <button ng-click="userForm.userName.$rollbackViewValue(); user.name=''">Clear</button>
27322 <pre>user.name = <span ng-bind="user.name"></span></pre>
27325 <file name="app.js">
27326 angular.module('optionsExample', [])
27327 .controller('ExampleController', ['$scope', function($scope) {
27328 $scope.user = { name: 'Igor' };
27333 This one shows how to bind to getter/setters:
27335 <example name="ngModelOptions-directive-getter-setter" module="getterSetterExample">
27336 <file name="index.html">
27337 <div ng-controller="ExampleController">
27338 <form name="userForm">
27340 <input type="text" name="userName"
27341 ng-model="user.name"
27342 ng-model-options="{ getterSetter: true }" />
27345 <pre>user.name = <span ng-bind="user.name()"></span></pre>
27348 <file name="app.js">
27349 angular.module('getterSetterExample', [])
27350 .controller('ExampleController', ['$scope', function($scope) {
27351 var _name = 'Brian';
27353 name: function(newName) {
27354 // Note that newName can be undefined for two reasons:
27355 // 1. Because it is called as a getter and thus called with no arguments
27356 // 2. Because the property should actually be set to undefined. This happens e.g. if the
27357 // input is invalid
27358 return arguments.length ? (_name = newName) : _name;
27365 var ngModelOptionsDirective = function() {
27368 controller: ['$scope', '$attrs', function($scope, $attrs) {
27370 this.$options = copy($scope.$eval($attrs.ngModelOptions));
27371 // Allow adding/overriding bound events
27372 if (isDefined(this.$options.updateOn)) {
27373 this.$options.updateOnDefault = false;
27374 // extract "default" pseudo-event from list of events that can trigger a model update
27375 this.$options.updateOn = trim(this.$options.updateOn.replace(DEFAULT_REGEXP, function() {
27376 that.$options.updateOnDefault = true;
27380 this.$options.updateOnDefault = true;
27389 function addSetValidityMethod(context) {
27390 var ctrl = context.ctrl,
27391 $element = context.$element,
27394 unset = context.unset,
27395 $animate = context.$animate;
27397 classCache[INVALID_CLASS] = !(classCache[VALID_CLASS] = $element.hasClass(VALID_CLASS));
27399 ctrl.$setValidity = setValidity;
27401 function setValidity(validationErrorKey, state, controller) {
27402 if (isUndefined(state)) {
27403 createAndSet('$pending', validationErrorKey, controller);
27405 unsetAndCleanup('$pending', validationErrorKey, controller);
27407 if (!isBoolean(state)) {
27408 unset(ctrl.$error, validationErrorKey, controller);
27409 unset(ctrl.$$success, validationErrorKey, controller);
27412 unset(ctrl.$error, validationErrorKey, controller);
27413 set(ctrl.$$success, validationErrorKey, controller);
27415 set(ctrl.$error, validationErrorKey, controller);
27416 unset(ctrl.$$success, validationErrorKey, controller);
27419 if (ctrl.$pending) {
27420 cachedToggleClass(PENDING_CLASS, true);
27421 ctrl.$valid = ctrl.$invalid = undefined;
27422 toggleValidationCss('', null);
27424 cachedToggleClass(PENDING_CLASS, false);
27425 ctrl.$valid = isObjectEmpty(ctrl.$error);
27426 ctrl.$invalid = !ctrl.$valid;
27427 toggleValidationCss('', ctrl.$valid);
27430 // re-read the state as the set/unset methods could have
27431 // combined state in ctrl.$error[validationError] (used for forms),
27432 // where setting/unsetting only increments/decrements the value,
27433 // and does not replace it.
27435 if (ctrl.$pending && ctrl.$pending[validationErrorKey]) {
27436 combinedState = undefined;
27437 } else if (ctrl.$error[validationErrorKey]) {
27438 combinedState = false;
27439 } else if (ctrl.$$success[validationErrorKey]) {
27440 combinedState = true;
27442 combinedState = null;
27445 toggleValidationCss(validationErrorKey, combinedState);
27446 ctrl.$$parentForm.$setValidity(validationErrorKey, combinedState, ctrl);
27449 function createAndSet(name, value, controller) {
27453 set(ctrl[name], value, controller);
27456 function unsetAndCleanup(name, value, controller) {
27458 unset(ctrl[name], value, controller);
27460 if (isObjectEmpty(ctrl[name])) {
27461 ctrl[name] = undefined;
27465 function cachedToggleClass(className, switchValue) {
27466 if (switchValue && !classCache[className]) {
27467 $animate.addClass($element, className);
27468 classCache[className] = true;
27469 } else if (!switchValue && classCache[className]) {
27470 $animate.removeClass($element, className);
27471 classCache[className] = false;
27475 function toggleValidationCss(validationErrorKey, isValid) {
27476 validationErrorKey = validationErrorKey ? '-' + snake_case(validationErrorKey, '-') : '';
27478 cachedToggleClass(VALID_CLASS + validationErrorKey, isValid === true);
27479 cachedToggleClass(INVALID_CLASS + validationErrorKey, isValid === false);
27483 function isObjectEmpty(obj) {
27485 for (var prop in obj) {
27486 if (obj.hasOwnProperty(prop)) {
27496 * @name ngNonBindable
27501 * The `ngNonBindable` directive tells Angular not to compile or bind the contents of the current
27502 * DOM element. This is useful if the element contains what appears to be Angular directives and
27503 * bindings but which should be ignored by Angular. This could be the case if you have a site that
27504 * displays snippets of code, for instance.
27509 * In this example there are two locations where a simple interpolation binding (`{{}}`) is present,
27510 * but the one wrapped in `ngNonBindable` is left alone.
27514 <file name="index.html">
27515 <div>Normal: {{1 + 2}}</div>
27516 <div ng-non-bindable>Ignored: {{1 + 2}}</div>
27518 <file name="protractor.js" type="protractor">
27519 it('should check ng-non-bindable', function() {
27520 expect(element(by.binding('1 + 2')).getText()).toContain('3');
27521 expect(element.all(by.css('div')).last().getText()).toMatch(/1 \+ 2/);
27526 var ngNonBindableDirective = ngDirective({ terminal: true, priority: 1000 });
27528 /* global jqLiteRemove */
27530 var ngOptionsMinErr = minErr('ngOptions');
27539 * The `ngOptions` attribute can be used to dynamically generate a list of `<option>`
27540 * elements for the `<select>` element using the array or object obtained by evaluating the
27541 * `ngOptions` comprehension expression.
27543 * In many cases, `ngRepeat` can be used on `<option>` elements instead of `ngOptions` to achieve a
27544 * similar result. However, `ngOptions` provides some benefits such as reducing memory and
27545 * increasing speed by not creating a new scope for each repeated instance, as well as providing
27546 * more flexibility in how the `<select>`'s model is assigned via the `select` **`as`** part of the
27547 * comprehension expression. `ngOptions` should be used when the `<select>` model needs to be bound
27548 * to a non-string value. This is because an option element can only be bound to string values at
27551 * When an item in the `<select>` menu is selected, the array element or object property
27552 * represented by the selected option will be bound to the model identified by the `ngModel`
27555 * Optionally, a single hard-coded `<option>` element, with the value set to an empty string, can
27556 * be nested into the `<select>` element. This element will then represent the `null` or "not selected"
27557 * option. See example below for demonstration.
27559 * ## Complex Models (objects or collections)
27561 * By default, `ngModel` watches the model by reference, not value. This is important to know when
27562 * binding the select to a model that is an object or a collection.
27564 * One issue occurs if you want to preselect an option. For example, if you set
27565 * the model to an object that is equal to an object in your collection, `ngOptions` won't be able to set the selection,
27566 * because the objects are not identical. So by default, you should always reference the item in your collection
27567 * for preselections, e.g.: `$scope.selected = $scope.collection[3]`.
27569 * Another solution is to use a `track by` clause, because then `ngOptions` will track the identity
27570 * of the item not by reference, but by the result of the `track by` expression. For example, if your
27571 * collection items have an id property, you would `track by item.id`.
27573 * A different issue with objects or collections is that ngModel won't detect if an object property or
27574 * a collection item changes. For that reason, `ngOptions` additionally watches the model using
27575 * `$watchCollection`, when the expression contains a `track by` clause or the the select has the `multiple` attribute.
27576 * This allows ngOptions to trigger a re-rendering of the options even if the actual object/collection
27577 * has not changed identity, but only a property on the object or an item in the collection changes.
27579 * Note that `$watchCollection` does a shallow comparison of the properties of the object (or the items in the collection
27580 * if the model is an array). This means that changing a property deeper than the first level inside the
27581 * object/collection will not trigger a re-rendering.
27583 * ## `select` **`as`**
27585 * Using `select` **`as`** will bind the result of the `select` expression to the model, but
27586 * the value of the `<select>` and `<option>` html elements will be either the index (for array data sources)
27587 * or property name (for object data sources) of the value within the collection. If a **`track by`** expression
27588 * is used, the result of that expression will be set as the value of the `option` and `select` elements.
27591 * ### `select` **`as`** and **`track by`**
27593 * <div class="alert alert-warning">
27594 * Be careful when using `select` **`as`** and **`track by`** in the same expression.
27597 * Given this array of items on the $scope:
27600 * $scope.items = [{
27603 * subItem: { name: 'aSubItem' }
27607 * subItem: { name: 'bSubItem' }
27614 * <select ng-options="item as item.label for item in items track by item.id" ng-model="selected"></select>
27617 * $scope.selected = $scope.items[0];
27620 * but this will not work:
27623 * <select ng-options="item.subItem as item.label for item in items track by item.id" ng-model="selected"></select>
27626 * $scope.selected = $scope.items[0].subItem;
27629 * In both examples, the **`track by`** expression is applied successfully to each `item` in the
27630 * `items` array. Because the selected option has been set programmatically in the controller, the
27631 * **`track by`** expression is also applied to the `ngModel` value. In the first example, the
27632 * `ngModel` value is `items[0]` and the **`track by`** expression evaluates to `items[0].id` with
27633 * no issue. In the second example, the `ngModel` value is `items[0].subItem` and the **`track by`**
27634 * expression evaluates to `items[0].subItem.id` (which is undefined). As a result, the model value
27635 * is not matched against any `<option>` and the `<select>` appears as having no selected value.
27638 * @param {string} ngModel Assignable angular expression to data-bind to.
27639 * @param {string=} name Property name of the form under which the control is published.
27640 * @param {string=} required The control is considered valid only if value is entered.
27641 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
27642 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
27643 * `required` when you want to data-bind to the `required` attribute.
27644 * @param {comprehension_expression=} ngOptions in one of the following forms:
27646 * * for array data sources:
27647 * * `label` **`for`** `value` **`in`** `array`
27648 * * `select` **`as`** `label` **`for`** `value` **`in`** `array`
27649 * * `label` **`group by`** `group` **`for`** `value` **`in`** `array`
27650 * * `label` **`disable when`** `disable` **`for`** `value` **`in`** `array`
27651 * * `label` **`group by`** `group` **`for`** `value` **`in`** `array` **`track by`** `trackexpr`
27652 * * `label` **`disable when`** `disable` **`for`** `value` **`in`** `array` **`track by`** `trackexpr`
27653 * * `label` **`for`** `value` **`in`** `array` | orderBy:`orderexpr` **`track by`** `trackexpr`
27654 * (for including a filter with `track by`)
27655 * * for object data sources:
27656 * * `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
27657 * * `select` **`as`** `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
27658 * * `label` **`group by`** `group` **`for (`**`key`**`,`** `value`**`) in`** `object`
27659 * * `label` **`disable when`** `disable` **`for (`**`key`**`,`** `value`**`) in`** `object`
27660 * * `select` **`as`** `label` **`group by`** `group`
27661 * **`for` `(`**`key`**`,`** `value`**`) in`** `object`
27662 * * `select` **`as`** `label` **`disable when`** `disable`
27663 * **`for` `(`**`key`**`,`** `value`**`) in`** `object`
27667 * * `array` / `object`: an expression which evaluates to an array / object to iterate over.
27668 * * `value`: local variable which will refer to each item in the `array` or each property value
27669 * of `object` during iteration.
27670 * * `key`: local variable which will refer to a property name in `object` during iteration.
27671 * * `label`: The result of this expression will be the label for `<option>` element. The
27672 * `expression` will most likely refer to the `value` variable (e.g. `value.propertyName`).
27673 * * `select`: The result of this expression will be bound to the model of the parent `<select>`
27674 * element. If not specified, `select` expression will default to `value`.
27675 * * `group`: The result of this expression will be used to group options using the `<optgroup>`
27677 * * `disable`: The result of this expression will be used to disable the rendered `<option>`
27678 * element. Return `true` to disable.
27679 * * `trackexpr`: Used when working with an array of objects. The result of this expression will be
27680 * used to identify the objects in the array. The `trackexpr` will most likely refer to the
27681 * `value` variable (e.g. `value.propertyName`). With this the selection is preserved
27682 * even when the options are recreated (e.g. reloaded from the server).
27685 <example module="selectExample">
27686 <file name="index.html">
27688 angular.module('selectExample', [])
27689 .controller('ExampleController', ['$scope', function($scope) {
27691 {name:'black', shade:'dark'},
27692 {name:'white', shade:'light', notAnOption: true},
27693 {name:'red', shade:'dark'},
27694 {name:'blue', shade:'dark', notAnOption: true},
27695 {name:'yellow', shade:'light', notAnOption: false}
27697 $scope.myColor = $scope.colors[2]; // red
27700 <div ng-controller="ExampleController">
27702 <li ng-repeat="color in colors">
27703 <label>Name: <input ng-model="color.name"></label>
27704 <label><input type="checkbox" ng-model="color.notAnOption"> Disabled?</label>
27705 <button ng-click="colors.splice($index, 1)" aria-label="Remove">X</button>
27708 <button ng-click="colors.push({})">add</button>
27712 <label>Color (null not allowed):
27713 <select ng-model="myColor" ng-options="color.name for color in colors"></select>
27715 <label>Color (null allowed):
27716 <span class="nullable">
27717 <select ng-model="myColor" ng-options="color.name for color in colors">
27718 <option value="">-- choose color --</option>
27720 </span></label><br/>
27722 <label>Color grouped by shade:
27723 <select ng-model="myColor" ng-options="color.name group by color.shade for color in colors">
27727 <label>Color grouped by shade, with some disabled:
27728 <select ng-model="myColor"
27729 ng-options="color.name group by color.shade disable when color.notAnOption for color in colors">
27735 Select <button ng-click="myColor = { name:'not in list', shade: 'other' }">bogus</button>.
27738 Currently selected: {{ {selected_color:myColor} }}
27739 <div style="border:solid 1px black; height:20px"
27740 ng-style="{'background-color':myColor.name}">
27744 <file name="protractor.js" type="protractor">
27745 it('should check ng-options', function() {
27746 expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('red');
27747 element.all(by.model('myColor')).first().click();
27748 element.all(by.css('select[ng-model="myColor"] option')).first().click();
27749 expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('black');
27750 element(by.css('.nullable select[ng-model="myColor"]')).click();
27751 element.all(by.css('.nullable select[ng-model="myColor"] option')).first().click();
27752 expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('null');
27758 // jshint maxlen: false
27759 // //00001111111111000000000002222222222000000000000000000000333333333300000000000000000000000004444444444400000000000005555555555555550000000006666666666666660000000777777777777777000000000000000888888888800000000000000000009999999999
27760 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]+?))?$/;
27761 // 1: value expression (valueFn)
27762 // 2: label expression (displayFn)
27763 // 3: group by expression (groupByFn)
27764 // 4: disable when expression (disableWhenFn)
27765 // 5: array item variable name
27766 // 6: object item key variable name
27767 // 7: object item value variable name
27768 // 8: collection expression
27769 // 9: track by expression
27770 // jshint maxlen: 100
27773 var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile, $document, $parse) {
27775 function parseOptionsExpression(optionsExp, selectElement, scope) {
27777 var match = optionsExp.match(NG_OPTIONS_REGEXP);
27779 throw ngOptionsMinErr('iexp',
27780 "Expected expression in form of " +
27781 "'_select_ (as _label_)? for (_key_,)?_value_ in _collection_'" +
27782 " but got '{0}'. Element: {1}",
27783 optionsExp, startingTag(selectElement));
27786 // Extract the parts from the ngOptions expression
27788 // The variable name for the value of the item in the collection
27789 var valueName = match[5] || match[7];
27790 // The variable name for the key of the item in the collection
27791 var keyName = match[6];
27793 // An expression that generates the viewValue for an option if there is a label expression
27794 var selectAs = / as /.test(match[0]) && match[1];
27795 // An expression that is used to track the id of each object in the options collection
27796 var trackBy = match[9];
27797 // An expression that generates the viewValue for an option if there is no label expression
27798 var valueFn = $parse(match[2] ? match[1] : valueName);
27799 var selectAsFn = selectAs && $parse(selectAs);
27800 var viewValueFn = selectAsFn || valueFn;
27801 var trackByFn = trackBy && $parse(trackBy);
27803 // Get the value by which we are going to track the option
27804 // if we have a trackFn then use that (passing scope and locals)
27805 // otherwise just hash the given viewValue
27806 var getTrackByValueFn = trackBy ?
27807 function(value, locals) { return trackByFn(scope, locals); } :
27808 function getHashOfValue(value) { return hashKey(value); };
27809 var getTrackByValue = function(value, key) {
27810 return getTrackByValueFn(value, getLocals(value, key));
27813 var displayFn = $parse(match[2] || match[1]);
27814 var groupByFn = $parse(match[3] || '');
27815 var disableWhenFn = $parse(match[4] || '');
27816 var valuesFn = $parse(match[8]);
27819 var getLocals = keyName ? function(value, key) {
27820 locals[keyName] = key;
27821 locals[valueName] = value;
27823 } : function(value) {
27824 locals[valueName] = value;
27829 function Option(selectValue, viewValue, label, group, disabled) {
27830 this.selectValue = selectValue;
27831 this.viewValue = viewValue;
27832 this.label = label;
27833 this.group = group;
27834 this.disabled = disabled;
27837 function getOptionValuesKeys(optionValues) {
27838 var optionValuesKeys;
27840 if (!keyName && isArrayLike(optionValues)) {
27841 optionValuesKeys = optionValues;
27843 // if object, extract keys, in enumeration order, unsorted
27844 optionValuesKeys = [];
27845 for (var itemKey in optionValues) {
27846 if (optionValues.hasOwnProperty(itemKey) && itemKey.charAt(0) !== '$') {
27847 optionValuesKeys.push(itemKey);
27851 return optionValuesKeys;
27856 getTrackByValue: getTrackByValue,
27857 getWatchables: $parse(valuesFn, function(optionValues) {
27858 // Create a collection of things that we would like to watch (watchedArray)
27859 // so that they can all be watched using a single $watchCollection
27860 // that only runs the handler once if anything changes
27861 var watchedArray = [];
27862 optionValues = optionValues || [];
27864 var optionValuesKeys = getOptionValuesKeys(optionValues);
27865 var optionValuesLength = optionValuesKeys.length;
27866 for (var index = 0; index < optionValuesLength; index++) {
27867 var key = (optionValues === optionValuesKeys) ? index : optionValuesKeys[index];
27868 var value = optionValues[key];
27870 var locals = getLocals(value, key);
27871 var selectValue = getTrackByValueFn(value, locals);
27872 watchedArray.push(selectValue);
27874 // Only need to watch the displayFn if there is a specific label expression
27875 if (match[2] || match[1]) {
27876 var label = displayFn(scope, locals);
27877 watchedArray.push(label);
27880 // Only need to watch the disableWhenFn if there is a specific disable expression
27882 var disableWhen = disableWhenFn(scope, locals);
27883 watchedArray.push(disableWhen);
27886 return watchedArray;
27889 getOptions: function() {
27891 var optionItems = [];
27892 var selectValueMap = {};
27894 // The option values were already computed in the `getWatchables` fn,
27895 // which must have been called to trigger `getOptions`
27896 var optionValues = valuesFn(scope) || [];
27897 var optionValuesKeys = getOptionValuesKeys(optionValues);
27898 var optionValuesLength = optionValuesKeys.length;
27900 for (var index = 0; index < optionValuesLength; index++) {
27901 var key = (optionValues === optionValuesKeys) ? index : optionValuesKeys[index];
27902 var value = optionValues[key];
27903 var locals = getLocals(value, key);
27904 var viewValue = viewValueFn(scope, locals);
27905 var selectValue = getTrackByValueFn(viewValue, locals);
27906 var label = displayFn(scope, locals);
27907 var group = groupByFn(scope, locals);
27908 var disabled = disableWhenFn(scope, locals);
27909 var optionItem = new Option(selectValue, viewValue, label, group, disabled);
27911 optionItems.push(optionItem);
27912 selectValueMap[selectValue] = optionItem;
27916 items: optionItems,
27917 selectValueMap: selectValueMap,
27918 getOptionFromViewValue: function(value) {
27919 return selectValueMap[getTrackByValue(value)];
27921 getViewValueFromOption: function(option) {
27922 // If the viewValue could be an object that may be mutated by the application,
27923 // we need to make a copy and not return the reference to the value on the option.
27924 return trackBy ? angular.copy(option.viewValue) : option.viewValue;
27932 // we can't just jqLite('<option>') since jqLite is not smart enough
27933 // to create it in <select> and IE barfs otherwise.
27934 var optionTemplate = window.document.createElement('option'),
27935 optGroupTemplate = window.document.createElement('optgroup');
27937 function ngOptionsPostLink(scope, selectElement, attr, ctrls) {
27939 var selectCtrl = ctrls[0];
27940 var ngModelCtrl = ctrls[1];
27941 var multiple = attr.multiple;
27943 // The emptyOption allows the application developer to provide their own custom "empty"
27944 // option when the viewValue does not match any of the option values.
27946 for (var i = 0, children = selectElement.children(), ii = children.length; i < ii; i++) {
27947 if (children[i].value === '') {
27948 emptyOption = children.eq(i);
27953 var providedEmptyOption = !!emptyOption;
27955 var unknownOption = jqLite(optionTemplate.cloneNode(false));
27956 unknownOption.val('?');
27959 var ngOptions = parseOptionsExpression(attr.ngOptions, selectElement, scope);
27960 // This stores the newly created options before they are appended to the select.
27961 // Since the contents are removed from the fragment when it is appended,
27962 // we only need to create it once.
27963 var listFragment = $document[0].createDocumentFragment();
27965 var renderEmptyOption = function() {
27966 if (!providedEmptyOption) {
27967 selectElement.prepend(emptyOption);
27969 selectElement.val('');
27970 emptyOption.prop('selected', true); // needed for IE
27971 emptyOption.attr('selected', true);
27974 var removeEmptyOption = function() {
27975 if (!providedEmptyOption) {
27976 emptyOption.remove();
27981 var renderUnknownOption = function() {
27982 selectElement.prepend(unknownOption);
27983 selectElement.val('?');
27984 unknownOption.prop('selected', true); // needed for IE
27985 unknownOption.attr('selected', true);
27988 var removeUnknownOption = function() {
27989 unknownOption.remove();
27992 // Update the controller methods for multiple selectable options
27995 selectCtrl.writeValue = function writeNgOptionsValue(value) {
27996 var option = options.getOptionFromViewValue(value);
27999 // Don't update the option when it is already selected.
28000 // For example, the browser will select the first option by default. In that case,
28001 // most properties are set automatically - except the `selected` attribute, which we
28004 if (selectElement[0].value !== option.selectValue) {
28005 removeUnknownOption();
28006 removeEmptyOption();
28008 selectElement[0].value = option.selectValue;
28009 option.element.selected = true;
28012 option.element.setAttribute('selected', 'selected');
28014 if (value === null || providedEmptyOption) {
28015 removeUnknownOption();
28016 renderEmptyOption();
28018 removeEmptyOption();
28019 renderUnknownOption();
28024 selectCtrl.readValue = function readNgOptionsValue() {
28026 var selectedOption = options.selectValueMap[selectElement.val()];
28028 if (selectedOption && !selectedOption.disabled) {
28029 removeEmptyOption();
28030 removeUnknownOption();
28031 return options.getViewValueFromOption(selectedOption);
28036 // If we are using `track by` then we must watch the tracked value on the model
28037 // since ngModel only watches for object identity change
28038 if (ngOptions.trackBy) {
28040 function() { return ngOptions.getTrackByValue(ngModelCtrl.$viewValue); },
28041 function() { ngModelCtrl.$render(); }
28047 ngModelCtrl.$isEmpty = function(value) {
28048 return !value || value.length === 0;
28052 selectCtrl.writeValue = function writeNgOptionsMultiple(value) {
28053 options.items.forEach(function(option) {
28054 option.element.selected = false;
28058 value.forEach(function(item) {
28059 var option = options.getOptionFromViewValue(item);
28060 if (option) option.element.selected = true;
28066 selectCtrl.readValue = function readNgOptionsMultiple() {
28067 var selectedValues = selectElement.val() || [],
28070 forEach(selectedValues, function(value) {
28071 var option = options.selectValueMap[value];
28072 if (option && !option.disabled) selections.push(options.getViewValueFromOption(option));
28078 // If we are using `track by` then we must watch these tracked values on the model
28079 // since ngModel only watches for object identity change
28080 if (ngOptions.trackBy) {
28082 scope.$watchCollection(function() {
28083 if (isArray(ngModelCtrl.$viewValue)) {
28084 return ngModelCtrl.$viewValue.map(function(value) {
28085 return ngOptions.getTrackByValue(value);
28089 ngModelCtrl.$render();
28096 if (providedEmptyOption) {
28098 // we need to remove it before calling selectElement.empty() because otherwise IE will
28099 // remove the label from the element. wtf?
28100 emptyOption.remove();
28102 // compile the element since there might be bindings in it
28103 $compile(emptyOption)(scope);
28105 // remove the class, which is added automatically because we recompile the element and it
28106 // becomes the compilation root
28107 emptyOption.removeClass('ng-scope');
28109 emptyOption = jqLite(optionTemplate.cloneNode(false));
28112 selectElement.empty();
28114 // We need to do this here to ensure that the options object is defined
28115 // when we first hit it in writeNgOptionsValue
28118 // We will re-render the option elements if the option values or labels change
28119 scope.$watchCollection(ngOptions.getWatchables, updateOptions);
28121 // ------------------------------------------------------------------ //
28123 function addOptionElement(option, parent) {
28124 var optionElement = optionTemplate.cloneNode(false);
28125 parent.appendChild(optionElement);
28126 updateOptionElement(option, optionElement);
28130 function updateOptionElement(option, element) {
28131 option.element = element;
28132 element.disabled = option.disabled;
28133 // NOTE: The label must be set before the value, otherwise IE10/11/EDGE create unresponsive
28134 // selects in certain circumstances when multiple selects are next to each other and display
28135 // the option list in listbox style, i.e. the select is [multiple], or specifies a [size].
28136 // See https://github.com/angular/angular.js/issues/11314 for more info.
28137 // This is unfortunately untestable with unit / e2e tests
28138 if (option.label !== element.label) {
28139 element.label = option.label;
28140 element.textContent = option.label;
28142 if (option.value !== element.value) element.value = option.selectValue;
28145 function updateOptions() {
28146 var previousValue = options && selectCtrl.readValue();
28148 // We must remove all current options, but cannot simply set innerHTML = null
28149 // since the providedEmptyOption might have an ngIf on it that inserts comments which we
28151 // Instead, iterate over the current option elements and remove them or their optgroup
28155 for (var i = options.items.length - 1; i >= 0; i--) {
28156 var option = options.items[i];
28157 if (option.group) {
28158 jqLiteRemove(option.element.parentNode);
28160 jqLiteRemove(option.element);
28165 options = ngOptions.getOptions();
28167 var groupElementMap = {};
28169 // Ensure that the empty option is always there if it was explicitly provided
28170 if (providedEmptyOption) {
28171 selectElement.prepend(emptyOption);
28174 options.items.forEach(function addOption(option) {
28177 if (isDefined(option.group)) {
28179 // This option is to live in a group
28180 // See if we have already created this group
28181 groupElement = groupElementMap[option.group];
28183 if (!groupElement) {
28185 groupElement = optGroupTemplate.cloneNode(false);
28186 listFragment.appendChild(groupElement);
28188 // Update the label on the group element
28189 groupElement.label = option.group;
28191 // Store it for use later
28192 groupElementMap[option.group] = groupElement;
28195 addOptionElement(option, groupElement);
28199 // This option is not in a group
28200 addOptionElement(option, listFragment);
28204 selectElement[0].appendChild(listFragment);
28206 ngModelCtrl.$render();
28208 // Check to see if the value has changed due to the update to the options
28209 if (!ngModelCtrl.$isEmpty(previousValue)) {
28210 var nextValue = selectCtrl.readValue();
28211 var isNotPrimitive = ngOptions.trackBy || multiple;
28212 if (isNotPrimitive ? !equals(previousValue, nextValue) : previousValue !== nextValue) {
28213 ngModelCtrl.$setViewValue(nextValue);
28214 ngModelCtrl.$render();
28224 require: ['select', 'ngModel'],
28226 pre: function ngOptionsPreLink(scope, selectElement, attr, ctrls) {
28227 // Deactivate the SelectController.register method to prevent
28228 // option directives from accidentally registering themselves
28229 // (and unwanted $destroy handlers etc.)
28230 ctrls[0].registerOption = noop;
28232 post: ngOptionsPostLink
28239 * @name ngPluralize
28243 * `ngPluralize` is a directive that displays messages according to en-US localization rules.
28244 * These rules are bundled with angular.js, but can be overridden
28245 * (see {@link guide/i18n Angular i18n} dev guide). You configure ngPluralize directive
28246 * by specifying the mappings between
28247 * [plural categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html)
28248 * and the strings to be displayed.
28250 * # Plural categories and explicit number rules
28252 * [plural categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html)
28253 * in Angular's default en-US locale: "one" and "other".
28255 * While a plural category may match many numbers (for example, in en-US locale, "other" can match
28256 * any number that is not 1), an explicit number rule can only match one number. For example, the
28257 * explicit number rule for "3" matches the number 3. There are examples of plural categories
28258 * and explicit number rules throughout the rest of this documentation.
28260 * # Configuring ngPluralize
28261 * You configure ngPluralize by providing 2 attributes: `count` and `when`.
28262 * You can also provide an optional attribute, `offset`.
28264 * The value of the `count` attribute can be either a string or an {@link guide/expression
28265 * Angular expression}; these are evaluated on the current scope for its bound value.
28267 * The `when` attribute specifies the mappings between plural categories and the actual
28268 * string to be displayed. The value of the attribute should be a JSON object.
28270 * The following example shows how to configure ngPluralize:
28273 * <ng-pluralize count="personCount"
28274 when="{'0': 'Nobody is viewing.',
28275 * 'one': '1 person is viewing.',
28276 * 'other': '{} people are viewing.'}">
28280 * In the example, `"0: Nobody is viewing."` is an explicit number rule. If you did not
28281 * specify this rule, 0 would be matched to the "other" category and "0 people are viewing"
28282 * would be shown instead of "Nobody is viewing". You can specify an explicit number rule for
28283 * other numbers, for example 12, so that instead of showing "12 people are viewing", you can
28284 * show "a dozen people are viewing".
28286 * You can use a set of closed braces (`{}`) as a placeholder for the number that you want substituted
28287 * into pluralized strings. In the previous example, Angular will replace `{}` with
28288 * <span ng-non-bindable>`{{personCount}}`</span>. The closed braces `{}` is a placeholder
28289 * for <span ng-non-bindable>{{numberExpression}}</span>.
28291 * If no rule is defined for a category, then an empty string is displayed and a warning is generated.
28292 * Note that some locales define more categories than `one` and `other`. For example, fr-fr defines `few` and `many`.
28294 * # Configuring ngPluralize with offset
28295 * The `offset` attribute allows further customization of pluralized text, which can result in
28296 * a better user experience. For example, instead of the message "4 people are viewing this document",
28297 * you might display "John, Kate and 2 others are viewing this document".
28298 * The offset attribute allows you to offset a number by any desired value.
28299 * Let's take a look at an example:
28302 * <ng-pluralize count="personCount" offset=2
28303 * when="{'0': 'Nobody is viewing.',
28304 * '1': '{{person1}} is viewing.',
28305 * '2': '{{person1}} and {{person2}} are viewing.',
28306 * 'one': '{{person1}}, {{person2}} and one other person are viewing.',
28307 * 'other': '{{person1}}, {{person2}} and {} other people are viewing.'}">
28311 * Notice that we are still using two plural categories(one, other), but we added
28312 * three explicit number rules 0, 1 and 2.
28313 * When one person, perhaps John, views the document, "John is viewing" will be shown.
28314 * When three people view the document, no explicit number rule is found, so
28315 * an offset of 2 is taken off 3, and Angular uses 1 to decide the plural category.
28316 * In this case, plural category 'one' is matched and "John, Mary and one other person are viewing"
28319 * Note that when you specify offsets, you must provide explicit number rules for
28320 * numbers from 0 up to and including the offset. If you use an offset of 3, for example,
28321 * you must provide explicit number rules for 0, 1, 2 and 3. You must also provide plural strings for
28322 * plural categories "one" and "other".
28324 * @param {string|expression} count The variable to be bound to.
28325 * @param {string} when The mapping between plural category to its corresponding strings.
28326 * @param {number=} offset Offset to deduct from the total number.
28329 <example module="pluralizeExample">
28330 <file name="index.html">
28332 angular.module('pluralizeExample', [])
28333 .controller('ExampleController', ['$scope', function($scope) {
28334 $scope.person1 = 'Igor';
28335 $scope.person2 = 'Misko';
28336 $scope.personCount = 1;
28339 <div ng-controller="ExampleController">
28340 <label>Person 1:<input type="text" ng-model="person1" value="Igor" /></label><br/>
28341 <label>Person 2:<input type="text" ng-model="person2" value="Misko" /></label><br/>
28342 <label>Number of People:<input type="text" ng-model="personCount" value="1" /></label><br/>
28344 <!--- Example with simple pluralization rules for en locale --->
28346 <ng-pluralize count="personCount"
28347 when="{'0': 'Nobody is viewing.',
28348 'one': '1 person is viewing.',
28349 'other': '{} people are viewing.'}">
28350 </ng-pluralize><br>
28352 <!--- Example with offset --->
28354 <ng-pluralize count="personCount" offset=2
28355 when="{'0': 'Nobody is viewing.',
28356 '1': '{{person1}} is viewing.',
28357 '2': '{{person1}} and {{person2}} are viewing.',
28358 'one': '{{person1}}, {{person2}} and one other person are viewing.',
28359 'other': '{{person1}}, {{person2}} and {} other people are viewing.'}">
28363 <file name="protractor.js" type="protractor">
28364 it('should show correct pluralized string', function() {
28365 var withoutOffset = element.all(by.css('ng-pluralize')).get(0);
28366 var withOffset = element.all(by.css('ng-pluralize')).get(1);
28367 var countInput = element(by.model('personCount'));
28369 expect(withoutOffset.getText()).toEqual('1 person is viewing.');
28370 expect(withOffset.getText()).toEqual('Igor is viewing.');
28372 countInput.clear();
28373 countInput.sendKeys('0');
28375 expect(withoutOffset.getText()).toEqual('Nobody is viewing.');
28376 expect(withOffset.getText()).toEqual('Nobody is viewing.');
28378 countInput.clear();
28379 countInput.sendKeys('2');
28381 expect(withoutOffset.getText()).toEqual('2 people are viewing.');
28382 expect(withOffset.getText()).toEqual('Igor and Misko are viewing.');
28384 countInput.clear();
28385 countInput.sendKeys('3');
28387 expect(withoutOffset.getText()).toEqual('3 people are viewing.');
28388 expect(withOffset.getText()).toEqual('Igor, Misko and one other person are viewing.');
28390 countInput.clear();
28391 countInput.sendKeys('4');
28393 expect(withoutOffset.getText()).toEqual('4 people are viewing.');
28394 expect(withOffset.getText()).toEqual('Igor, Misko and 2 other people are viewing.');
28396 it('should show data-bound names', function() {
28397 var withOffset = element.all(by.css('ng-pluralize')).get(1);
28398 var personCount = element(by.model('personCount'));
28399 var person1 = element(by.model('person1'));
28400 var person2 = element(by.model('person2'));
28401 personCount.clear();
28402 personCount.sendKeys('4');
28404 person1.sendKeys('Di');
28406 person2.sendKeys('Vojta');
28407 expect(withOffset.getText()).toEqual('Di, Vojta and 2 other people are viewing.');
28412 var ngPluralizeDirective = ['$locale', '$interpolate', '$log', function($locale, $interpolate, $log) {
28414 IS_WHEN = /^when(Minus)?(.+)$/;
28417 link: function(scope, element, attr) {
28418 var numberExp = attr.count,
28419 whenExp = attr.$attr.when && element.attr(attr.$attr.when), // we have {{}} in attrs
28420 offset = attr.offset || 0,
28421 whens = scope.$eval(whenExp) || {},
28423 startSymbol = $interpolate.startSymbol(),
28424 endSymbol = $interpolate.endSymbol(),
28425 braceReplacement = startSymbol + numberExp + '-' + offset + endSymbol,
28426 watchRemover = angular.noop,
28429 forEach(attr, function(expression, attributeName) {
28430 var tmpMatch = IS_WHEN.exec(attributeName);
28432 var whenKey = (tmpMatch[1] ? '-' : '') + lowercase(tmpMatch[2]);
28433 whens[whenKey] = element.attr(attr.$attr[attributeName]);
28436 forEach(whens, function(expression, key) {
28437 whensExpFns[key] = $interpolate(expression.replace(BRACE, braceReplacement));
28441 scope.$watch(numberExp, function ngPluralizeWatchAction(newVal) {
28442 var count = parseFloat(newVal);
28443 var countIsNaN = isNaN(count);
28445 if (!countIsNaN && !(count in whens)) {
28446 // If an explicit number rule such as 1, 2, 3... is defined, just use it.
28447 // Otherwise, check it against pluralization rules in $locale service.
28448 count = $locale.pluralCat(count - offset);
28451 // If both `count` and `lastCount` are NaN, we don't need to re-register a watch.
28452 // In JS `NaN !== NaN`, so we have to explicitly check.
28453 if ((count !== lastCount) && !(countIsNaN && isNumber(lastCount) && isNaN(lastCount))) {
28455 var whenExpFn = whensExpFns[count];
28456 if (isUndefined(whenExpFn)) {
28457 if (newVal != null) {
28458 $log.debug("ngPluralize: no rule defined for '" + count + "' in " + whenExp);
28460 watchRemover = noop;
28461 updateElementText();
28463 watchRemover = scope.$watch(whenExpFn, updateElementText);
28469 function updateElementText(newText) {
28470 element.text(newText || '');
28482 * The `ngRepeat` directive instantiates a template once per item from a collection. Each template
28483 * instance gets its own scope, where the given loop variable is set to the current collection item,
28484 * and `$index` is set to the item index or key.
28486 * Special properties are exposed on the local scope of each template instance, including:
28488 * | Variable | Type | Details |
28489 * |-----------|-----------------|-----------------------------------------------------------------------------|
28490 * | `$index` | {@type number} | iterator offset of the repeated element (0..length-1) |
28491 * | `$first` | {@type boolean} | true if the repeated element is first in the iterator. |
28492 * | `$middle` | {@type boolean} | true if the repeated element is between the first and last in the iterator. |
28493 * | `$last` | {@type boolean} | true if the repeated element is last in the iterator. |
28494 * | `$even` | {@type boolean} | true if the iterator position `$index` is even (otherwise false). |
28495 * | `$odd` | {@type boolean} | true if the iterator position `$index` is odd (otherwise false). |
28497 * <div class="alert alert-info">
28498 * Creating aliases for these properties is possible with {@link ng.directive:ngInit `ngInit`}.
28499 * This may be useful when, for instance, nesting ngRepeats.
28503 * # Iterating over object properties
28505 * It is possible to get `ngRepeat` to iterate over the properties of an object using the following
28509 * <div ng-repeat="(key, value) in myObj"> ... </div>
28512 * However, there are a limitations compared to array iteration:
28514 * - The JavaScript specification does not define the order of keys
28515 * returned for an object, so Angular relies on the order returned by the browser
28516 * when running `for key in myObj`. Browsers generally follow the strategy of providing
28517 * keys in the order in which they were defined, although there are exceptions when keys are deleted
28518 * and reinstated. See the
28519 * [MDN page on `delete` for more info](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/delete#Cross-browser_notes).
28521 * - `ngRepeat` will silently *ignore* object keys starting with `$`, because
28522 * it's a prefix used by Angular for public (`$`) and private (`$$`) properties.
28524 * - The built-in filters {@link ng.orderBy orderBy} and {@link ng.filter filter} do not work with
28525 * objects, and will throw if used with one.
28527 * If you are hitting any of these limitations, the recommended workaround is to convert your object into an array
28528 * that is sorted into the order that you prefer before providing it to `ngRepeat`. You could
28529 * do this with a filter such as [toArrayFilter](http://ngmodules.org/modules/angular-toArrayFilter)
28530 * or implement a `$watch` on the object yourself.
28533 * # Tracking and Duplicates
28535 * `ngRepeat` uses {@link $rootScope.Scope#$watchCollection $watchCollection} to detect changes in
28536 * the collection. When a change happens, ngRepeat then makes the corresponding changes to the DOM:
28538 * * When an item is added, a new instance of the template is added to the DOM.
28539 * * When an item is removed, its template instance is removed from the DOM.
28540 * * When items are reordered, their respective templates are reordered in the DOM.
28542 * To minimize creation of DOM elements, `ngRepeat` uses a function
28543 * to "keep track" of all items in the collection and their corresponding DOM elements.
28544 * For example, if an item is added to the collection, ngRepeat will know that all other items
28545 * already have DOM elements, and will not re-render them.
28547 * The default tracking function (which tracks items by their identity) does not allow
28548 * duplicate items in arrays. This is because when there are duplicates, it is not possible
28549 * to maintain a one-to-one mapping between collection items and DOM elements.
28551 * If you do need to repeat duplicate items, you can substitute the default tracking behavior
28552 * with your own using the `track by` expression.
28554 * For example, you may track items by the index of each item in the collection, using the
28555 * special scope property `$index`:
28557 * <div ng-repeat="n in [42, 42, 43, 43] track by $index">
28562 * You may also use arbitrary expressions in `track by`, including references to custom functions
28565 * <div ng-repeat="n in [42, 42, 43, 43] track by myTrackingFunction(n)">
28570 * <div class="alert alert-success">
28571 * If you are working with objects that have an identifier property, you should track
28572 * by the identifier instead of the whole object. Should you reload your data later, `ngRepeat`
28573 * will not have to rebuild the DOM elements for items it has already rendered, even if the
28574 * JavaScript objects in the collection have been substituted for new ones. For large collections,
28575 * this significantly improves rendering performance. If you don't have a unique identifier,
28576 * `track by $index` can also provide a performance boost.
28579 * <div ng-repeat="model in collection track by model.id">
28584 * When no `track by` expression is provided, it is equivalent to tracking by the built-in
28585 * `$id` function, which tracks items by their identity:
28587 * <div ng-repeat="obj in collection track by $id(obj)">
28592 * <div class="alert alert-warning">
28593 * **Note:** `track by` must always be the last expression:
28596 * <div ng-repeat="model in collection | orderBy: 'id' as filtered_result track by model.id">
28601 * # Special repeat start and end points
28602 * To repeat a series of elements instead of just one parent element, ngRepeat (as well as other ng directives) supports extending
28603 * the range of the repeater by defining explicit start and end points by using **ng-repeat-start** and **ng-repeat-end** respectively.
28604 * 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)
28605 * up to and including the ending HTML tag where **ng-repeat-end** is placed.
28607 * The example below makes use of this feature:
28609 * <header ng-repeat-start="item in items">
28610 * Header {{ item }}
28612 * <div class="body">
28615 * <footer ng-repeat-end>
28616 * Footer {{ item }}
28620 * And with an input of {@type ['A','B']} for the items variable in the example above, the output will evaluate to:
28625 * <div class="body">
28634 * <div class="body">
28642 * The custom start and end points for ngRepeat also support all other HTML directive syntax flavors provided in AngularJS (such
28643 * as **data-ng-repeat-start**, **x-ng-repeat-start** and **ng:repeat-start**).
28646 * | Animation | Occurs |
28647 * |----------------------------------|-------------------------------------|
28648 * | {@link ng.$animate#enter enter} | when a new item is added to the list or when an item is revealed after a filter |
28649 * | {@link ng.$animate#leave leave} | when an item is removed from the list or when an item is filtered out |
28650 * | {@link ng.$animate#move move } | when an adjacent item is filtered out causing a reorder or when the item contents are reordered |
28652 * See the example below for defining CSS animations with ngRepeat.
28657 * @param {repeat_expression} ngRepeat The expression indicating how to enumerate a collection. These
28658 * formats are currently supported:
28660 * * `variable in expression` – where variable is the user defined loop variable and `expression`
28661 * is a scope expression giving the collection to enumerate.
28663 * For example: `album in artist.albums`.
28665 * * `(key, value) in expression` – where `key` and `value` can be any user defined identifiers,
28666 * and `expression` is the scope expression giving the collection to enumerate.
28668 * For example: `(name, age) in {'adam':10, 'amalie':12}`.
28670 * * `variable in expression track by tracking_expression` – You can also provide an optional tracking expression
28671 * which can be used to associate the objects in the collection with the DOM elements. If no tracking expression
28672 * is specified, ng-repeat associates elements by identity. It is an error to have
28673 * more than one tracking expression value resolve to the same key. (This would mean that two distinct objects are
28674 * mapped to the same DOM element, which is not possible.)
28676 * Note that the tracking expression must come last, after any filters, and the alias expression.
28678 * For example: `item in items` is equivalent to `item in items track by $id(item)`. This implies that the DOM elements
28679 * will be associated by item identity in the array.
28681 * For example: `item in items track by $id(item)`. A built in `$id()` function can be used to assign a unique
28682 * `$$hashKey` property to each item in the array. This property is then used as a key to associated DOM elements
28683 * with the corresponding item in the array by identity. Moving the same object in array would move the DOM
28684 * element in the same way in the DOM.
28686 * For example: `item in items track by item.id` is a typical pattern when the items come from the database. In this
28687 * case the object identity does not matter. Two objects are considered equivalent as long as their `id`
28688 * property is same.
28690 * For example: `item in items | filter:searchText track by item.id` is a pattern that might be used to apply a filter
28691 * to items in conjunction with a tracking expression.
28693 * * `variable in expression as alias_expression` – You can also provide an optional alias expression which will then store the
28694 * intermediate results of the repeater after the filters have been applied. Typically this is used to render a special message
28695 * when a filter is active on the repeater, but the filtered result set is empty.
28697 * For example: `item in items | filter:x as results` will store the fragment of the repeated items as `results`, but only after
28698 * the items have been processed through the filter.
28700 * 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
28701 * (and not as operator, inside an expression).
28703 * For example: `item in items | filter : x | orderBy : order | limitTo : limit as results` .
28706 * This example uses `ngRepeat` to display a list of people. A filter is used to restrict the displayed
28707 * results by name. New (entering) and removed (leaving) items are animated.
28708 <example module="ngRepeat" name="ngRepeat" deps="angular-animate.js" animations="true">
28709 <file name="index.html">
28710 <div ng-controller="repeatController">
28711 I have {{friends.length}} friends. They are:
28712 <input type="search" ng-model="q" placeholder="filter friends..." aria-label="filter friends" />
28713 <ul class="example-animate-container">
28714 <li class="animate-repeat" ng-repeat="friend in friends | filter:q as results">
28715 [{{$index + 1}}] {{friend.name}} who is {{friend.age}} years old.
28717 <li class="animate-repeat" ng-if="results.length == 0">
28718 <strong>No results found...</strong>
28723 <file name="script.js">
28724 angular.module('ngRepeat', ['ngAnimate']).controller('repeatController', function($scope) {
28726 {name:'John', age:25, gender:'boy'},
28727 {name:'Jessie', age:30, gender:'girl'},
28728 {name:'Johanna', age:28, gender:'girl'},
28729 {name:'Joy', age:15, gender:'girl'},
28730 {name:'Mary', age:28, gender:'girl'},
28731 {name:'Peter', age:95, gender:'boy'},
28732 {name:'Sebastian', age:50, gender:'boy'},
28733 {name:'Erika', age:27, gender:'girl'},
28734 {name:'Patrick', age:40, gender:'boy'},
28735 {name:'Samantha', age:60, gender:'girl'}
28739 <file name="animations.css">
28740 .example-animate-container {
28742 border:1px solid black;
28751 box-sizing:border-box;
28754 .animate-repeat.ng-move,
28755 .animate-repeat.ng-enter,
28756 .animate-repeat.ng-leave {
28757 transition:all linear 0.5s;
28760 .animate-repeat.ng-leave.ng-leave-active,
28761 .animate-repeat.ng-move,
28762 .animate-repeat.ng-enter {
28767 .animate-repeat.ng-leave,
28768 .animate-repeat.ng-move.ng-move-active,
28769 .animate-repeat.ng-enter.ng-enter-active {
28774 <file name="protractor.js" type="protractor">
28775 var friends = element.all(by.repeater('friend in friends'));
28777 it('should render initial data set', function() {
28778 expect(friends.count()).toBe(10);
28779 expect(friends.get(0).getText()).toEqual('[1] John who is 25 years old.');
28780 expect(friends.get(1).getText()).toEqual('[2] Jessie who is 30 years old.');
28781 expect(friends.last().getText()).toEqual('[10] Samantha who is 60 years old.');
28782 expect(element(by.binding('friends.length')).getText())
28783 .toMatch("I have 10 friends. They are:");
28786 it('should update repeater when filter predicate changes', function() {
28787 expect(friends.count()).toBe(10);
28789 element(by.model('q')).sendKeys('ma');
28791 expect(friends.count()).toBe(2);
28792 expect(friends.get(0).getText()).toEqual('[1] Mary who is 28 years old.');
28793 expect(friends.last().getText()).toEqual('[2] Samantha who is 60 years old.');
28798 var ngRepeatDirective = ['$parse', '$animate', '$compile', function($parse, $animate, $compile) {
28799 var NG_REMOVED = '$$NG_REMOVED';
28800 var ngRepeatMinErr = minErr('ngRepeat');
28802 var updateScope = function(scope, index, valueIdentifier, value, keyIdentifier, key, arrayLength) {
28803 // TODO(perf): generate setters to shave off ~40ms or 1-1.5%
28804 scope[valueIdentifier] = value;
28805 if (keyIdentifier) scope[keyIdentifier] = key;
28806 scope.$index = index;
28807 scope.$first = (index === 0);
28808 scope.$last = (index === (arrayLength - 1));
28809 scope.$middle = !(scope.$first || scope.$last);
28810 // jshint bitwise: false
28811 scope.$odd = !(scope.$even = (index&1) === 0);
28812 // jshint bitwise: true
28815 var getBlockStart = function(block) {
28816 return block.clone[0];
28819 var getBlockEnd = function(block) {
28820 return block.clone[block.clone.length - 1];
28826 multiElement: true,
28827 transclude: 'element',
28831 compile: function ngRepeatCompile($element, $attr) {
28832 var expression = $attr.ngRepeat;
28833 var ngRepeatEndComment = $compile.$$createComment('end ngRepeat', expression);
28835 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*$/);
28838 throw ngRepeatMinErr('iexp', "Expected expression in form of '_item_ in _collection_[ track by _id_]' but got '{0}'.",
28842 var lhs = match[1];
28843 var rhs = match[2];
28844 var aliasAs = match[3];
28845 var trackByExp = match[4];
28847 match = lhs.match(/^(?:(\s*[\$\w]+)|\(\s*([\$\w]+)\s*,\s*([\$\w]+)\s*\))$/);
28850 throw ngRepeatMinErr('iidexp', "'_item_' in '_item_ in _collection_' should be an identifier or '(_key_, _value_)' expression, but got '{0}'.",
28853 var valueIdentifier = match[3] || match[1];
28854 var keyIdentifier = match[2];
28856 if (aliasAs && (!/^[$a-zA-Z_][$a-zA-Z0-9_]*$/.test(aliasAs) ||
28857 /^(null|undefined|this|\$index|\$first|\$middle|\$last|\$even|\$odd|\$parent|\$root|\$id)$/.test(aliasAs))) {
28858 throw ngRepeatMinErr('badident', "alias '{0}' is invalid --- must be a valid JS identifier which is not a reserved name.",
28862 var trackByExpGetter, trackByIdExpFn, trackByIdArrayFn, trackByIdObjFn;
28863 var hashFnLocals = {$id: hashKey};
28866 trackByExpGetter = $parse(trackByExp);
28868 trackByIdArrayFn = function(key, value) {
28869 return hashKey(value);
28871 trackByIdObjFn = function(key) {
28876 return function ngRepeatLink($scope, $element, $attr, ctrl, $transclude) {
28878 if (trackByExpGetter) {
28879 trackByIdExpFn = function(key, value, index) {
28880 // assign key, value, and $index to the locals so that they can be used in hash functions
28881 if (keyIdentifier) hashFnLocals[keyIdentifier] = key;
28882 hashFnLocals[valueIdentifier] = value;
28883 hashFnLocals.$index = index;
28884 return trackByExpGetter($scope, hashFnLocals);
28888 // Store a list of elements from previous run. This is a hash where key is the item from the
28889 // iterator, and the value is objects with following properties.
28890 // - scope: bound scope
28891 // - element: previous element.
28892 // - index: position
28894 // We are using no-proto object so that we don't need to guard against inherited props via
28896 var lastBlockMap = createMap();
28899 $scope.$watchCollection(rhs, function ngRepeatAction(collection) {
28901 previousNode = $element[0], // node that cloned nodes should be inserted after
28902 // initialized to the comment node anchor
28904 // Same as lastBlockMap but it has the current state. It will become the
28905 // lastBlockMap on the next iteration.
28906 nextBlockMap = createMap(),
28908 key, value, // key/value of iteration
28912 block, // last object information {scope, element, id}
28917 $scope[aliasAs] = collection;
28920 if (isArrayLike(collection)) {
28921 collectionKeys = collection;
28922 trackByIdFn = trackByIdExpFn || trackByIdArrayFn;
28924 trackByIdFn = trackByIdExpFn || trackByIdObjFn;
28925 // if object, extract keys, in enumeration order, unsorted
28926 collectionKeys = [];
28927 for (var itemKey in collection) {
28928 if (hasOwnProperty.call(collection, itemKey) && itemKey.charAt(0) !== '$') {
28929 collectionKeys.push(itemKey);
28934 collectionLength = collectionKeys.length;
28935 nextBlockOrder = new Array(collectionLength);
28937 // locate existing items
28938 for (index = 0; index < collectionLength; index++) {
28939 key = (collection === collectionKeys) ? index : collectionKeys[index];
28940 value = collection[key];
28941 trackById = trackByIdFn(key, value, index);
28942 if (lastBlockMap[trackById]) {
28943 // found previously seen block
28944 block = lastBlockMap[trackById];
28945 delete lastBlockMap[trackById];
28946 nextBlockMap[trackById] = block;
28947 nextBlockOrder[index] = block;
28948 } else if (nextBlockMap[trackById]) {
28949 // if collision detected. restore lastBlockMap and throw an error
28950 forEach(nextBlockOrder, function(block) {
28951 if (block && block.scope) lastBlockMap[block.id] = block;
28953 throw ngRepeatMinErr('dupes',
28954 "Duplicates in a repeater are not allowed. Use 'track by' expression to specify unique keys. Repeater: {0}, Duplicate key: {1}, Duplicate value: {2}",
28955 expression, trackById, value);
28957 // new never before seen block
28958 nextBlockOrder[index] = {id: trackById, scope: undefined, clone: undefined};
28959 nextBlockMap[trackById] = true;
28963 // remove leftover items
28964 for (var blockKey in lastBlockMap) {
28965 block = lastBlockMap[blockKey];
28966 elementsToRemove = getBlockNodes(block.clone);
28967 $animate.leave(elementsToRemove);
28968 if (elementsToRemove[0].parentNode) {
28969 // if the element was not removed yet because of pending animation, mark it as deleted
28970 // so that we can ignore it later
28971 for (index = 0, length = elementsToRemove.length; index < length; index++) {
28972 elementsToRemove[index][NG_REMOVED] = true;
28975 block.scope.$destroy();
28978 // we are not using forEach for perf reasons (trying to avoid #call)
28979 for (index = 0; index < collectionLength; index++) {
28980 key = (collection === collectionKeys) ? index : collectionKeys[index];
28981 value = collection[key];
28982 block = nextBlockOrder[index];
28985 // if we have already seen this object, then we need to reuse the
28986 // associated scope/element
28988 nextNode = previousNode;
28990 // skip nodes that are already pending removal via leave animation
28992 nextNode = nextNode.nextSibling;
28993 } while (nextNode && nextNode[NG_REMOVED]);
28995 if (getBlockStart(block) != nextNode) {
28996 // existing item which got moved
28997 $animate.move(getBlockNodes(block.clone), null, previousNode);
28999 previousNode = getBlockEnd(block);
29000 updateScope(block.scope, index, valueIdentifier, value, keyIdentifier, key, collectionLength);
29002 // new item which we don't know about
29003 $transclude(function ngRepeatTransclude(clone, scope) {
29004 block.scope = scope;
29005 // http://jsperf.com/clone-vs-createcomment
29006 var endNode = ngRepeatEndComment.cloneNode(false);
29007 clone[clone.length++] = endNode;
29009 $animate.enter(clone, null, previousNode);
29010 previousNode = endNode;
29011 // Note: We only need the first/last node of the cloned nodes.
29012 // However, we need to keep the reference to the jqlite wrapper as it might be changed later
29013 // by a directive with templateUrl when its template arrives.
29014 block.clone = clone;
29015 nextBlockMap[block.id] = block;
29016 updateScope(block.scope, index, valueIdentifier, value, keyIdentifier, key, collectionLength);
29020 lastBlockMap = nextBlockMap;
29027 var NG_HIDE_CLASS = 'ng-hide';
29028 var NG_HIDE_IN_PROGRESS_CLASS = 'ng-hide-animate';
29035 * The `ngShow` directive shows or hides the given HTML element based on the expression
29036 * provided to the `ngShow` attribute. The element is shown or hidden by removing or adding
29037 * the `.ng-hide` CSS class onto the element. The `.ng-hide` CSS class is predefined
29038 * in AngularJS and sets the display style to none (using an !important flag).
29039 * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
29042 * <!-- when $scope.myValue is truthy (element is visible) -->
29043 * <div ng-show="myValue"></div>
29045 * <!-- when $scope.myValue is falsy (element is hidden) -->
29046 * <div ng-show="myValue" class="ng-hide"></div>
29049 * When the `ngShow` expression evaluates to a falsy value then the `.ng-hide` CSS class is added to the class
29050 * attribute on the element causing it to become hidden. When truthy, the `.ng-hide` CSS class is removed
29051 * from the element causing the element not to appear hidden.
29053 * ## Why is !important used?
29055 * You may be wondering why !important is used for the `.ng-hide` CSS class. This is because the `.ng-hide` selector
29056 * can be easily overridden by heavier selectors. For example, something as simple
29057 * as changing the display style on a HTML list item would make hidden elements appear visible.
29058 * This also becomes a bigger issue when dealing with CSS frameworks.
29060 * By using !important, the show and hide behavior will work as expected despite any clash between CSS selector
29061 * specificity (when !important isn't used with any conflicting styles). If a developer chooses to override the
29062 * styling to change how to hide an element then it is just a matter of using !important in their own CSS code.
29064 * ### Overriding `.ng-hide`
29066 * By default, the `.ng-hide` class will style the element with `display: none!important`. If you wish to change
29067 * the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide`
29068 * class CSS. Note that the selector that needs to be used is actually `.ng-hide:not(.ng-hide-animate)` to cope
29069 * with extra animation classes that can be added.
29072 * .ng-hide:not(.ng-hide-animate) {
29073 * /* this is just another form of hiding an element */
29074 * display: block!important;
29075 * position: absolute;
29081 * By default you don't need to override in CSS anything and the animations will work around the display style.
29083 * ## A note about animations with `ngShow`
29085 * Animations in ngShow/ngHide work with the show and hide events that are triggered when the directive expression
29086 * is true and false. This system works like the animation system present with ngClass except that
29087 * you must also include the !important flag to override the display property
29088 * so that you can perform an animation when the element is hidden during the time of the animation.
29092 * //a working example can be found at the bottom of this page
29094 * .my-element.ng-hide-add, .my-element.ng-hide-remove {
29095 * /* this is required as of 1.3x to properly
29096 * apply all styling in a show/hide animation */
29097 * transition: 0s linear all;
29100 * .my-element.ng-hide-add-active,
29101 * .my-element.ng-hide-remove-active {
29102 * /* the transition is defined in the active class */
29103 * transition: 1s linear all;
29106 * .my-element.ng-hide-add { ... }
29107 * .my-element.ng-hide-add.ng-hide-add-active { ... }
29108 * .my-element.ng-hide-remove { ... }
29109 * .my-element.ng-hide-remove.ng-hide-remove-active { ... }
29112 * Keep in mind that, as of AngularJS version 1.3, there is no need to change the display
29113 * property to block during animation states--ngAnimate will handle the style toggling automatically for you.
29116 * | Animation | Occurs |
29117 * |----------------------------------|-------------------------------------|
29118 * | {@link $animate#addClass addClass} `.ng-hide` | after the `ngShow` expression evaluates to a non truthy value and just before the contents are set to hidden |
29119 * | {@link $animate#removeClass removeClass} `.ng-hide` | after the `ngShow` expression evaluates to a truthy value and just before contents are set to visible |
29122 * @param {expression} ngShow If the {@link guide/expression expression} is truthy
29123 * then the element is shown or hidden respectively.
29126 <example module="ngAnimate" deps="angular-animate.js" animations="true">
29127 <file name="index.html">
29128 Click me: <input type="checkbox" ng-model="checked" aria-label="Toggle ngHide"><br/>
29131 <div class="check-element animate-show" ng-show="checked">
29132 <span class="glyphicon glyphicon-thumbs-up"></span> I show up when your checkbox is checked.
29137 <div class="check-element animate-show" ng-hide="checked">
29138 <span class="glyphicon glyphicon-thumbs-down"></span> I hide when your checkbox is checked.
29142 <file name="glyphicons.css">
29143 @import url(../../components/bootstrap-3.1.1/css/bootstrap.css);
29145 <file name="animations.css">
29150 border: 1px solid black;
29154 .animate-show.ng-hide-add, .animate-show.ng-hide-remove {
29155 transition: all linear 0.5s;
29158 .animate-show.ng-hide {
29166 border: 1px solid black;
29170 <file name="protractor.js" type="protractor">
29171 var thumbsUp = element(by.css('span.glyphicon-thumbs-up'));
29172 var thumbsDown = element(by.css('span.glyphicon-thumbs-down'));
29174 it('should check ng-show / ng-hide', function() {
29175 expect(thumbsUp.isDisplayed()).toBeFalsy();
29176 expect(thumbsDown.isDisplayed()).toBeTruthy();
29178 element(by.model('checked')).click();
29180 expect(thumbsUp.isDisplayed()).toBeTruthy();
29181 expect(thumbsDown.isDisplayed()).toBeFalsy();
29186 var ngShowDirective = ['$animate', function($animate) {
29189 multiElement: true,
29190 link: function(scope, element, attr) {
29191 scope.$watch(attr.ngShow, function ngShowWatchAction(value) {
29192 // we're adding a temporary, animation-specific class for ng-hide since this way
29193 // we can control when the element is actually displayed on screen without having
29194 // to have a global/greedy CSS selector that breaks when other animations are run.
29195 // Read: https://github.com/angular/angular.js/issues/9103#issuecomment-58335845
29196 $animate[value ? 'removeClass' : 'addClass'](element, NG_HIDE_CLASS, {
29197 tempClasses: NG_HIDE_IN_PROGRESS_CLASS
29211 * The `ngHide` directive shows or hides the given HTML element based on the expression
29212 * provided to the `ngHide` attribute. The element is shown or hidden by removing or adding
29213 * the `ng-hide` CSS class onto the element. The `.ng-hide` CSS class is predefined
29214 * in AngularJS and sets the display style to none (using an !important flag).
29215 * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
29218 * <!-- when $scope.myValue is truthy (element is hidden) -->
29219 * <div ng-hide="myValue" class="ng-hide"></div>
29221 * <!-- when $scope.myValue is falsy (element is visible) -->
29222 * <div ng-hide="myValue"></div>
29225 * When the `ngHide` expression evaluates to a truthy value then the `.ng-hide` CSS class is added to the class
29226 * attribute on the element causing it to become hidden. When falsy, the `.ng-hide` CSS class is removed
29227 * from the element causing the element not to appear hidden.
29229 * ## Why is !important used?
29231 * You may be wondering why !important is used for the `.ng-hide` CSS class. This is because the `.ng-hide` selector
29232 * can be easily overridden by heavier selectors. For example, something as simple
29233 * as changing the display style on a HTML list item would make hidden elements appear visible.
29234 * This also becomes a bigger issue when dealing with CSS frameworks.
29236 * By using !important, the show and hide behavior will work as expected despite any clash between CSS selector
29237 * specificity (when !important isn't used with any conflicting styles). If a developer chooses to override the
29238 * styling to change how to hide an element then it is just a matter of using !important in their own CSS code.
29240 * ### Overriding `.ng-hide`
29242 * By default, the `.ng-hide` class will style the element with `display: none!important`. If you wish to change
29243 * the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide`
29248 * /* this is just another form of hiding an element */
29249 * display: block!important;
29250 * position: absolute;
29256 * By default you don't need to override in CSS anything and the animations will work around the display style.
29258 * ## A note about animations with `ngHide`
29260 * Animations in ngShow/ngHide work with the show and hide events that are triggered when the directive expression
29261 * is true and false. This system works like the animation system present with ngClass, except that the `.ng-hide`
29262 * CSS class is added and removed for you instead of your own CSS class.
29266 * //a working example can be found at the bottom of this page
29268 * .my-element.ng-hide-add, .my-element.ng-hide-remove {
29269 * transition: 0.5s linear all;
29272 * .my-element.ng-hide-add { ... }
29273 * .my-element.ng-hide-add.ng-hide-add-active { ... }
29274 * .my-element.ng-hide-remove { ... }
29275 * .my-element.ng-hide-remove.ng-hide-remove-active { ... }
29278 * Keep in mind that, as of AngularJS version 1.3, there is no need to change the display
29279 * property to block during animation states--ngAnimate will handle the style toggling automatically for you.
29282 * | Animation | Occurs |
29283 * |----------------------------------|-------------------------------------|
29284 * | {@link $animate#addClass addClass} `.ng-hide` | after the `ngHide` expression evaluates to a truthy value and just before the contents are set to hidden |
29285 * | {@link $animate#removeClass removeClass} `.ng-hide` | after the `ngHide` expression evaluates to a non truthy value and just before contents are set to visible |
29289 * @param {expression} ngHide If the {@link guide/expression expression} is truthy then
29290 * the element is shown or hidden respectively.
29293 <example module="ngAnimate" deps="angular-animate.js" animations="true">
29294 <file name="index.html">
29295 Click me: <input type="checkbox" ng-model="checked" aria-label="Toggle ngShow"><br/>
29298 <div class="check-element animate-hide" ng-show="checked">
29299 <span class="glyphicon glyphicon-thumbs-up"></span> I show up when your checkbox is checked.
29304 <div class="check-element animate-hide" ng-hide="checked">
29305 <span class="glyphicon glyphicon-thumbs-down"></span> I hide when your checkbox is checked.
29309 <file name="glyphicons.css">
29310 @import url(../../components/bootstrap-3.1.1/css/bootstrap.css);
29312 <file name="animations.css">
29314 transition: all linear 0.5s;
29318 border: 1px solid black;
29322 .animate-hide.ng-hide {
29330 border: 1px solid black;
29334 <file name="protractor.js" type="protractor">
29335 var thumbsUp = element(by.css('span.glyphicon-thumbs-up'));
29336 var thumbsDown = element(by.css('span.glyphicon-thumbs-down'));
29338 it('should check ng-show / ng-hide', function() {
29339 expect(thumbsUp.isDisplayed()).toBeFalsy();
29340 expect(thumbsDown.isDisplayed()).toBeTruthy();
29342 element(by.model('checked')).click();
29344 expect(thumbsUp.isDisplayed()).toBeTruthy();
29345 expect(thumbsDown.isDisplayed()).toBeFalsy();
29350 var ngHideDirective = ['$animate', function($animate) {
29353 multiElement: true,
29354 link: function(scope, element, attr) {
29355 scope.$watch(attr.ngHide, function ngHideWatchAction(value) {
29356 // The comment inside of the ngShowDirective explains why we add and
29357 // remove a temporary class for the show/hide animation
29358 $animate[value ? 'addClass' : 'removeClass'](element,NG_HIDE_CLASS, {
29359 tempClasses: NG_HIDE_IN_PROGRESS_CLASS
29372 * The `ngStyle` directive allows you to set CSS style on an HTML element conditionally.
29375 * @param {expression} ngStyle
29377 * {@link guide/expression Expression} which evals to an
29378 * object whose keys are CSS style names and values are corresponding values for those CSS
29381 * Since some CSS style names are not valid keys for an object, they must be quoted.
29382 * See the 'background-color' style in the example below.
29386 <file name="index.html">
29387 <input type="button" value="set color" ng-click="myStyle={color:'red'}">
29388 <input type="button" value="set background" ng-click="myStyle={'background-color':'blue'}">
29389 <input type="button" value="clear" ng-click="myStyle={}">
29391 <span ng-style="myStyle">Sample Text</span>
29392 <pre>myStyle={{myStyle}}</pre>
29394 <file name="style.css">
29399 <file name="protractor.js" type="protractor">
29400 var colorSpan = element(by.css('span'));
29402 it('should check ng-style', function() {
29403 expect(colorSpan.getCssValue('color')).toBe('rgba(0, 0, 0, 1)');
29404 element(by.css('input[value=\'set color\']')).click();
29405 expect(colorSpan.getCssValue('color')).toBe('rgba(255, 0, 0, 1)');
29406 element(by.css('input[value=clear]')).click();
29407 expect(colorSpan.getCssValue('color')).toBe('rgba(0, 0, 0, 1)');
29412 var ngStyleDirective = ngDirective(function(scope, element, attr) {
29413 scope.$watch(attr.ngStyle, function ngStyleWatchAction(newStyles, oldStyles) {
29414 if (oldStyles && (newStyles !== oldStyles)) {
29415 forEach(oldStyles, function(val, style) { element.css(style, '');});
29417 if (newStyles) element.css(newStyles);
29427 * The `ngSwitch` directive is used to conditionally swap DOM structure on your template based on a scope expression.
29428 * Elements within `ngSwitch` but without `ngSwitchWhen` or `ngSwitchDefault` directives will be preserved at the location
29429 * as specified in the template.
29431 * The directive itself works similar to ngInclude, however, instead of downloading template code (or loading it
29432 * from the template cache), `ngSwitch` simply chooses one of the nested elements and makes it visible based on which element
29433 * matches the value obtained from the evaluated expression. In other words, you define a container element
29434 * (where you place the directive), place an expression on the **`on="..."` attribute**
29435 * (or the **`ng-switch="..."` attribute**), define any inner elements inside of the directive and place
29436 * a when attribute per element. The when attribute is used to inform ngSwitch which element to display when the on
29437 * expression is evaluated. If a matching expression is not found via a when attribute then an element with the default
29438 * attribute is displayed.
29440 * <div class="alert alert-info">
29441 * Be aware that the attribute values to match against cannot be expressions. They are interpreted
29442 * as literal string values to match against.
29443 * For example, **`ng-switch-when="someVal"`** will match against the string `"someVal"` not against the
29444 * value of the expression `$scope.someVal`.
29448 * | Animation | Occurs |
29449 * |----------------------------------|-------------------------------------|
29450 * | {@link ng.$animate#enter enter} | after the ngSwitch contents change and the matched child element is placed inside the container |
29451 * | {@link ng.$animate#leave leave} | after the ngSwitch contents change and just before the former contents are removed from the DOM |
29456 * <ANY ng-switch="expression">
29457 * <ANY ng-switch-when="matchValue1">...</ANY>
29458 * <ANY ng-switch-when="matchValue2">...</ANY>
29459 * <ANY ng-switch-default>...</ANY>
29466 * @param {*} ngSwitch|on expression to match against <code>ng-switch-when</code>.
29467 * On child elements add:
29469 * * `ngSwitchWhen`: the case statement to match against. If match then this
29470 * case will be displayed. If the same match appears multiple times, all the
29471 * elements will be displayed.
29472 * * `ngSwitchDefault`: the default case when no other case match. If there
29473 * are multiple default cases, all of them will be displayed when no other
29478 <example module="switchExample" deps="angular-animate.js" animations="true">
29479 <file name="index.html">
29480 <div ng-controller="ExampleController">
29481 <select ng-model="selection" ng-options="item for item in items">
29483 <code>selection={{selection}}</code>
29485 <div class="animate-switch-container"
29486 ng-switch on="selection">
29487 <div class="animate-switch" ng-switch-when="settings">Settings Div</div>
29488 <div class="animate-switch" ng-switch-when="home">Home Span</div>
29489 <div class="animate-switch" ng-switch-default>default</div>
29493 <file name="script.js">
29494 angular.module('switchExample', ['ngAnimate'])
29495 .controller('ExampleController', ['$scope', function($scope) {
29496 $scope.items = ['settings', 'home', 'other'];
29497 $scope.selection = $scope.items[0];
29500 <file name="animations.css">
29501 .animate-switch-container {
29504 border:1px solid black;
29513 .animate-switch.ng-animate {
29514 transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
29523 .animate-switch.ng-leave.ng-leave-active,
29524 .animate-switch.ng-enter {
29527 .animate-switch.ng-leave,
29528 .animate-switch.ng-enter.ng-enter-active {
29532 <file name="protractor.js" type="protractor">
29533 var switchElem = element(by.css('[ng-switch]'));
29534 var select = element(by.model('selection'));
29536 it('should start in settings', function() {
29537 expect(switchElem.getText()).toMatch(/Settings Div/);
29539 it('should change to home', function() {
29540 select.all(by.css('option')).get(1).click();
29541 expect(switchElem.getText()).toMatch(/Home Span/);
29543 it('should select default', function() {
29544 select.all(by.css('option')).get(2).click();
29545 expect(switchElem.getText()).toMatch(/default/);
29550 var ngSwitchDirective = ['$animate', '$compile', function($animate, $compile) {
29552 require: 'ngSwitch',
29554 // asks for $scope to fool the BC controller module
29555 controller: ['$scope', function ngSwitchController() {
29558 link: function(scope, element, attr, ngSwitchController) {
29559 var watchExpr = attr.ngSwitch || attr.on,
29560 selectedTranscludes = [],
29561 selectedElements = [],
29562 previousLeaveAnimations = [],
29563 selectedScopes = [];
29565 var spliceFactory = function(array, index) {
29566 return function() { array.splice(index, 1); };
29569 scope.$watch(watchExpr, function ngSwitchWatchAction(value) {
29571 for (i = 0, ii = previousLeaveAnimations.length; i < ii; ++i) {
29572 $animate.cancel(previousLeaveAnimations[i]);
29574 previousLeaveAnimations.length = 0;
29576 for (i = 0, ii = selectedScopes.length; i < ii; ++i) {
29577 var selected = getBlockNodes(selectedElements[i].clone);
29578 selectedScopes[i].$destroy();
29579 var promise = previousLeaveAnimations[i] = $animate.leave(selected);
29580 promise.then(spliceFactory(previousLeaveAnimations, i));
29583 selectedElements.length = 0;
29584 selectedScopes.length = 0;
29586 if ((selectedTranscludes = ngSwitchController.cases['!' + value] || ngSwitchController.cases['?'])) {
29587 forEach(selectedTranscludes, function(selectedTransclude) {
29588 selectedTransclude.transclude(function(caseElement, selectedScope) {
29589 selectedScopes.push(selectedScope);
29590 var anchor = selectedTransclude.element;
29591 caseElement[caseElement.length++] = $compile.$$createComment('end ngSwitchWhen');
29592 var block = { clone: caseElement };
29594 selectedElements.push(block);
29595 $animate.enter(caseElement, anchor.parent(), anchor);
29604 var ngSwitchWhenDirective = ngDirective({
29605 transclude: 'element',
29607 require: '^ngSwitch',
29608 multiElement: true,
29609 link: function(scope, element, attrs, ctrl, $transclude) {
29610 ctrl.cases['!' + attrs.ngSwitchWhen] = (ctrl.cases['!' + attrs.ngSwitchWhen] || []);
29611 ctrl.cases['!' + attrs.ngSwitchWhen].push({ transclude: $transclude, element: element });
29615 var ngSwitchDefaultDirective = ngDirective({
29616 transclude: 'element',
29618 require: '^ngSwitch',
29619 multiElement: true,
29620 link: function(scope, element, attr, ctrl, $transclude) {
29621 ctrl.cases['?'] = (ctrl.cases['?'] || []);
29622 ctrl.cases['?'].push({ transclude: $transclude, element: element });
29628 * @name ngTransclude
29632 * Directive that marks the insertion point for the transcluded DOM of the nearest parent directive that uses transclusion.
29634 * You can specify that you want to insert a named transclusion slot, instead of the default slot, by providing the slot name
29635 * as the value of the `ng-transclude` or `ng-transclude-slot` attribute.
29637 * If the transcluded content is not empty (i.e. contains one or more DOM nodes, including whitespace text nodes), any existing
29638 * content of this element will be removed before the transcluded content is inserted.
29639 * If the transcluded content is empty, the existing content is left intact. This lets you provide fallback content in the case
29640 * that no transcluded content is provided.
29644 * @param {string} ngTransclude|ngTranscludeSlot the name of the slot to insert at this point. If this is not provided, is empty
29645 * or its value is the same as the name of the attribute then the default slot is used.
29648 * ### Basic transclusion
29649 * This example demonstrates basic transclusion of content into a component directive.
29650 * <example name="simpleTranscludeExample" module="transcludeExample">
29651 * <file name="index.html">
29653 * angular.module('transcludeExample', [])
29654 * .directive('pane', function(){
29657 * transclude: true,
29658 * scope: { title:'@' },
29659 * template: '<div style="border: 1px solid black;">' +
29660 * '<div style="background-color: gray">{{title}}</div>' +
29661 * '<ng-transclude></ng-transclude>' +
29665 * .controller('ExampleController', ['$scope', function($scope) {
29666 * $scope.title = 'Lorem Ipsum';
29667 * $scope.text = 'Neque porro quisquam est qui dolorem ipsum quia dolor...';
29670 * <div ng-controller="ExampleController">
29671 * <input ng-model="title" aria-label="title"> <br/>
29672 * <textarea ng-model="text" aria-label="text"></textarea> <br/>
29673 * <pane title="{{title}}">{{text}}</pane>
29676 * <file name="protractor.js" type="protractor">
29677 * it('should have transcluded', function() {
29678 * var titleElement = element(by.model('title'));
29679 * titleElement.clear();
29680 * titleElement.sendKeys('TITLE');
29681 * var textElement = element(by.model('text'));
29682 * textElement.clear();
29683 * textElement.sendKeys('TEXT');
29684 * expect(element(by.binding('title')).getText()).toEqual('TITLE');
29685 * expect(element(by.binding('text')).getText()).toEqual('TEXT');
29691 * ### Transclude fallback content
29692 * This example shows how to use `NgTransclude` with fallback content, that
29693 * is displayed if no transcluded content is provided.
29695 * <example module="transcludeFallbackContentExample">
29696 * <file name="index.html">
29698 * angular.module('transcludeFallbackContentExample', [])
29699 * .directive('myButton', function(){
29702 * transclude: true,
29704 * template: '<button style="cursor: pointer;">' +
29705 * '<ng-transclude>' +
29706 * '<b style="color: red;">Button1</b>' +
29707 * '</ng-transclude>' +
29712 * <!-- fallback button content -->
29713 * <my-button id="fallback"></my-button>
29714 * <!-- modified button content -->
29715 * <my-button id="modified">
29716 * <i style="color: green;">Button2</i>
29719 * <file name="protractor.js" type="protractor">
29720 * it('should have different transclude element content', function() {
29721 * expect(element(by.id('fallback')).getText()).toBe('Button1');
29722 * expect(element(by.id('modified')).getText()).toBe('Button2');
29728 * ### Multi-slot transclusion
29729 * This example demonstrates using multi-slot transclusion in a component directive.
29730 * <example name="multiSlotTranscludeExample" module="multiSlotTranscludeExample">
29731 * <file name="index.html">
29733 * .title, .footer {
29734 * background-color: gray
29737 * <div ng-controller="ExampleController">
29738 * <input ng-model="title" aria-label="title"> <br/>
29739 * <textarea ng-model="text" aria-label="text"></textarea> <br/>
29741 * <pane-title><a ng-href="{{link}}">{{title}}</a></pane-title>
29742 * <pane-body><p>{{text}}</p></pane-body>
29746 * <file name="app.js">
29747 * angular.module('multiSlotTranscludeExample', [])
29748 * .directive('pane', function(){
29752 * 'title': '?paneTitle',
29753 * 'body': 'paneBody',
29754 * 'footer': '?paneFooter'
29756 * template: '<div style="border: 1px solid black;">' +
29757 * '<div class="title" ng-transclude="title">Fallback Title</div>' +
29758 * '<div ng-transclude="body"></div>' +
29759 * '<div class="footer" ng-transclude="footer">Fallback Footer</div>' +
29763 * .controller('ExampleController', ['$scope', function($scope) {
29764 * $scope.title = 'Lorem Ipsum';
29765 * $scope.link = "https://google.com";
29766 * $scope.text = 'Neque porro quisquam est qui dolorem ipsum quia dolor...';
29769 * <file name="protractor.js" type="protractor">
29770 * it('should have transcluded the title and the body', function() {
29771 * var titleElement = element(by.model('title'));
29772 * titleElement.clear();
29773 * titleElement.sendKeys('TITLE');
29774 * var textElement = element(by.model('text'));
29775 * textElement.clear();
29776 * textElement.sendKeys('TEXT');
29777 * expect(element(by.css('.title')).getText()).toEqual('TITLE');
29778 * expect(element(by.binding('text')).getText()).toEqual('TEXT');
29779 * expect(element(by.css('.footer')).getText()).toEqual('Fallback Footer');
29784 var ngTranscludeMinErr = minErr('ngTransclude');
29785 var ngTranscludeDirective = ngDirective({
29787 link: function($scope, $element, $attrs, controller, $transclude) {
29789 if ($attrs.ngTransclude === $attrs.$attr.ngTransclude) {
29790 // If the attribute is of the form: `ng-transclude="ng-transclude"`
29791 // then treat it like the default
29792 $attrs.ngTransclude = '';
29795 function ngTranscludeCloneAttachFn(clone) {
29796 if (clone.length) {
29798 $element.append(clone);
29802 if (!$transclude) {
29803 throw ngTranscludeMinErr('orphan',
29804 'Illegal use of ngTransclude directive in the template! ' +
29805 'No parent directive that requires a transclusion found. ' +
29807 startingTag($element));
29810 // If there is no slot name defined or the slot name is not optional
29811 // then transclude the slot
29812 var slotName = $attrs.ngTransclude || $attrs.ngTranscludeSlot;
29813 $transclude(ngTranscludeCloneAttachFn, null, slotName);
29823 * Load the content of a `<script>` element into {@link ng.$templateCache `$templateCache`}, so that the
29824 * template can be used by {@link ng.directive:ngInclude `ngInclude`},
29825 * {@link ngRoute.directive:ngView `ngView`}, or {@link guide/directive directives}. The type of the
29826 * `<script>` element must be specified as `text/ng-template`, and a cache name for the template must be
29827 * assigned through the element's `id`, which can then be used as a directive's `templateUrl`.
29829 * @param {string} type Must be set to `'text/ng-template'`.
29830 * @param {string} id Cache name of the template.
29834 <file name="index.html">
29835 <script type="text/ng-template" id="/tpl.html">
29836 Content of the template.
29839 <a ng-click="currentTpl='/tpl.html'" id="tpl-link">Load inlined template</a>
29840 <div id="tpl-content" ng-include src="currentTpl"></div>
29842 <file name="protractor.js" type="protractor">
29843 it('should load template defined inside script tag', function() {
29844 element(by.css('#tpl-link')).click();
29845 expect(element(by.css('#tpl-content')).getText()).toMatch(/Content of the template/);
29850 var scriptDirective = ['$templateCache', function($templateCache) {
29854 compile: function(element, attr) {
29855 if (attr.type == 'text/ng-template') {
29856 var templateUrl = attr.id,
29857 text = element[0].text;
29859 $templateCache.put(templateUrl, text);
29865 var noopNgModelController = { $setViewValue: noop, $render: noop };
29867 function chromeHack(optionElement) {
29868 // Workaround for https://code.google.com/p/chromium/issues/detail?id=381459
29869 // Adding an <option selected="selected"> element to a <select required="required"> should
29870 // automatically select the new element
29871 if (optionElement[0].hasAttribute('selected')) {
29872 optionElement[0].selected = true;
29878 * @name select.SelectController
29880 * The controller for the `<select>` directive. This provides support for reading
29881 * and writing the selected value(s) of the control and also coordinates dynamically
29882 * added `<option>` elements, perhaps by an `ngRepeat` directive.
29884 var SelectController =
29885 ['$element', '$scope', function($element, $scope) {
29888 optionsMap = new HashMap();
29890 // If the ngModel doesn't get provided then provide a dummy noop version to prevent errors
29891 self.ngModelCtrl = noopNgModelController;
29893 // The "unknown" option is one that is prepended to the list if the viewValue
29894 // does not match any of the options. When it is rendered the value of the unknown
29895 // option is '? XXX ?' where XXX is the hashKey of the value that is not known.
29897 // We can't just jqLite('<option>') since jqLite is not smart enough
29898 // to create it in <select> and IE barfs otherwise.
29899 self.unknownOption = jqLite(window.document.createElement('option'));
29900 self.renderUnknownOption = function(val) {
29901 var unknownVal = '? ' + hashKey(val) + ' ?';
29902 self.unknownOption.val(unknownVal);
29903 $element.prepend(self.unknownOption);
29904 $element.val(unknownVal);
29907 $scope.$on('$destroy', function() {
29908 // disable unknown option so that we don't do work when the whole select is being destroyed
29909 self.renderUnknownOption = noop;
29912 self.removeUnknownOption = function() {
29913 if (self.unknownOption.parent()) self.unknownOption.remove();
29917 // Read the value of the select control, the implementation of this changes depending
29918 // upon whether the select can have multiple values and whether ngOptions is at work.
29919 self.readValue = function readSingleValue() {
29920 self.removeUnknownOption();
29921 return $element.val();
29925 // Write the value to the select control, the implementation of this changes depending
29926 // upon whether the select can have multiple values and whether ngOptions is at work.
29927 self.writeValue = function writeSingleValue(value) {
29928 if (self.hasOption(value)) {
29929 self.removeUnknownOption();
29930 $element.val(value);
29931 if (value === '') self.emptyOption.prop('selected', true); // to make IE9 happy
29933 if (value == null && self.emptyOption) {
29934 self.removeUnknownOption();
29937 self.renderUnknownOption(value);
29943 // Tell the select control that an option, with the given value, has been added
29944 self.addOption = function(value, element) {
29945 // Skip comment nodes, as they only pollute the `optionsMap`
29946 if (element[0].nodeType === NODE_TYPE_COMMENT) return;
29948 assertNotHasOwnProperty(value, '"option value"');
29949 if (value === '') {
29950 self.emptyOption = element;
29952 var count = optionsMap.get(value) || 0;
29953 optionsMap.put(value, count + 1);
29954 self.ngModelCtrl.$render();
29955 chromeHack(element);
29958 // Tell the select control that an option, with the given value, has been removed
29959 self.removeOption = function(value) {
29960 var count = optionsMap.get(value);
29963 optionsMap.remove(value);
29964 if (value === '') {
29965 self.emptyOption = undefined;
29968 optionsMap.put(value, count - 1);
29973 // Check whether the select control has an option matching the given value
29974 self.hasOption = function(value) {
29975 return !!optionsMap.get(value);
29979 self.registerOption = function(optionScope, optionElement, optionAttrs, interpolateValueFn, interpolateTextFn) {
29981 if (interpolateValueFn) {
29982 // The value attribute is interpolated
29984 optionAttrs.$observe('value', function valueAttributeObserveAction(newVal) {
29985 if (isDefined(oldVal)) {
29986 self.removeOption(oldVal);
29989 self.addOption(newVal, optionElement);
29991 } else if (interpolateTextFn) {
29992 // The text content is interpolated
29993 optionScope.$watch(interpolateTextFn, function interpolateWatchAction(newVal, oldVal) {
29994 optionAttrs.$set('value', newVal);
29995 if (oldVal !== newVal) {
29996 self.removeOption(oldVal);
29998 self.addOption(newVal, optionElement);
30001 // The value attribute is static
30002 self.addOption(optionAttrs.value, optionElement);
30005 optionElement.on('$destroy', function() {
30006 self.removeOption(optionAttrs.value);
30007 self.ngModelCtrl.$render();
30018 * HTML `SELECT` element with angular data-binding.
30020 * The `select` directive is used together with {@link ngModel `ngModel`} to provide data-binding
30021 * between the scope and the `<select>` control (including setting default values).
30022 * It also handles dynamic `<option>` elements, which can be added using the {@link ngRepeat `ngRepeat}` or
30023 * {@link ngOptions `ngOptions`} directives.
30025 * When an item in the `<select>` menu is selected, the value of the selected option will be bound
30026 * to the model identified by the `ngModel` directive. With static or repeated options, this is
30027 * the content of the `value` attribute or the textContent of the `<option>`, if the value attribute is missing.
30028 * If you want dynamic value attributes, you can use interpolation inside the value attribute.
30030 * <div class="alert alert-warning">
30031 * Note that the value of a `select` directive used without `ngOptions` is always a string.
30032 * When the model needs to be bound to a non-string value, you must either explicitly convert it
30033 * using a directive (see example below) or use `ngOptions` to specify the set of options.
30034 * This is because an option element can only be bound to string values at present.
30037 * If the viewValue of `ngModel` does not match any of the options, then the control
30038 * will automatically add an "unknown" option, which it then removes when the mismatch is resolved.
30040 * Optionally, a single hard-coded `<option>` element, with the value set to an empty string, can
30041 * be nested into the `<select>` element. This element will then represent the `null` or "not selected"
30042 * option. See example below for demonstration.
30044 * <div class="alert alert-info">
30045 * In many cases, `ngRepeat` can be used on `<option>` elements instead of {@link ng.directive:ngOptions
30046 * ngOptions} to achieve a similar result. However, `ngOptions` provides some benefits, such as
30047 * more flexibility in how the `<select>`'s model is assigned via the `select` **`as`** part of the
30048 * comprehension expression, and additionally in reducing memory and increasing speed by not creating
30049 * a new scope for each repeated instance.
30053 * @param {string} ngModel Assignable angular expression to data-bind to.
30054 * @param {string=} name Property name of the form under which the control is published.
30055 * @param {string=} multiple Allows multiple options to be selected. The selected values will be
30056 * bound to the model as an array.
30057 * @param {string=} required Sets `required` validation error key if the value is not entered.
30058 * @param {string=} ngRequired Adds required attribute and required validation constraint to
30059 * the element when the ngRequired expression evaluates to true. Use ngRequired instead of required
30060 * when you want to data-bind to the required attribute.
30061 * @param {string=} ngChange Angular expression to be executed when selected option(s) changes due to user
30062 * interaction with the select element.
30063 * @param {string=} ngOptions sets the options that the select is populated with and defines what is
30064 * set on the model on selection. See {@link ngOptions `ngOptions`}.
30067 * ### Simple `select` elements with static options
30069 * <example name="static-select" module="staticSelect">
30070 * <file name="index.html">
30071 * <div ng-controller="ExampleController">
30072 * <form name="myForm">
30073 * <label for="singleSelect"> Single select: </label><br>
30074 * <select name="singleSelect" ng-model="data.singleSelect">
30075 * <option value="option-1">Option 1</option>
30076 * <option value="option-2">Option 2</option>
30079 * <label for="singleSelect"> Single select with "not selected" option and dynamic option values: </label><br>
30080 * <select name="singleSelect" id="singleSelect" ng-model="data.singleSelect">
30081 * <option value="">---Please select---</option> <!-- not selected / blank option -->
30082 * <option value="{{data.option1}}">Option 1</option> <!-- interpolation -->
30083 * <option value="option-2">Option 2</option>
30085 * <button ng-click="forceUnknownOption()">Force unknown option</button><br>
30086 * <tt>singleSelect = {{data.singleSelect}}</tt>
30089 * <label for="multipleSelect"> Multiple select: </label><br>
30090 * <select name="multipleSelect" id="multipleSelect" ng-model="data.multipleSelect" multiple>
30091 * <option value="option-1">Option 1</option>
30092 * <option value="option-2">Option 2</option>
30093 * <option value="option-3">Option 3</option>
30095 * <tt>multipleSelect = {{data.multipleSelect}}</tt><br/>
30099 * <file name="app.js">
30100 * angular.module('staticSelect', [])
30101 * .controller('ExampleController', ['$scope', function($scope) {
30103 * singleSelect: null,
30104 * multipleSelect: [],
30105 * option1: 'option-1',
30108 * $scope.forceUnknownOption = function() {
30109 * $scope.data.singleSelect = 'nonsense';
30115 * ### Using `ngRepeat` to generate `select` options
30116 * <example name="ngrepeat-select" module="ngrepeatSelect">
30117 * <file name="index.html">
30118 * <div ng-controller="ExampleController">
30119 * <form name="myForm">
30120 * <label for="repeatSelect"> Repeat select: </label>
30121 * <select name="repeatSelect" id="repeatSelect" ng-model="data.repeatSelect">
30122 * <option ng-repeat="option in data.availableOptions" value="{{option.id}}">{{option.name}}</option>
30126 * <tt>repeatSelect = {{data.repeatSelect}}</tt><br/>
30129 * <file name="app.js">
30130 * angular.module('ngrepeatSelect', [])
30131 * .controller('ExampleController', ['$scope', function($scope) {
30133 * repeatSelect: null,
30134 * availableOptions: [
30135 * {id: '1', name: 'Option A'},
30136 * {id: '2', name: 'Option B'},
30137 * {id: '3', name: 'Option C'}
30145 * ### Using `select` with `ngOptions` and setting a default value
30146 * See the {@link ngOptions ngOptions documentation} for more `ngOptions` usage examples.
30148 * <example name="select-with-default-values" module="defaultValueSelect">
30149 * <file name="index.html">
30150 * <div ng-controller="ExampleController">
30151 * <form name="myForm">
30152 * <label for="mySelect">Make a choice:</label>
30153 * <select name="mySelect" id="mySelect"
30154 * ng-options="option.name for option in data.availableOptions track by option.id"
30155 * ng-model="data.selectedOption"></select>
30158 * <tt>option = {{data.selectedOption}}</tt><br/>
30161 * <file name="app.js">
30162 * angular.module('defaultValueSelect', [])
30163 * .controller('ExampleController', ['$scope', function($scope) {
30165 * availableOptions: [
30166 * {id: '1', name: 'Option A'},
30167 * {id: '2', name: 'Option B'},
30168 * {id: '3', name: 'Option C'}
30170 * selectedOption: {id: '3', name: 'Option C'} //This sets the default value of the select in the ui
30177 * ### Binding `select` to a non-string value via `ngModel` parsing / formatting
30179 * <example name="select-with-non-string-options" module="nonStringSelect">
30180 * <file name="index.html">
30181 * <select ng-model="model.id" convert-to-number>
30182 * <option value="0">Zero</option>
30183 * <option value="1">One</option>
30184 * <option value="2">Two</option>
30188 * <file name="app.js">
30189 * angular.module('nonStringSelect', [])
30190 * .run(function($rootScope) {
30191 * $rootScope.model = { id: 2 };
30193 * .directive('convertToNumber', function() {
30195 * require: 'ngModel',
30196 * link: function(scope, element, attrs, ngModel) {
30197 * ngModel.$parsers.push(function(val) {
30198 * return parseInt(val, 10);
30200 * ngModel.$formatters.push(function(val) {
30207 * <file name="protractor.js" type="protractor">
30208 * it('should initialize to model', function() {
30209 * var select = element(by.css('select'));
30210 * expect(element(by.model('model.id')).$('option:checked').getText()).toEqual('Two');
30216 var selectDirective = function() {
30220 require: ['select', '?ngModel'],
30221 controller: SelectController,
30224 pre: selectPreLink,
30225 post: selectPostLink
30229 function selectPreLink(scope, element, attr, ctrls) {
30231 // if ngModel is not defined, we don't need to do anything
30232 var ngModelCtrl = ctrls[1];
30233 if (!ngModelCtrl) return;
30235 var selectCtrl = ctrls[0];
30237 selectCtrl.ngModelCtrl = ngModelCtrl;
30239 // When the selected item(s) changes we delegate getting the value of the select control
30240 // to the `readValue` method, which can be changed if the select can have multiple
30241 // selected values or if the options are being generated by `ngOptions`
30242 element.on('change', function() {
30243 scope.$apply(function() {
30244 ngModelCtrl.$setViewValue(selectCtrl.readValue());
30248 // If the select allows multiple values then we need to modify how we read and write
30249 // values from and to the control; also what it means for the value to be empty and
30250 // we have to add an extra watch since ngModel doesn't work well with arrays - it
30251 // doesn't trigger rendering if only an item in the array changes.
30252 if (attr.multiple) {
30254 // Read value now needs to check each option to see if it is selected
30255 selectCtrl.readValue = function readMultipleValue() {
30257 forEach(element.find('option'), function(option) {
30258 if (option.selected) {
30259 array.push(option.value);
30265 // Write value now needs to set the selected property of each matching option
30266 selectCtrl.writeValue = function writeMultipleValue(value) {
30267 var items = new HashMap(value);
30268 forEach(element.find('option'), function(option) {
30269 option.selected = isDefined(items.get(option.value));
30273 // we have to do it on each watch since ngModel watches reference, but
30274 // we need to work of an array, so we need to see if anything was inserted/removed
30275 var lastView, lastViewRef = NaN;
30276 scope.$watch(function selectMultipleWatch() {
30277 if (lastViewRef === ngModelCtrl.$viewValue && !equals(lastView, ngModelCtrl.$viewValue)) {
30278 lastView = shallowCopy(ngModelCtrl.$viewValue);
30279 ngModelCtrl.$render();
30281 lastViewRef = ngModelCtrl.$viewValue;
30284 // If we are a multiple select then value is now a collection
30285 // so the meaning of $isEmpty changes
30286 ngModelCtrl.$isEmpty = function(value) {
30287 return !value || value.length === 0;
30293 function selectPostLink(scope, element, attrs, ctrls) {
30294 // if ngModel is not defined, we don't need to do anything
30295 var ngModelCtrl = ctrls[1];
30296 if (!ngModelCtrl) return;
30298 var selectCtrl = ctrls[0];
30300 // We delegate rendering to the `writeValue` method, which can be changed
30301 // if the select can have multiple selected values or if the options are being
30302 // generated by `ngOptions`.
30303 // This must be done in the postLink fn to prevent $render to be called before
30304 // all nodes have been linked correctly.
30305 ngModelCtrl.$render = function() {
30306 selectCtrl.writeValue(ngModelCtrl.$viewValue);
30312 // The option directive is purely designed to communicate the existence (or lack of)
30313 // of dynamically created (and destroyed) option elements to their containing select
30314 // directive via its controller.
30315 var optionDirective = ['$interpolate', function($interpolate) {
30319 compile: function(element, attr) {
30320 if (isDefined(attr.value)) {
30321 // If the value attribute is defined, check if it contains an interpolation
30322 var interpolateValueFn = $interpolate(attr.value, true);
30324 // If the value attribute is not defined then we fall back to the
30325 // text content of the option element, which may be interpolated
30326 var interpolateTextFn = $interpolate(element.text(), true);
30327 if (!interpolateTextFn) {
30328 attr.$set('value', element.text());
30332 return function(scope, element, attr) {
30333 // This is an optimization over using ^^ since we don't want to have to search
30334 // all the way to the root of the DOM for every single option element
30335 var selectCtrlName = '$selectController',
30336 parent = element.parent(),
30337 selectCtrl = parent.data(selectCtrlName) ||
30338 parent.parent().data(selectCtrlName); // in case we are in optgroup
30341 selectCtrl.registerOption(scope, element, attr, interpolateValueFn, interpolateTextFn);
30348 var styleDirective = valueFn({
30359 * ngRequired adds the required {@link ngModel.NgModelController#$validators `validator`} to {@link ngModel `ngModel`}.
30360 * It is most often used for {@link input `input`} and {@link select `select`} controls, but can also be
30361 * applied to custom controls.
30363 * The directive sets the `required` attribute on the element if the Angular expression inside
30364 * `ngRequired` evaluates to true. A special directive for setting `required` is necessary because we
30365 * cannot use interpolation inside `required`. See the {@link guide/interpolation interpolation guide}
30368 * The validator will set the `required` error key to true if the `required` attribute is set and
30369 * calling {@link ngModel.NgModelController#$isEmpty `NgModelController.$isEmpty`} with the
30370 * {@link ngModel.NgModelController#$viewValue `ngModel.$viewValue`} returns `true`. For example, the
30371 * `$isEmpty()` implementation for `input[text]` checks the length of the `$viewValue`. When developing
30372 * custom controls, `$isEmpty()` can be overwritten to account for a $viewValue that is not string-based.
30375 * <example name="ngRequiredDirective" module="ngRequiredExample">
30376 * <file name="index.html">
30378 * angular.module('ngRequiredExample', [])
30379 * .controller('ExampleController', ['$scope', function($scope) {
30380 * $scope.required = true;
30383 * <div ng-controller="ExampleController">
30384 * <form name="form">
30385 * <label for="required">Toggle required: </label>
30386 * <input type="checkbox" ng-model="required" id="required" />
30388 * <label for="input">This input must be filled if `required` is true: </label>
30389 * <input type="text" ng-model="model" id="input" name="input" ng-required="required" /><br>
30391 * required error set? = <code>{{form.input.$error.required}}</code><br>
30392 * model = <code>{{model}}</code>
30396 * <file name="protractor.js" type="protractor">
30397 var required = element(by.binding('form.input.$error.required'));
30398 var model = element(by.binding('model'));
30399 var input = element(by.id('input'));
30401 it('should set the required error', function() {
30402 expect(required.getText()).toContain('true');
30404 input.sendKeys('123');
30405 expect(required.getText()).not.toContain('true');
30406 expect(model.getText()).toContain('123');
30411 var requiredDirective = function() {
30414 require: '?ngModel',
30415 link: function(scope, elm, attr, ctrl) {
30417 attr.required = true; // force truthy in case we are on non input element
30419 ctrl.$validators.required = function(modelValue, viewValue) {
30420 return !attr.required || !ctrl.$isEmpty(viewValue);
30423 attr.$observe('required', function() {
30436 * ngPattern adds the pattern {@link ngModel.NgModelController#$validators `validator`} to {@link ngModel `ngModel`}.
30437 * It is most often used for text-based {@link input `input`} controls, but can also be applied to custom text-based controls.
30439 * The validator sets the `pattern` error key if the {@link ngModel.NgModelController#$viewValue `ngModel.$viewValue`}
30440 * does not match a RegExp which is obtained by evaluating the Angular expression given in the
30441 * `ngPattern` attribute value:
30442 * * If the expression evaluates to a RegExp object, then this is used directly.
30443 * * If the expression evaluates to a string, then it will be converted to a RegExp after wrapping it
30444 * in `^` and `$` characters. For instance, `"abc"` will be converted to `new RegExp('^abc$')`.
30446 * <div class="alert alert-info">
30447 * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
30448 * start at the index of the last search's match, thus not taking the whole input value into
30452 * <div class="alert alert-info">
30453 * **Note:** This directive is also added when the plain `pattern` attribute is used, with two
30457 * `ngPattern` does not set the `pattern` attribute and therefore HTML5 constraint validation is
30461 * The `ngPattern` attribute must be an expression, while the `pattern` value must be
30468 * <example name="ngPatternDirective" module="ngPatternExample">
30469 * <file name="index.html">
30471 * angular.module('ngPatternExample', [])
30472 * .controller('ExampleController', ['$scope', function($scope) {
30473 * $scope.regex = '\\d+';
30476 * <div ng-controller="ExampleController">
30477 * <form name="form">
30478 * <label for="regex">Set a pattern (regex string): </label>
30479 * <input type="text" ng-model="regex" id="regex" />
30481 * <label for="input">This input is restricted by the current pattern: </label>
30482 * <input type="text" ng-model="model" id="input" name="input" ng-pattern="regex" /><br>
30484 * input valid? = <code>{{form.input.$valid}}</code><br>
30485 * model = <code>{{model}}</code>
30489 * <file name="protractor.js" type="protractor">
30490 var model = element(by.binding('model'));
30491 var input = element(by.id('input'));
30493 it('should validate the input with the default pattern', function() {
30494 input.sendKeys('aaa');
30495 expect(model.getText()).not.toContain('aaa');
30497 input.clear().then(function() {
30498 input.sendKeys('123');
30499 expect(model.getText()).toContain('123');
30505 var patternDirective = function() {
30508 require: '?ngModel',
30509 link: function(scope, elm, attr, ctrl) {
30512 var regexp, patternExp = attr.ngPattern || attr.pattern;
30513 attr.$observe('pattern', function(regex) {
30514 if (isString(regex) && regex.length > 0) {
30515 regex = new RegExp('^' + regex + '$');
30518 if (regex && !regex.test) {
30519 throw minErr('ngPattern')('noregexp',
30520 'Expected {0} to be a RegExp but was {1}. Element: {2}', patternExp,
30521 regex, startingTag(elm));
30524 regexp = regex || undefined;
30528 ctrl.$validators.pattern = function(modelValue, viewValue) {
30529 // HTML5 pattern constraint validates the input value, so we validate the viewValue
30530 return ctrl.$isEmpty(viewValue) || isUndefined(regexp) || regexp.test(viewValue);
30538 * @name ngMaxlength
30542 * ngMaxlength adds the maxlength {@link ngModel.NgModelController#$validators `validator`} to {@link ngModel `ngModel`}.
30543 * It is most often used for text-based {@link input `input`} controls, but can also be applied to custom text-based controls.
30545 * The validator sets the `maxlength` error key if the {@link ngModel.NgModelController#$viewValue `ngModel.$viewValue`}
30546 * is longer than the integer obtained by evaluating the Angular expression given in the
30547 * `ngMaxlength` attribute value.
30549 * <div class="alert alert-info">
30550 * **Note:** This directive is also added when the plain `maxlength` attribute is used, with two
30554 * `ngMaxlength` does not set the `maxlength` attribute and therefore HTML5 constraint
30555 * validation is not available.
30558 * The `ngMaxlength` attribute must be an expression, while the `maxlength` value must be
30565 * <example name="ngMaxlengthDirective" module="ngMaxlengthExample">
30566 * <file name="index.html">
30568 * angular.module('ngMaxlengthExample', [])
30569 * .controller('ExampleController', ['$scope', function($scope) {
30570 * $scope.maxlength = 5;
30573 * <div ng-controller="ExampleController">
30574 * <form name="form">
30575 * <label for="maxlength">Set a maxlength: </label>
30576 * <input type="number" ng-model="maxlength" id="maxlength" />
30578 * <label for="input">This input is restricted by the current maxlength: </label>
30579 * <input type="text" ng-model="model" id="input" name="input" ng-maxlength="maxlength" /><br>
30581 * input valid? = <code>{{form.input.$valid}}</code><br>
30582 * model = <code>{{model}}</code>
30586 * <file name="protractor.js" type="protractor">
30587 var model = element(by.binding('model'));
30588 var input = element(by.id('input'));
30590 it('should validate the input with the default maxlength', function() {
30591 input.sendKeys('abcdef');
30592 expect(model.getText()).not.toContain('abcdef');
30594 input.clear().then(function() {
30595 input.sendKeys('abcde');
30596 expect(model.getText()).toContain('abcde');
30602 var maxlengthDirective = function() {
30605 require: '?ngModel',
30606 link: function(scope, elm, attr, ctrl) {
30609 var maxlength = -1;
30610 attr.$observe('maxlength', function(value) {
30611 var intVal = toInt(value);
30612 maxlength = isNaN(intVal) ? -1 : intVal;
30615 ctrl.$validators.maxlength = function(modelValue, viewValue) {
30616 return (maxlength < 0) || ctrl.$isEmpty(viewValue) || (viewValue.length <= maxlength);
30624 * @name ngMinlength
30628 * ngMinlength adds the minlength {@link ngModel.NgModelController#$validators `validator`} to {@link ngModel `ngModel`}.
30629 * It is most often used for text-based {@link input `input`} controls, but can also be applied to custom text-based controls.
30631 * The validator sets the `minlength` error key if the {@link ngModel.NgModelController#$viewValue `ngModel.$viewValue`}
30632 * is shorter than the integer obtained by evaluating the Angular expression given in the
30633 * `ngMinlength` attribute value.
30635 * <div class="alert alert-info">
30636 * **Note:** This directive is also added when the plain `minlength` attribute is used, with two
30640 * `ngMinlength` does not set the `minlength` attribute and therefore HTML5 constraint
30641 * validation is not available.
30644 * The `ngMinlength` value must be an expression, while the `minlength` value must be
30651 * <example name="ngMinlengthDirective" module="ngMinlengthExample">
30652 * <file name="index.html">
30654 * angular.module('ngMinlengthExample', [])
30655 * .controller('ExampleController', ['$scope', function($scope) {
30656 * $scope.minlength = 3;
30659 * <div ng-controller="ExampleController">
30660 * <form name="form">
30661 * <label for="minlength">Set a minlength: </label>
30662 * <input type="number" ng-model="minlength" id="minlength" />
30664 * <label for="input">This input is restricted by the current minlength: </label>
30665 * <input type="text" ng-model="model" id="input" name="input" ng-minlength="minlength" /><br>
30667 * input valid? = <code>{{form.input.$valid}}</code><br>
30668 * model = <code>{{model}}</code>
30672 * <file name="protractor.js" type="protractor">
30673 var model = element(by.binding('model'));
30674 var input = element(by.id('input'));
30676 it('should validate the input with the default minlength', function() {
30677 input.sendKeys('ab');
30678 expect(model.getText()).not.toContain('ab');
30680 input.sendKeys('abc');
30681 expect(model.getText()).toContain('abc');
30686 var minlengthDirective = function() {
30689 require: '?ngModel',
30690 link: function(scope, elm, attr, ctrl) {
30694 attr.$observe('minlength', function(value) {
30695 minlength = toInt(value) || 0;
30698 ctrl.$validators.minlength = function(modelValue, viewValue) {
30699 return ctrl.$isEmpty(viewValue) || viewValue.length >= minlength;
30705 if (window.angular.bootstrap) {
30706 //AngularJS is already loaded, so we can return here...
30707 if (window.console) {
30708 console.log('WARNING: Tried to load angular more than once.');
30713 //try to bind to jquery now so that one can write jqLite(document).ready()
30714 //but we will rebind on bootstrap again.
30717 publishExternalAPI(angular);
30719 angular.module("ngLocale", [], ["$provide", function($provide) {
30720 var PLURAL_CATEGORY = {ZERO: "zero", ONE: "one", TWO: "two", FEW: "few", MANY: "many", OTHER: "other"};
30721 function getDecimals(n) {
30723 var i = n.indexOf('.');
30724 return (i == -1) ? 0 : n.length - i - 1;
30727 function getVF(n, opt_precision) {
30728 var v = opt_precision;
30730 if (undefined === v) {
30731 v = Math.min(getDecimals(n), 3);
30734 var base = Math.pow(10, v);
30735 var f = ((n * base) | 0) % base;
30736 return {v: v, f: f};
30739 $provide.value("$locale", {
30740 "DATETIME_FORMATS": {
30762 "FIRSTDAYOFWEEK": 6,
30800 "STANDALONEMONTH": [
30818 "fullDate": "EEEE, MMMM d, y",
30819 "longDate": "MMMM d, y",
30820 "medium": "MMM d, y h:mm:ss a",
30821 "mediumDate": "MMM d, y",
30822 "mediumTime": "h:mm:ss a",
30823 "short": "M/d/yy h:mm a",
30824 "shortDate": "M/d/yy",
30825 "shortTime": "h:mm a"
30827 "NUMBER_FORMATS": {
30828 "CURRENCY_SYM": "$",
30829 "DECIMAL_SEP": ".",
30849 "negPre": "-\u00a4",
30851 "posPre": "\u00a4",
30857 "localeID": "en_US",
30858 "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;}
30862 jqLite(window.document).ready(function() {
30863 angularInit(window.document, bootstrap);
30868 !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>');