2 * @license AngularJS v1.3.20
3 * (c) 2010-2014 Google, Inc. http://angularjs.org
6 (function(window, document, undefined) {'use strict';
11 * This object provides a utility for producing rich Error messages within
12 * Angular. It can be called as follows:
14 * var exampleMinErr = minErr('example');
15 * throw exampleMinErr('one', 'This {0} is {1}', foo, bar);
17 * The above creates an instance of minErr in the example namespace. The
18 * resulting error will have a namespaced error code of example.one. The
19 * resulting error will replace {0} with the value of foo, and {1} with the
20 * value of bar. The object is not restricted in the number of arguments it can
23 * If fewer arguments are specified than necessary for interpolation, the extra
24 * interpolation markers will be preserved in the final string.
26 * Since data will be parsed statically during a build step, some restrictions
27 * are applied with respect to how minErr instances are created and called.
28 * Instances should have names of the form namespaceMinErr for a minErr created
29 * using minErr('namespace') . Error codes, namespaces and template strings
30 * should all be static strings, not variables or general expressions.
32 * @param {string} module The namespace to use for the new minErr instance.
33 * @param {function} ErrorConstructor Custom error constructor to be instantiated when returning
34 * error from returned function, for cases when a particular type of error is useful.
35 * @returns {function(code:string, template:string, ...templateArgs): Error} minErr instance
38 function minErr(module, ErrorConstructor) {
39 ErrorConstructor = ErrorConstructor || Error;
41 var code = arguments[0],
42 prefix = '[' + (module ? module + ':' : '') + code + '] ',
43 template = arguments[1],
44 templateArgs = arguments,
48 message = prefix + template.replace(/\{\d+\}/g, function(match) {
49 var index = +match.slice(1, -1), arg;
51 if (index + 2 < templateArgs.length) {
52 return toDebugString(templateArgs[index + 2]);
57 message = message + '\nhttp://errors.angularjs.org/1.3.20/' +
58 (module ? module + '/' : '') + code;
59 for (i = 2; i < arguments.length; i++) {
60 message = message + (i == 2 ? '?' : '&') + 'p' + (i - 2) + '=' +
61 encodeURIComponent(toDebugString(arguments[i]));
63 return new ErrorConstructor(message);
67 /* We need to tell jshint what variables are being exported */
68 /* global angular: true,
79 REGEX_STRING_REGEXP: true,
80 VALIDITY_STATE_PROPERTY: true,
84 manualLowercase: true,
85 manualUppercase: true,
117 escapeForRegexp: true,
129 toJsonReplacer: true,
133 tryDecodeURIComponent: true,
136 encodeUriSegment: true,
137 encodeUriQuery: true,
140 getTestability: true,
145 assertNotHasOwnProperty: true,
148 hasOwnProperty: true,
151 NODE_TYPE_ELEMENT: true,
152 NODE_TYPE_ATTRIBUTE: true,
153 NODE_TYPE_TEXT: true,
154 NODE_TYPE_COMMENT: true,
155 NODE_TYPE_DOCUMENT: true,
156 NODE_TYPE_DOCUMENT_FRAGMENT: true,
159 ////////////////////////////////////
168 * The ng module is loaded by default when an AngularJS application is started. The module itself
169 * contains the essential components for an AngularJS application to function. The table below
170 * lists a high level breakdown of each of the services/factories, filters, directives and testing
171 * components available within this core module.
173 * <div doc-module-components="ng"></div>
176 var REGEX_STRING_REGEXP = /^\/(.+)\/([a-z]*)$/;
178 // The name of a form control's ValidityState property.
179 // This is used so that it's possible for internal tests to create mock ValidityStates.
180 var VALIDITY_STATE_PROPERTY = 'validity';
184 * @name angular.lowercase
188 * @description Converts the specified string to lowercase.
189 * @param {string} string String to be converted to lowercase.
190 * @returns {string} Lowercased string.
192 var lowercase = function(string) {return isString(string) ? string.toLowerCase() : string;};
193 var hasOwnProperty = Object.prototype.hasOwnProperty;
197 * @name angular.uppercase
201 * @description Converts the specified string to uppercase.
202 * @param {string} string String to be converted to uppercase.
203 * @returns {string} Uppercased string.
205 var uppercase = function(string) {return isString(string) ? string.toUpperCase() : string;};
208 var manualLowercase = function(s) {
209 /* jshint bitwise: false */
211 ? s.replace(/[A-Z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) | 32);})
214 var manualUppercase = function(s) {
215 /* jshint bitwise: false */
217 ? s.replace(/[a-z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) & ~32);})
222 // String#toLowerCase and String#toUpperCase don't produce correct results in browsers with Turkish
223 // locale, for this reason we need to detect this case and redefine lowercase/uppercase methods
224 // with correct but slower alternatives.
225 if ('i' !== 'I'.toLowerCase()) {
226 lowercase = manualLowercase;
227 uppercase = manualUppercase;
232 msie, // holds major version number for IE, or NaN if UA is not IE.
233 jqLite, // delay binding since jQuery could be loaded after us.
234 jQuery, // delay binding
238 toString = Object.prototype.toString,
239 ngMinErr = minErr('ng'),
242 angular = window.angular || (window.angular = {}),
247 * documentMode is an IE-only property
248 * http://msdn.microsoft.com/en-us/library/ie/cc196988(v=vs.85).aspx
250 msie = document.documentMode;
256 * @return {boolean} Returns true if `obj` is an array or array-like object (NodeList, Arguments,
259 function isArrayLike(obj) {
260 if (obj == null || isWindow(obj)) {
264 // Support: iOS 8.2 (not reproducible in simulator)
265 // "length" in obj used to prevent JIT error (gh-11508)
266 var length = "length" in Object(obj) && obj.length;
268 if (obj.nodeType === NODE_TYPE_ELEMENT && length) {
272 return isString(obj) || isArray(obj) || length === 0 ||
273 typeof length === 'number' && length > 0 && (length - 1) in obj;
278 * @name angular.forEach
283 * Invokes the `iterator` function once for each item in `obj` collection, which can be either an
284 * object or an array. The `iterator` function is invoked with `iterator(value, key, obj)`, where `value`
285 * is the value of an object property or an array element, `key` is the object property key or
286 * array element index and obj is the `obj` itself. Specifying a `context` for the function is optional.
288 * It is worth noting that `.forEach` does not iterate over inherited properties because it filters
289 * using the `hasOwnProperty` method.
292 * [Array.prototype.forEach](http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.18),
293 * Providing 'undefined' or 'null' values for `obj` will not throw a TypeError, but rather just
294 * return the value provided.
297 var values = {name: 'misko', gender: 'male'};
299 angular.forEach(values, function(value, key) {
300 this.push(key + ': ' + value);
302 expect(log).toEqual(['name: misko', 'gender: male']);
305 * @param {Object|Array} obj Object to iterate over.
306 * @param {Function} iterator Iterator function.
307 * @param {Object=} context Object to become context (`this`) for the iterator function.
308 * @returns {Object|Array} Reference to `obj`.
311 function forEach(obj, iterator, context) {
314 if (isFunction(obj)) {
316 // Need to check if hasOwnProperty exists,
317 // as on IE8 the result of querySelectorAll is an object without a hasOwnProperty function
318 if (key != 'prototype' && key != 'length' && key != 'name' && (!obj.hasOwnProperty || obj.hasOwnProperty(key))) {
319 iterator.call(context, obj[key], key, obj);
322 } else if (isArray(obj) || isArrayLike(obj)) {
323 var isPrimitive = typeof obj !== 'object';
324 for (key = 0, length = obj.length; key < length; key++) {
325 if (isPrimitive || key in obj) {
326 iterator.call(context, obj[key], key, obj);
329 } else if (obj.forEach && obj.forEach !== forEach) {
330 obj.forEach(iterator, context, obj);
333 if (obj.hasOwnProperty(key)) {
334 iterator.call(context, obj[key], key, obj);
342 function sortedKeys(obj) {
343 return Object.keys(obj).sort();
346 function forEachSorted(obj, iterator, context) {
347 var keys = sortedKeys(obj);
348 for (var i = 0; i < keys.length; i++) {
349 iterator.call(context, obj[keys[i]], keys[i]);
356 * when using forEach the params are value, key, but it is often useful to have key, value.
357 * @param {function(string, *)} iteratorFn
358 * @returns {function(*, string)}
360 function reverseParams(iteratorFn) {
361 return function(value, key) { iteratorFn(key, value); };
365 * A consistent way of creating unique IDs in angular.
367 * Using simple numbers allows us to generate 28.6 million unique ids per second for 10 years before
368 * we hit number precision issues in JavaScript.
370 * Math.pow(2,53) / 60 / 60 / 24 / 365 / 10 = 28.6M
372 * @returns {number} an unique alpha-numeric string
380 * Set or clear the hashkey for an object.
382 * @param h the hashkey (!truthy to delete the hashkey)
384 function setHashKey(obj, h) {
388 delete obj.$$hashKey;
394 * @name angular.extend
399 * Extends the destination object `dst` by copying own enumerable properties from the `src` object(s)
400 * to `dst`. You can specify multiple `src` objects. If you want to preserve original objects, you can do so
401 * by passing an empty object as the target: `var object = angular.extend({}, object1, object2)`.
402 * Note: Keep in mind that `angular.extend` does not support recursive merge (deep copy).
404 * @param {Object} dst Destination object.
405 * @param {...Object} src Source object(s).
406 * @returns {Object} Reference to `dst`.
408 function extend(dst) {
409 var h = dst.$$hashKey;
411 for (var i = 1, ii = arguments.length; i < ii; i++) {
412 var obj = arguments[i];
414 var keys = Object.keys(obj);
415 for (var j = 0, jj = keys.length; j < jj; j++) {
427 return parseInt(str, 10);
431 function inherit(parent, extra) {
432 return extend(Object.create(parent), extra);
442 * A function that performs no operations. This function can be useful when writing code in the
445 function foo(callback) {
446 var result = calculateResult();
447 (callback || angular.noop)(result);
457 * @name angular.identity
462 * A function that returns its first argument. This function is useful when writing code in the
466 function transformer(transformationFn, value) {
467 return (transformationFn || angular.identity)(value);
470 * @param {*} value to be returned.
471 * @returns {*} the value passed in.
473 function identity($) {return $;}
474 identity.$inject = [];
477 function valueFn(value) {return function() {return value;};}
481 * @name angular.isUndefined
486 * Determines if a reference is undefined.
488 * @param {*} value Reference to check.
489 * @returns {boolean} True if `value` is undefined.
491 function isUndefined(value) {return typeof value === 'undefined';}
496 * @name angular.isDefined
501 * Determines if a reference is defined.
503 * @param {*} value Reference to check.
504 * @returns {boolean} True if `value` is defined.
506 function isDefined(value) {return typeof value !== 'undefined';}
511 * @name angular.isObject
516 * Determines if a reference is an `Object`. Unlike `typeof` in JavaScript, `null`s are not
517 * considered to be objects. Note that JavaScript arrays are objects.
519 * @param {*} value Reference to check.
520 * @returns {boolean} True if `value` is an `Object` but not `null`.
522 function isObject(value) {
523 // http://jsperf.com/isobject4
524 return value !== null && typeof value === 'object';
530 * @name angular.isString
535 * Determines if a reference is a `String`.
537 * @param {*} value Reference to check.
538 * @returns {boolean} True if `value` is a `String`.
540 function isString(value) {return typeof value === 'string';}
545 * @name angular.isNumber
550 * Determines if a reference is a `Number`.
552 * This includes the "special" numbers `NaN`, `+Infinity` and `-Infinity`.
554 * If you wish to exclude these then you can use the native
555 * [`isFinite'](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/isFinite)
558 * @param {*} value Reference to check.
559 * @returns {boolean} True if `value` is a `Number`.
561 function isNumber(value) {return typeof value === 'number';}
566 * @name angular.isDate
571 * Determines if a value is a date.
573 * @param {*} value Reference to check.
574 * @returns {boolean} True if `value` is a `Date`.
576 function isDate(value) {
577 return toString.call(value) === '[object Date]';
583 * @name angular.isArray
588 * Determines if a reference is an `Array`.
590 * @param {*} value Reference to check.
591 * @returns {boolean} True if `value` is an `Array`.
593 var isArray = Array.isArray;
597 * @name angular.isFunction
602 * Determines if a reference is a `Function`.
604 * @param {*} value Reference to check.
605 * @returns {boolean} True if `value` is a `Function`.
607 function isFunction(value) {return typeof value === 'function';}
611 * Determines if a value is a regular expression object.
614 * @param {*} value Reference to check.
615 * @returns {boolean} True if `value` is a `RegExp`.
617 function isRegExp(value) {
618 return toString.call(value) === '[object RegExp]';
623 * Checks if `obj` is a window object.
626 * @param {*} obj Object to check
627 * @returns {boolean} True if `obj` is a window obj.
629 function isWindow(obj) {
630 return obj && obj.window === obj;
634 function isScope(obj) {
635 return obj && obj.$evalAsync && obj.$watch;
639 function isFile(obj) {
640 return toString.call(obj) === '[object File]';
644 function isFormData(obj) {
645 return toString.call(obj) === '[object FormData]';
649 function isBlob(obj) {
650 return toString.call(obj) === '[object Blob]';
654 function isBoolean(value) {
655 return typeof value === 'boolean';
659 function isPromiseLike(obj) {
660 return obj && isFunction(obj.then);
664 var trim = function(value) {
665 return isString(value) ? value.trim() : value;
669 // http://docs.closure-library.googlecode.com/git/local_closure_goog_string_string.js.source.html#line1021
670 // Prereq: s is a string.
671 var escapeForRegexp = function(s) {
672 return s.replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g, '\\$1').
673 replace(/\x08/g, '\\x08');
679 * @name angular.isElement
684 * Determines if a reference is a DOM element (or wrapped jQuery element).
686 * @param {*} value Reference to check.
687 * @returns {boolean} True if `value` is a DOM element (or wrapped jQuery element).
689 function isElement(node) {
691 (node.nodeName // we are a direct element
692 || (node.prop && node.attr && node.find))); // we have an on and find method part of jQuery API
696 * @param str 'key1,key2,...'
697 * @returns {object} in the form of {key1:true, key2:true, ...}
699 function makeMap(str) {
700 var obj = {}, items = str.split(","), i;
701 for (i = 0; i < items.length; i++)
702 obj[items[i]] = true;
707 function nodeName_(element) {
708 return lowercase(element.nodeName || (element[0] && element[0].nodeName));
711 function includes(array, obj) {
712 return Array.prototype.indexOf.call(array, obj) != -1;
715 function arrayRemove(array, value) {
716 var index = array.indexOf(value);
718 array.splice(index, 1);
729 * Creates a deep copy of `source`, which should be an object or an array.
731 * * If no destination is supplied, a copy of the object or array is created.
732 * * If a destination is provided, all of its elements (for arrays) or properties (for objects)
733 * are deleted and then all elements/properties from the source are copied to it.
734 * * If `source` is not an object or array (inc. `null` and `undefined`), `source` is returned.
735 * * If `source` is identical to 'destination' an exception will be thrown.
737 * @param {*} source The source that will be used to make a copy.
738 * Can be any type, including primitives, `null`, and `undefined`.
739 * @param {(Object|Array)=} destination Destination into which the source is copied. If
740 * provided, must be of the same type as `source`.
741 * @returns {*} The copy or updated `destination`, if `destination` was specified.
744 <example module="copyExample">
745 <file name="index.html">
746 <div ng-controller="ExampleController">
747 <form novalidate class="simple-form">
748 Name: <input type="text" ng-model="user.name" /><br />
749 E-mail: <input type="email" ng-model="user.email" /><br />
750 Gender: <input type="radio" ng-model="user.gender" value="male" />male
751 <input type="radio" ng-model="user.gender" value="female" />female<br />
752 <button ng-click="reset()">RESET</button>
753 <button ng-click="update(user)">SAVE</button>
755 <pre>form = {{user | json}}</pre>
756 <pre>master = {{master | json}}</pre>
760 angular.module('copyExample', [])
761 .controller('ExampleController', ['$scope', function($scope) {
764 $scope.update = function(user) {
765 // Example with 1 argument
766 $scope.master= angular.copy(user);
769 $scope.reset = function() {
770 // Example with 2 arguments
771 angular.copy($scope.master, $scope.user);
780 function copy(source, destination, stackSource, stackDest) {
781 if (isWindow(source) || isScope(source)) {
782 throw ngMinErr('cpws',
783 "Can't copy! Making copies of Window or Scope instances is not supported.");
787 destination = source;
789 if (isArray(source)) {
790 destination = copy(source, [], stackSource, stackDest);
791 } else if (isDate(source)) {
792 destination = new Date(source.getTime());
793 } else if (isRegExp(source)) {
794 destination = new RegExp(source.source, source.toString().match(/[^\/]*$/)[0]);
795 destination.lastIndex = source.lastIndex;
796 } else if (isObject(source)) {
797 var emptyObject = Object.create(Object.getPrototypeOf(source));
798 destination = copy(source, emptyObject, stackSource, stackDest);
802 if (source === destination) throw ngMinErr('cpi',
803 "Can't copy! Source and destination are identical.");
805 stackSource = stackSource || [];
806 stackDest = stackDest || [];
808 if (isObject(source)) {
809 var index = stackSource.indexOf(source);
810 if (index !== -1) return stackDest[index];
812 stackSource.push(source);
813 stackDest.push(destination);
817 if (isArray(source)) {
818 destination.length = 0;
819 for (var i = 0; i < source.length; i++) {
820 result = copy(source[i], null, stackSource, stackDest);
821 if (isObject(source[i])) {
822 stackSource.push(source[i]);
823 stackDest.push(result);
825 destination.push(result);
828 var h = destination.$$hashKey;
829 if (isArray(destination)) {
830 destination.length = 0;
832 forEach(destination, function(value, key) {
833 delete destination[key];
836 for (var key in source) {
837 if (source.hasOwnProperty(key)) {
838 result = copy(source[key], null, stackSource, stackDest);
839 if (isObject(source[key])) {
840 stackSource.push(source[key]);
841 stackDest.push(result);
843 destination[key] = result;
846 setHashKey(destination,h);
854 * Creates a shallow copy of an object, an array or a primitive.
856 * Assumes that there are no proto properties for objects.
858 function shallowCopy(src, dst) {
862 for (var i = 0, ii = src.length; i < ii; i++) {
865 } else if (isObject(src)) {
868 for (var key in src) {
869 if (!(key.charAt(0) === '$' && key.charAt(1) === '$')) {
881 * @name angular.equals
886 * Determines if two objects or two values are equivalent. Supports value types, regular
887 * expressions, arrays and objects.
889 * Two objects or values are considered equivalent if at least one of the following is true:
891 * * Both objects or values pass `===` comparison.
892 * * Both objects or values are of the same type and all of their properties are equal by
893 * comparing them with `angular.equals`.
894 * * Both values are NaN. (In JavaScript, NaN == NaN => false. But we consider two NaN as equal)
895 * * Both values represent the same regular expression (In JavaScript,
896 * /abc/ == /abc/ => false. But we consider two regular expressions as equal when their textual
897 * representation matches).
899 * During a property comparison, properties of `function` type and properties with names
900 * that begin with `$` are ignored.
902 * Scope and DOMWindow objects are being compared only by identify (`===`).
904 * @param {*} o1 Object or value to compare.
905 * @param {*} o2 Object or value to compare.
906 * @returns {boolean} True if arguments are equal.
908 function equals(o1, o2) {
909 if (o1 === o2) return true;
910 if (o1 === null || o2 === null) return false;
911 if (o1 !== o1 && o2 !== o2) return true; // NaN === NaN
912 var t1 = typeof o1, t2 = typeof o2, length, key, keySet;
914 if (t1 == 'object') {
916 if (!isArray(o2)) return false;
917 if ((length = o1.length) == o2.length) {
918 for (key = 0; key < length; key++) {
919 if (!equals(o1[key], o2[key])) return false;
923 } else if (isDate(o1)) {
924 if (!isDate(o2)) return false;
925 return equals(o1.getTime(), o2.getTime());
926 } else if (isRegExp(o1)) {
927 return isRegExp(o2) ? o1.toString() == o2.toString() : false;
929 if (isScope(o1) || isScope(o2) || isWindow(o1) || isWindow(o2) ||
930 isArray(o2) || isDate(o2) || isRegExp(o2)) return false;
933 if (key.charAt(0) === '$' || isFunction(o1[key])) continue;
934 if (!equals(o1[key], o2[key])) return false;
938 if (!keySet.hasOwnProperty(key) &&
939 key.charAt(0) !== '$' &&
940 o2[key] !== undefined &&
941 !isFunction(o2[key])) return false;
950 var csp = function() {
951 if (isDefined(csp.isActive_)) return csp.isActive_;
953 var active = !!(document.querySelector('[ng-csp]') ||
954 document.querySelector('[data-ng-csp]'));
958 /* jshint -W031, -W054 */
960 /* jshint +W031, +W054 */
966 return (csp.isActive_ = active);
971 function concat(array1, array2, index) {
972 return array1.concat(slice.call(array2, index));
975 function sliceArgs(args, startIndex) {
976 return slice.call(args, startIndex || 0);
988 * Returns a function which calls function `fn` bound to `self` (`self` becomes the `this` for
989 * `fn`). You can supply optional `args` that are prebound to the function. This feature is also
990 * known as [partial application](http://en.wikipedia.org/wiki/Partial_application), as
991 * distinguished from [function currying](http://en.wikipedia.org/wiki/Currying#Contrast_with_partial_function_application).
993 * @param {Object} self Context which `fn` should be evaluated in.
994 * @param {function()} fn Function to be bound.
995 * @param {...*} args Optional arguments to be prebound to the `fn` function call.
996 * @returns {function()} Function that wraps the `fn` with all the specified bindings.
999 function bind(self, fn) {
1000 var curryArgs = arguments.length > 2 ? sliceArgs(arguments, 2) : [];
1001 if (isFunction(fn) && !(fn instanceof RegExp)) {
1002 return curryArgs.length
1004 return arguments.length
1005 ? fn.apply(self, concat(curryArgs, arguments, 0))
1006 : fn.apply(self, curryArgs);
1009 return arguments.length
1010 ? fn.apply(self, arguments)
1014 // in IE, native methods are not functions so they cannot be bound (note: they don't need to be)
1020 function toJsonReplacer(key, value) {
1023 if (typeof key === 'string' && key.charAt(0) === '$' && key.charAt(1) === '$') {
1025 } else if (isWindow(value)) {
1027 } else if (value && document === value) {
1029 } else if (isScope(value)) {
1039 * @name angular.toJson
1044 * Serializes input into a JSON-formatted string. Properties with leading $$ characters will be
1045 * stripped since angular uses this notation internally.
1047 * @param {Object|Array|Date|string|number} obj Input to be serialized into JSON.
1048 * @param {boolean|number} [pretty=2] If set to true, the JSON output will contain newlines and whitespace.
1049 * If set to an integer, the JSON output will contain that many spaces per indentation.
1050 * @returns {string|undefined} JSON-ified string representing `obj`.
1052 function toJson(obj, pretty) {
1053 if (typeof obj === 'undefined') return undefined;
1054 if (!isNumber(pretty)) {
1055 pretty = pretty ? 2 : null;
1057 return JSON.stringify(obj, toJsonReplacer, pretty);
1063 * @name angular.fromJson
1068 * Deserializes a JSON string.
1070 * @param {string} json JSON string to deserialize.
1071 * @returns {Object|Array|string|number} Deserialized JSON string.
1073 function fromJson(json) {
1074 return isString(json)
1081 * @returns {string} Returns the string representation of the element.
1083 function startingTag(element) {
1084 element = jqLite(element).clone();
1086 // turns out IE does not let you set .html() on elements which
1087 // are not allowed to have children. So we just ignore it.
1090 var elemHtml = jqLite('<div>').append(element).html();
1092 return element[0].nodeType === NODE_TYPE_TEXT ? lowercase(elemHtml) :
1094 match(/^(<[^>]+>)/)[1].
1095 replace(/^<([\w\-]+)/, function(match, nodeName) { return '<' + lowercase(nodeName); });
1097 return lowercase(elemHtml);
1103 /////////////////////////////////////////////////
1106 * Tries to decode the URI component without throwing an exception.
1109 * @param str value potential URI component to check.
1110 * @returns {boolean} True if `value` can be decoded
1111 * with the decodeURIComponent function.
1113 function tryDecodeURIComponent(value) {
1115 return decodeURIComponent(value);
1117 // Ignore any invalid uri component
1123 * Parses an escaped url query string into key-value pairs.
1124 * @returns {Object.<string,boolean|Array>}
1126 function parseKeyValue(/**string*/keyValue) {
1127 var obj = {}, key_value, key;
1128 forEach((keyValue || "").split('&'), function(keyValue) {
1130 key_value = keyValue.replace(/\+/g,'%20').split('=');
1131 key = tryDecodeURIComponent(key_value[0]);
1132 if (isDefined(key)) {
1133 var val = isDefined(key_value[1]) ? tryDecodeURIComponent(key_value[1]) : true;
1134 if (!hasOwnProperty.call(obj, key)) {
1136 } else if (isArray(obj[key])) {
1139 obj[key] = [obj[key],val];
1147 function toKeyValue(obj) {
1149 forEach(obj, function(value, key) {
1150 if (isArray(value)) {
1151 forEach(value, function(arrayValue) {
1152 parts.push(encodeUriQuery(key, true) +
1153 (arrayValue === true ? '' : '=' + encodeUriQuery(arrayValue, true)));
1156 parts.push(encodeUriQuery(key, true) +
1157 (value === true ? '' : '=' + encodeUriQuery(value, true)));
1160 return parts.length ? parts.join('&') : '';
1165 * We need our custom method because encodeURIComponent is too aggressive and doesn't follow
1166 * http://www.ietf.org/rfc/rfc3986.txt with regards to the character set (pchar) allowed in path
1169 * pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
1170 * pct-encoded = "%" HEXDIG HEXDIG
1171 * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
1172 * sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
1173 * / "*" / "+" / "," / ";" / "="
1175 function encodeUriSegment(val) {
1176 return encodeUriQuery(val, true).
1177 replace(/%26/gi, '&').
1178 replace(/%3D/gi, '=').
1179 replace(/%2B/gi, '+');
1184 * This method is intended for encoding *key* or *value* parts of query component. We need a custom
1185 * method because encodeURIComponent is too aggressive and encodes stuff that doesn't have to be
1186 * encoded per http://tools.ietf.org/html/rfc3986:
1187 * query = *( pchar / "/" / "?" )
1188 * pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
1189 * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
1190 * pct-encoded = "%" HEXDIG HEXDIG
1191 * sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
1192 * / "*" / "+" / "," / ";" / "="
1194 function encodeUriQuery(val, pctEncodeSpaces) {
1195 return encodeURIComponent(val).
1196 replace(/%40/gi, '@').
1197 replace(/%3A/gi, ':').
1198 replace(/%24/g, '$').
1199 replace(/%2C/gi, ',').
1200 replace(/%3B/gi, ';').
1201 replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
1204 var ngAttrPrefixes = ['ng-', 'data-ng-', 'ng:', 'x-ng-'];
1206 function getNgAttribute(element, ngAttr) {
1207 var attr, i, ii = ngAttrPrefixes.length;
1208 element = jqLite(element);
1209 for (i = 0; i < ii; ++i) {
1210 attr = ngAttrPrefixes[i] + ngAttr;
1211 if (isString(attr = element.attr(attr))) {
1224 * @param {angular.Module} ngApp an optional application
1225 * {@link angular.module module} name to load.
1226 * @param {boolean=} ngStrictDi if this attribute is present on the app element, the injector will be
1227 * created in "strict-di" mode. This means that the application will fail to invoke functions which
1228 * do not use explicit function annotation (and are thus unsuitable for minification), as described
1229 * in {@link guide/di the Dependency Injection guide}, and useful debugging info will assist in
1230 * tracking down the root of these bugs.
1234 * Use this directive to **auto-bootstrap** an AngularJS application. The `ngApp` directive
1235 * designates the **root element** of the application and is typically placed near the root element
1236 * of the page - e.g. on the `<body>` or `<html>` tags.
1238 * Only one AngularJS application can be auto-bootstrapped per HTML document. The first `ngApp`
1239 * found in the document will be used to define the root element to auto-bootstrap as an
1240 * application. To run multiple applications in an HTML document you must manually bootstrap them using
1241 * {@link angular.bootstrap} instead. AngularJS applications cannot be nested within each other.
1243 * You can specify an **AngularJS module** to be used as the root module for the application. This
1244 * module will be loaded into the {@link auto.$injector} when the application is bootstrapped. It
1245 * should contain the application code needed or have dependencies on other modules that will
1246 * contain the code. See {@link angular.module} for more information.
1248 * In the example below if the `ngApp` directive were not placed on the `html` element then the
1249 * document would not be compiled, the `AppController` would not be instantiated and the `{{ a+b }}`
1250 * would not be resolved to `3`.
1252 * `ngApp` is the easiest, and most common way to bootstrap an application.
1254 <example module="ngAppDemo">
1255 <file name="index.html">
1256 <div ng-controller="ngAppDemoController">
1257 I can add: {{a}} + {{b}} = {{ a+b }}
1260 <file name="script.js">
1261 angular.module('ngAppDemo', []).controller('ngAppDemoController', function($scope) {
1268 * Using `ngStrictDi`, you would see something like this:
1270 <example ng-app-included="true">
1271 <file name="index.html">
1272 <div ng-app="ngAppStrictDemo" ng-strict-di>
1273 <div ng-controller="GoodController1">
1274 I can add: {{a}} + {{b}} = {{ a+b }}
1276 <p>This renders because the controller does not fail to
1277 instantiate, by using explicit annotation style (see
1278 script.js for details)
1282 <div ng-controller="GoodController2">
1283 Name: <input ng-model="name"><br />
1286 <p>This renders because the controller does not fail to
1287 instantiate, by using explicit annotation style
1288 (see script.js for details)
1292 <div ng-controller="BadController">
1293 I can add: {{a}} + {{b}} = {{ a+b }}
1295 <p>The controller could not be instantiated, due to relying
1296 on automatic function annotations (which are disabled in
1297 strict mode). As such, the content of this section is not
1298 interpolated, and there should be an error in your web console.
1303 <file name="script.js">
1304 angular.module('ngAppStrictDemo', [])
1305 // BadController will fail to instantiate, due to relying on automatic function annotation,
1306 // rather than an explicit annotation
1307 .controller('BadController', function($scope) {
1311 // Unlike BadController, GoodController1 and GoodController2 will not fail to be instantiated,
1312 // due to using explicit annotations using the array style and $inject property, respectively.
1313 .controller('GoodController1', ['$scope', function($scope) {
1317 .controller('GoodController2', GoodController2);
1318 function GoodController2($scope) {
1319 $scope.name = "World";
1321 GoodController2.$inject = ['$scope'];
1323 <file name="style.css">
1324 div[ng-controller] {
1326 -webkit-border-radius: 4px;
1331 div[ng-controller^=Good] {
1332 border-color: #d6e9c6;
1333 background-color: #dff0d8;
1336 div[ng-controller^=Bad] {
1337 border-color: #ebccd1;
1338 background-color: #f2dede;
1345 function angularInit(element, bootstrap) {
1350 // The element `element` has priority over any other element
1351 forEach(ngAttrPrefixes, function(prefix) {
1352 var name = prefix + 'app';
1354 if (!appElement && element.hasAttribute && element.hasAttribute(name)) {
1355 appElement = element;
1356 module = element.getAttribute(name);
1359 forEach(ngAttrPrefixes, function(prefix) {
1360 var name = prefix + 'app';
1363 if (!appElement && (candidate = element.querySelector('[' + name.replace(':', '\\:') + ']'))) {
1364 appElement = candidate;
1365 module = candidate.getAttribute(name);
1369 config.strictDi = getNgAttribute(appElement, "strict-di") !== null;
1370 bootstrap(appElement, module ? [module] : [], config);
1376 * @name angular.bootstrap
1379 * Use this function to manually start up angular application.
1381 * See: {@link guide/bootstrap Bootstrap}
1383 * Note that Protractor based end-to-end tests cannot use this function to bootstrap manually.
1384 * They must use {@link ng.directive:ngApp ngApp}.
1386 * Angular will detect if it has been loaded into the browser more than once and only allow the
1387 * first loaded script to be bootstrapped and will report a warning to the browser console for
1388 * each of the subsequent scripts. This prevents strange results in applications, where otherwise
1389 * multiple instances of Angular try to work on the DOM.
1395 * <div ng-controller="WelcomeController">
1399 * <script src="angular.js"></script>
1401 * var app = angular.module('demo', [])
1402 * .controller('WelcomeController', function($scope) {
1403 * $scope.greeting = 'Welcome!';
1405 * angular.bootstrap(document, ['demo']);
1411 * @param {DOMElement} element DOM element which is the root of angular application.
1412 * @param {Array<String|Function|Array>=} modules an array of modules to load into the application.
1413 * Each item in the array should be the name of a predefined module or a (DI annotated)
1414 * function that will be invoked by the injector as a `config` block.
1415 * See: {@link angular.module modules}
1416 * @param {Object=} config an object for defining configuration options for the application. The
1417 * following keys are supported:
1419 * * `strictDi` - disable automatic function annotation for the application. This is meant to
1420 * assist in finding bugs which break minified code. Defaults to `false`.
1422 * @returns {auto.$injector} Returns the newly created injector for this app.
1424 function bootstrap(element, modules, config) {
1425 if (!isObject(config)) config = {};
1426 var defaultConfig = {
1429 config = extend(defaultConfig, config);
1430 var doBootstrap = function() {
1431 element = jqLite(element);
1433 if (element.injector()) {
1434 var tag = (element[0] === document) ? 'document' : startingTag(element);
1435 //Encode angle brackets to prevent input from being sanitized to empty string #8683
1438 "App Already Bootstrapped with this Element '{0}'",
1439 tag.replace(/</,'<').replace(/>/,'>'));
1442 modules = modules || [];
1443 modules.unshift(['$provide', function($provide) {
1444 $provide.value('$rootElement', element);
1447 if (config.debugInfoEnabled) {
1448 // Pushing so that this overrides `debugInfoEnabled` setting defined in user's `modules`.
1449 modules.push(['$compileProvider', function($compileProvider) {
1450 $compileProvider.debugInfoEnabled(true);
1454 modules.unshift('ng');
1455 var injector = createInjector(modules, config.strictDi);
1456 injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector',
1457 function bootstrapApply(scope, element, compile, injector) {
1458 scope.$apply(function() {
1459 element.data('$injector', injector);
1460 compile(element)(scope);
1467 var NG_ENABLE_DEBUG_INFO = /^NG_ENABLE_DEBUG_INFO!/;
1468 var NG_DEFER_BOOTSTRAP = /^NG_DEFER_BOOTSTRAP!/;
1470 if (window && NG_ENABLE_DEBUG_INFO.test(window.name)) {
1471 config.debugInfoEnabled = true;
1472 window.name = window.name.replace(NG_ENABLE_DEBUG_INFO, '');
1475 if (window && !NG_DEFER_BOOTSTRAP.test(window.name)) {
1476 return doBootstrap();
1479 window.name = window.name.replace(NG_DEFER_BOOTSTRAP, '');
1480 angular.resumeBootstrap = function(extraModules) {
1481 forEach(extraModules, function(module) {
1482 modules.push(module);
1484 return doBootstrap();
1487 if (isFunction(angular.resumeDeferredBootstrap)) {
1488 angular.resumeDeferredBootstrap();
1494 * @name angular.reloadWithDebugInfo
1497 * Use this function to reload the current application with debug information turned on.
1498 * This takes precedence over a call to `$compileProvider.debugInfoEnabled(false)`.
1500 * See {@link ng.$compileProvider#debugInfoEnabled} for more.
1502 function reloadWithDebugInfo() {
1503 window.name = 'NG_ENABLE_DEBUG_INFO!' + window.name;
1504 window.location.reload();
1508 * @name angular.getTestability
1511 * Get the testability service for the instance of Angular on the given
1513 * @param {DOMElement} element DOM element which is the root of angular application.
1515 function getTestability(rootElement) {
1516 var injector = angular.element(rootElement).injector();
1518 throw ngMinErr('test',
1519 'no injector found for element argument to getTestability');
1521 return injector.get('$$testability');
1524 var SNAKE_CASE_REGEXP = /[A-Z]/g;
1525 function snake_case(name, separator) {
1526 separator = separator || '_';
1527 return name.replace(SNAKE_CASE_REGEXP, function(letter, pos) {
1528 return (pos ? separator : '') + letter.toLowerCase();
1532 var bindJQueryFired = false;
1533 var skipDestroyOnNextJQueryCleanData;
1534 function bindJQuery() {
1535 var originalCleanData;
1537 if (bindJQueryFired) {
1541 // bind to jQuery if present;
1542 jQuery = window.jQuery;
1543 // Use jQuery if it exists with proper functionality, otherwise default to us.
1544 // Angular 1.2+ requires jQuery 1.7+ for on()/off() support.
1545 // Angular 1.3+ technically requires at least jQuery 2.1+ but it may work with older
1546 // versions. It will not work for sure with jQuery <1.7, though.
1547 if (jQuery && jQuery.fn.on) {
1550 scope: JQLitePrototype.scope,
1551 isolateScope: JQLitePrototype.isolateScope,
1552 controller: JQLitePrototype.controller,
1553 injector: JQLitePrototype.injector,
1554 inheritedData: JQLitePrototype.inheritedData
1557 // All nodes removed from the DOM via various jQuery APIs like .remove()
1558 // are passed through jQuery.cleanData. Monkey-patch this method to fire
1559 // the $destroy event on all removed nodes.
1560 originalCleanData = jQuery.cleanData;
1561 jQuery.cleanData = function(elems) {
1563 if (!skipDestroyOnNextJQueryCleanData) {
1564 for (var i = 0, elem; (elem = elems[i]) != null; i++) {
1565 events = jQuery._data(elem, "events");
1566 if (events && events.$destroy) {
1567 jQuery(elem).triggerHandler('$destroy');
1571 skipDestroyOnNextJQueryCleanData = false;
1573 originalCleanData(elems);
1579 angular.element = jqLite;
1581 // Prevent double-proxying.
1582 bindJQueryFired = true;
1586 * throw error if the argument is falsy.
1588 function assertArg(arg, name, reason) {
1590 throw ngMinErr('areq', "Argument '{0}' is {1}", (name || '?'), (reason || "required"));
1595 function assertArgFn(arg, name, acceptArrayAnnotation) {
1596 if (acceptArrayAnnotation && isArray(arg)) {
1597 arg = arg[arg.length - 1];
1600 assertArg(isFunction(arg), name, 'not a function, got ' +
1601 (arg && typeof arg === 'object' ? arg.constructor.name || 'Object' : typeof arg));
1606 * throw error if the name given is hasOwnProperty
1607 * @param {String} name the name to test
1608 * @param {String} context the context in which the name is used, such as module or directive
1610 function assertNotHasOwnProperty(name, context) {
1611 if (name === 'hasOwnProperty') {
1612 throw ngMinErr('badname', "hasOwnProperty is not a valid {0} name", context);
1617 * Return the value accessible from the object by path. Any undefined traversals are ignored
1618 * @param {Object} obj starting object
1619 * @param {String} path path to traverse
1620 * @param {boolean} [bindFnToScope=true]
1621 * @returns {Object} value as accessible by path
1623 //TODO(misko): this function needs to be removed
1624 function getter(obj, path, bindFnToScope) {
1625 if (!path) return obj;
1626 var keys = path.split('.');
1628 var lastInstance = obj;
1629 var len = keys.length;
1631 for (var i = 0; i < len; i++) {
1634 obj = (lastInstance = obj)[key];
1637 if (!bindFnToScope && isFunction(obj)) {
1638 return bind(lastInstance, obj);
1644 * Return the DOM siblings between the first and last node in the given array.
1645 * @param {Array} array like object
1646 * @returns {jqLite} jqLite collection containing the nodes
1648 function getBlockNodes(nodes) {
1649 // TODO(perf): just check if all items in `nodes` are siblings and if they are return the original
1650 // collection, otherwise update the original collection.
1651 var node = nodes[0];
1652 var endNode = nodes[nodes.length - 1];
1653 var blockNodes = [node];
1656 node = node.nextSibling;
1658 blockNodes.push(node);
1659 } while (node !== endNode);
1661 return jqLite(blockNodes);
1666 * Creates a new object without a prototype. This object is useful for lookup without having to
1667 * guard against prototypically inherited properties via hasOwnProperty.
1669 * Related micro-benchmarks:
1670 * - http://jsperf.com/object-create2
1671 * - http://jsperf.com/proto-map-lookup/2
1672 * - http://jsperf.com/for-in-vs-object-keys2
1676 function createMap() {
1677 return Object.create(null);
1680 var NODE_TYPE_ELEMENT = 1;
1681 var NODE_TYPE_ATTRIBUTE = 2;
1682 var NODE_TYPE_TEXT = 3;
1683 var NODE_TYPE_COMMENT = 8;
1684 var NODE_TYPE_DOCUMENT = 9;
1685 var NODE_TYPE_DOCUMENT_FRAGMENT = 11;
1689 * @name angular.Module
1693 * Interface for configuring angular {@link angular.module modules}.
1696 function setupModuleLoader(window) {
1698 var $injectorMinErr = minErr('$injector');
1699 var ngMinErr = minErr('ng');
1701 function ensure(obj, name, factory) {
1702 return obj[name] || (obj[name] = factory());
1705 var angular = ensure(window, 'angular', Object);
1707 // We need to expose `angular.$$minErr` to modules such as `ngResource` that reference it during bootstrap
1708 angular.$$minErr = angular.$$minErr || minErr;
1710 return ensure(angular, 'module', function() {
1711 /** @type {Object.<string, angular.Module>} */
1716 * @name angular.module
1720 * The `angular.module` is a global place for creating, registering and retrieving Angular
1722 * All modules (angular core or 3rd party) that should be available to an application must be
1723 * registered using this mechanism.
1725 * When passed two or more arguments, a new module is created. If passed only one argument, an
1726 * existing module (the name passed as the first argument to `module`) is retrieved.
1731 * A module is a collection of services, directives, controllers, filters, and configuration information.
1732 * `angular.module` is used to configure the {@link auto.$injector $injector}.
1735 * // Create a new module
1736 * var myModule = angular.module('myModule', []);
1738 * // register a new service
1739 * myModule.value('appName', 'MyCoolApp');
1741 * // configure existing services inside initialization blocks.
1742 * myModule.config(['$locationProvider', function($locationProvider) {
1743 * // Configure existing providers
1744 * $locationProvider.hashPrefix('!');
1748 * Then you can create an injector and load your modules like this:
1751 * var injector = angular.injector(['ng', 'myModule'])
1754 * However it's more likely that you'll just use
1755 * {@link ng.directive:ngApp ngApp} or
1756 * {@link angular.bootstrap} to simplify this process for you.
1758 * @param {!string} name The name of the module to create or retrieve.
1759 * @param {!Array.<string>=} requires If specified then new module is being created. If
1760 * unspecified then the module is being retrieved for further configuration.
1761 * @param {Function=} configFn Optional configuration function for the module. Same as
1762 * {@link angular.Module#config Module#config()}.
1763 * @returns {module} new module with the {@link angular.Module} api.
1765 return function module(name, requires, configFn) {
1766 var assertNotHasOwnProperty = function(name, context) {
1767 if (name === 'hasOwnProperty') {
1768 throw ngMinErr('badname', 'hasOwnProperty is not a valid {0} name', context);
1772 assertNotHasOwnProperty(name, 'module');
1773 if (requires && modules.hasOwnProperty(name)) {
1774 modules[name] = null;
1776 return ensure(modules, name, function() {
1778 throw $injectorMinErr('nomod', "Module '{0}' is not available! You either misspelled " +
1779 "the module name or forgot to load it. If registering a module ensure that you " +
1780 "specify the dependencies as the second argument.", name);
1783 /** @type {!Array.<Array.<*>>} */
1784 var invokeQueue = [];
1786 /** @type {!Array.<Function>} */
1787 var configBlocks = [];
1789 /** @type {!Array.<Function>} */
1792 var config = invokeLater('$injector', 'invoke', 'push', configBlocks);
1794 /** @type {angular.Module} */
1795 var moduleInstance = {
1797 _invokeQueue: invokeQueue,
1798 _configBlocks: configBlocks,
1799 _runBlocks: runBlocks,
1803 * @name angular.Module#requires
1807 * Holds the list of modules which the injector will load before the current module is
1814 * @name angular.Module#name
1818 * Name of the module.
1825 * @name angular.Module#provider
1827 * @param {string} name service name
1828 * @param {Function} providerType Construction function for creating new instance of the
1831 * See {@link auto.$provide#provider $provide.provider()}.
1833 provider: invokeLater('$provide', 'provider'),
1837 * @name angular.Module#factory
1839 * @param {string} name service name
1840 * @param {Function} providerFunction Function for creating new instance of the service.
1842 * See {@link auto.$provide#factory $provide.factory()}.
1844 factory: invokeLater('$provide', 'factory'),
1848 * @name angular.Module#service
1850 * @param {string} name service name
1851 * @param {Function} constructor A constructor function that will be instantiated.
1853 * See {@link auto.$provide#service $provide.service()}.
1855 service: invokeLater('$provide', 'service'),
1859 * @name angular.Module#value
1861 * @param {string} name service name
1862 * @param {*} object Service instance object.
1864 * See {@link auto.$provide#value $provide.value()}.
1866 value: invokeLater('$provide', 'value'),
1870 * @name angular.Module#constant
1872 * @param {string} name constant name
1873 * @param {*} object Constant value.
1875 * Because the constant are fixed, they get applied before other provide methods.
1876 * See {@link auto.$provide#constant $provide.constant()}.
1878 constant: invokeLater('$provide', 'constant', 'unshift'),
1882 * @name angular.Module#animation
1884 * @param {string} name animation name
1885 * @param {Function} animationFactory Factory function for creating new instance of an
1889 * **NOTE**: animations take effect only if the **ngAnimate** module is loaded.
1892 * Defines an animation hook that can be later used with
1893 * {@link ngAnimate.$animate $animate} service and directives that use this service.
1896 * module.animation('.animation-name', function($inject1, $inject2) {
1898 * eventName : function(element, done) {
1899 * //code to run the animation
1900 * //once complete, then run done()
1901 * return function cancellationFunction(element) {
1902 * //code to cancel the animation
1909 * See {@link ng.$animateProvider#register $animateProvider.register()} and
1910 * {@link ngAnimate ngAnimate module} for more information.
1912 animation: invokeLater('$animateProvider', 'register'),
1916 * @name angular.Module#filter
1918 * @param {string} name Filter name - this must be a valid angular expression identifier
1919 * @param {Function} filterFactory Factory function for creating new instance of filter.
1921 * See {@link ng.$filterProvider#register $filterProvider.register()}.
1923 * <div class="alert alert-warning">
1924 * **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`.
1925 * Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace
1926 * your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
1927 * (`myapp_subsection_filterx`).
1930 filter: invokeLater('$filterProvider', 'register'),
1934 * @name angular.Module#controller
1936 * @param {string|Object} name Controller name, or an object map of controllers where the
1937 * keys are the names and the values are the constructors.
1938 * @param {Function} constructor Controller constructor function.
1940 * See {@link ng.$controllerProvider#register $controllerProvider.register()}.
1942 controller: invokeLater('$controllerProvider', 'register'),
1946 * @name angular.Module#directive
1948 * @param {string|Object} name Directive name, or an object map of directives where the
1949 * keys are the names and the values are the factories.
1950 * @param {Function} directiveFactory Factory function for creating new instance of
1953 * See {@link ng.$compileProvider#directive $compileProvider.directive()}.
1955 directive: invokeLater('$compileProvider', 'directive'),
1959 * @name angular.Module#config
1961 * @param {Function} configFn Execute this function on module load. Useful for service
1964 * Use this method to register work which needs to be performed on module loading.
1965 * For more about how to configure services, see
1966 * {@link providers#provider-recipe Provider Recipe}.
1972 * @name angular.Module#run
1974 * @param {Function} initializationFn Execute this function after injector creation.
1975 * Useful for application initialization.
1977 * Use this method to register work which should be performed when the injector is done
1978 * loading all modules.
1980 run: function(block) {
1981 runBlocks.push(block);
1990 return moduleInstance;
1993 * @param {string} provider
1994 * @param {string} method
1995 * @param {String=} insertMethod
1996 * @returns {angular.Module}
1998 function invokeLater(provider, method, insertMethod, queue) {
1999 if (!queue) queue = invokeQueue;
2001 queue[insertMethod || 'push']([provider, method, arguments]);
2002 return moduleInstance;
2011 /* global: toDebugString: true */
2013 function serializeObject(obj) {
2016 return JSON.stringify(obj, function(key, val) {
2017 val = toJsonReplacer(key, val);
2018 if (isObject(val)) {
2020 if (seen.indexOf(val) >= 0) return '<<already seen>>';
2028 function toDebugString(obj) {
2029 if (typeof obj === 'function') {
2030 return obj.toString().replace(/ \{[\s\S]*$/, '');
2031 } else if (typeof obj === 'undefined') {
2033 } else if (typeof obj !== 'string') {
2034 return serializeObject(obj);
2039 /* global angularModule: true,
2045 htmlAnchorDirective,
2054 ngBindHtmlDirective,
2055 ngBindTemplateDirective,
2057 ngClassEvenDirective,
2058 ngClassOddDirective,
2061 ngControllerDirective,
2066 ngIncludeFillContentDirective,
2068 ngNonBindableDirective,
2069 ngPluralizeDirective,
2074 ngSwitchWhenDirective,
2075 ngSwitchDefaultDirective,
2077 ngTranscludeDirective,
2090 ngModelOptionsDirective,
2091 ngAttributeAliasDirectives,
2094 $AnchorScrollProvider,
2097 $CacheFactoryProvider,
2098 $ControllerProvider,
2100 $ExceptionHandlerProvider,
2102 $InterpolateProvider,
2105 $HttpBackendProvider,
2112 $$SanitizeUriProvider,
2114 $SceDelegateProvider,
2116 $TemplateCacheProvider,
2117 $TemplateRequestProvider,
2118 $$TestabilityProvider,
2121 $$AsyncCallbackProvider,
2129 * @name angular.version
2132 * An object that contains information about the current AngularJS version. This object has the
2133 * following properties:
2135 * - `full` – `{string}` – Full version string, such as "0.9.18".
2136 * - `major` – `{number}` – Major version number, such as "0".
2137 * - `minor` – `{number}` – Minor version number, such as "9".
2138 * - `dot` – `{number}` – Dot version number, such as "18".
2139 * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat".
2142 full: '1.3.20', // all of these placeholder strings will be replaced by grunt's
2143 major: 1, // package task
2146 codeName: 'shallow-translucence'
2150 function publishExternalAPI(angular) {
2152 'bootstrap': bootstrap,
2158 'injector': createInjector,
2162 'fromJson': fromJson,
2163 'identity': identity,
2164 'isUndefined': isUndefined,
2165 'isDefined': isDefined,
2166 'isString': isString,
2167 'isFunction': isFunction,
2168 'isObject': isObject,
2169 'isNumber': isNumber,
2170 'isElement': isElement,
2174 'lowercase': lowercase,
2175 'uppercase': uppercase,
2176 'callbacks': {counter: 0},
2177 'getTestability': getTestability,
2180 'reloadWithDebugInfo': reloadWithDebugInfo
2183 angularModule = setupModuleLoader(window);
2185 angularModule('ngLocale');
2187 angularModule('ngLocale', []).provider('$locale', $LocaleProvider);
2190 angularModule('ng', ['ngLocale'], ['$provide',
2191 function ngModule($provide) {
2192 // $$sanitizeUriProvider needs to be before $compileProvider as it is used by it.
2194 $$sanitizeUri: $$SanitizeUriProvider
2196 $provide.provider('$compile', $CompileProvider).
2198 a: htmlAnchorDirective,
2199 input: inputDirective,
2200 textarea: inputDirective,
2201 form: formDirective,
2202 script: scriptDirective,
2203 select: selectDirective,
2204 style: styleDirective,
2205 option: optionDirective,
2206 ngBind: ngBindDirective,
2207 ngBindHtml: ngBindHtmlDirective,
2208 ngBindTemplate: ngBindTemplateDirective,
2209 ngClass: ngClassDirective,
2210 ngClassEven: ngClassEvenDirective,
2211 ngClassOdd: ngClassOddDirective,
2212 ngCloak: ngCloakDirective,
2213 ngController: ngControllerDirective,
2214 ngForm: ngFormDirective,
2215 ngHide: ngHideDirective,
2216 ngIf: ngIfDirective,
2217 ngInclude: ngIncludeDirective,
2218 ngInit: ngInitDirective,
2219 ngNonBindable: ngNonBindableDirective,
2220 ngPluralize: ngPluralizeDirective,
2221 ngRepeat: ngRepeatDirective,
2222 ngShow: ngShowDirective,
2223 ngStyle: ngStyleDirective,
2224 ngSwitch: ngSwitchDirective,
2225 ngSwitchWhen: ngSwitchWhenDirective,
2226 ngSwitchDefault: ngSwitchDefaultDirective,
2227 ngOptions: ngOptionsDirective,
2228 ngTransclude: ngTranscludeDirective,
2229 ngModel: ngModelDirective,
2230 ngList: ngListDirective,
2231 ngChange: ngChangeDirective,
2232 pattern: patternDirective,
2233 ngPattern: patternDirective,
2234 required: requiredDirective,
2235 ngRequired: requiredDirective,
2236 minlength: minlengthDirective,
2237 ngMinlength: minlengthDirective,
2238 maxlength: maxlengthDirective,
2239 ngMaxlength: maxlengthDirective,
2240 ngValue: ngValueDirective,
2241 ngModelOptions: ngModelOptionsDirective
2244 ngInclude: ngIncludeFillContentDirective
2246 directive(ngAttributeAliasDirectives).
2247 directive(ngEventDirectives);
2249 $anchorScroll: $AnchorScrollProvider,
2250 $animate: $AnimateProvider,
2251 $browser: $BrowserProvider,
2252 $cacheFactory: $CacheFactoryProvider,
2253 $controller: $ControllerProvider,
2254 $document: $DocumentProvider,
2255 $exceptionHandler: $ExceptionHandlerProvider,
2256 $filter: $FilterProvider,
2257 $interpolate: $InterpolateProvider,
2258 $interval: $IntervalProvider,
2259 $http: $HttpProvider,
2260 $httpBackend: $HttpBackendProvider,
2261 $location: $LocationProvider,
2263 $parse: $ParseProvider,
2264 $rootScope: $RootScopeProvider,
2268 $sceDelegate: $SceDelegateProvider,
2269 $sniffer: $SnifferProvider,
2270 $templateCache: $TemplateCacheProvider,
2271 $templateRequest: $TemplateRequestProvider,
2272 $$testability: $$TestabilityProvider,
2273 $timeout: $TimeoutProvider,
2274 $window: $WindowProvider,
2275 $$rAF: $$RAFProvider,
2276 $$asyncCallback: $$AsyncCallbackProvider,
2277 $$jqLite: $$jqLiteProvider
2283 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2284 * Any commits to this file should be reviewed with security in mind. *
2285 * Changes to this file can potentially create security vulnerabilities. *
2286 * An approval from 2 Core members with history of modifying *
2287 * this file is required. *
2289 * Does the change somehow allow for arbitrary javascript to be executed? *
2290 * Or allows for someone to change the prototype of built-in objects? *
2291 * Or gives undesired access to variables likes document or window? *
2292 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2294 /* global JQLitePrototype: true,
2295 addEventListenerFn: true,
2296 removeEventListenerFn: true,
2301 //////////////////////////////////
2303 //////////////////////////////////
2307 * @name angular.element
2312 * Wraps a raw DOM element or HTML string as a [jQuery](http://jquery.com) element.
2314 * If jQuery is available, `angular.element` is an alias for the
2315 * [jQuery](http://api.jquery.com/jQuery/) function. If jQuery is not available, `angular.element`
2316 * delegates to Angular's built-in subset of jQuery, called "jQuery lite" or "jqLite."
2318 * <div class="alert alert-success">jqLite is a tiny, API-compatible subset of jQuery that allows
2319 * Angular to manipulate the DOM in a cross-browser compatible way. **jqLite** implements only the most
2320 * commonly needed functionality with the goal of having a very small footprint.</div>
2322 * To use `jQuery`, simply ensure it is loaded before the `angular.js` file.
2324 * <div class="alert">**Note:** all element references in Angular are always wrapped with jQuery or
2325 * jqLite; they are never raw DOM references.</div>
2327 * ## Angular's jqLite
2328 * jqLite provides only the following jQuery methods:
2330 * - [`addClass()`](http://api.jquery.com/addClass/)
2331 * - [`after()`](http://api.jquery.com/after/)
2332 * - [`append()`](http://api.jquery.com/append/)
2333 * - [`attr()`](http://api.jquery.com/attr/) - Does not support functions as parameters
2334 * - [`bind()`](http://api.jquery.com/bind/) - Does not support namespaces, selectors or eventData
2335 * - [`children()`](http://api.jquery.com/children/) - Does not support selectors
2336 * - [`clone()`](http://api.jquery.com/clone/)
2337 * - [`contents()`](http://api.jquery.com/contents/)
2338 * - [`css()`](http://api.jquery.com/css/) - Only retrieves inline-styles, does not call `getComputedStyle()`. As a setter, does not convert numbers to strings or append 'px'.
2339 * - [`data()`](http://api.jquery.com/data/)
2340 * - [`detach()`](http://api.jquery.com/detach/)
2341 * - [`empty()`](http://api.jquery.com/empty/)
2342 * - [`eq()`](http://api.jquery.com/eq/)
2343 * - [`find()`](http://api.jquery.com/find/) - Limited to lookups by tag name
2344 * - [`hasClass()`](http://api.jquery.com/hasClass/)
2345 * - [`html()`](http://api.jquery.com/html/)
2346 * - [`next()`](http://api.jquery.com/next/) - Does not support selectors
2347 * - [`on()`](http://api.jquery.com/on/) - Does not support namespaces, selectors or eventData
2348 * - [`off()`](http://api.jquery.com/off/) - Does not support namespaces or selectors
2349 * - [`one()`](http://api.jquery.com/one/) - Does not support namespaces or selectors
2350 * - [`parent()`](http://api.jquery.com/parent/) - Does not support selectors
2351 * - [`prepend()`](http://api.jquery.com/prepend/)
2352 * - [`prop()`](http://api.jquery.com/prop/)
2353 * - [`ready()`](http://api.jquery.com/ready/)
2354 * - [`remove()`](http://api.jquery.com/remove/)
2355 * - [`removeAttr()`](http://api.jquery.com/removeAttr/)
2356 * - [`removeClass()`](http://api.jquery.com/removeClass/)
2357 * - [`removeData()`](http://api.jquery.com/removeData/)
2358 * - [`replaceWith()`](http://api.jquery.com/replaceWith/)
2359 * - [`text()`](http://api.jquery.com/text/)
2360 * - [`toggleClass()`](http://api.jquery.com/toggleClass/)
2361 * - [`triggerHandler()`](http://api.jquery.com/triggerHandler/) - Passes a dummy event object to handlers.
2362 * - [`unbind()`](http://api.jquery.com/unbind/) - Does not support namespaces
2363 * - [`val()`](http://api.jquery.com/val/)
2364 * - [`wrap()`](http://api.jquery.com/wrap/)
2366 * ## jQuery/jqLite Extras
2367 * Angular also provides the following additional methods and events to both jQuery and jqLite:
2370 * - `$destroy` - AngularJS intercepts all jqLite/jQuery's DOM destruction apis and fires this event
2371 * on all DOM nodes being removed. This can be used to clean up any 3rd party bindings to the DOM
2372 * element before it is removed.
2375 * - `controller(name)` - retrieves the controller of the current element or its parent. By default
2376 * retrieves controller associated with the `ngController` directive. If `name` is provided as
2377 * camelCase directive name, then the controller for this directive will be retrieved (e.g.
2379 * - `injector()` - retrieves the injector of the current element or its parent.
2380 * - `scope()` - retrieves the {@link ng.$rootScope.Scope scope} of the current
2381 * element or its parent. Requires {@link guide/production#disabling-debug-data Debug Data} to
2383 * - `isolateScope()` - retrieves an isolate {@link ng.$rootScope.Scope scope} if one is attached directly to the
2384 * current element. This getter should be used only on elements that contain a directive which starts a new isolate
2385 * scope. Calling `scope()` on this element always returns the original non-isolate scope.
2386 * Requires {@link guide/production#disabling-debug-data Debug Data} to be enabled.
2387 * - `inheritedData()` - same as `data()`, but walks up the DOM until a value is found or the top
2388 * parent element is reached.
2390 * @param {string|DOMElement} element HTML string or DOMElement to be wrapped into jQuery.
2391 * @returns {Object} jQuery object.
2394 JQLite.expando = 'ng339';
2396 var jqCache = JQLite.cache = {},
2398 addEventListenerFn = function(element, type, fn) {
2399 element.addEventListener(type, fn, false);
2401 removeEventListenerFn = function(element, type, fn) {
2402 element.removeEventListener(type, fn, false);
2406 * !!! This is an undocumented "private" function !!!
2408 JQLite._data = function(node) {
2409 //jQuery always returns an object on cache miss
2410 return this.cache[node[this.expando]] || {};
2413 function jqNextId() { return ++jqId; }
2416 var SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g;
2417 var MOZ_HACK_REGEXP = /^moz([A-Z])/;
2418 var MOUSE_EVENT_MAP= { mouseleave: "mouseout", mouseenter: "mouseover"};
2419 var jqLiteMinErr = minErr('jqLite');
2422 * Converts snake_case to camelCase.
2423 * Also there is special case for Moz prefix starting with upper case letter.
2424 * @param name Name to normalize
2426 function camelCase(name) {
2428 replace(SPECIAL_CHARS_REGEXP, function(_, separator, letter, offset) {
2429 return offset ? letter.toUpperCase() : letter;
2431 replace(MOZ_HACK_REGEXP, 'Moz$1');
2434 var SINGLE_TAG_REGEXP = /^<(\w+)\s*\/?>(?:<\/\1>|)$/;
2435 var HTML_REGEXP = /<|&#?\w+;/;
2436 var TAG_NAME_REGEXP = /<([\w:]+)/;
2437 var XHTML_TAG_REGEXP = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi;
2440 'option': [1, '<select multiple="multiple">', '</select>'],
2442 'thead': [1, '<table>', '</table>'],
2443 'col': [2, '<table><colgroup>', '</colgroup></table>'],
2444 'tr': [2, '<table><tbody>', '</tbody></table>'],
2445 'td': [3, '<table><tbody><tr>', '</tr></tbody></table>'],
2446 '_default': [0, "", ""]
2449 wrapMap.optgroup = wrapMap.option;
2450 wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
2451 wrapMap.th = wrapMap.td;
2454 function jqLiteIsTextNode(html) {
2455 return !HTML_REGEXP.test(html);
2458 function jqLiteAcceptsData(node) {
2459 // The window object can accept data but has no nodeType
2460 // Otherwise we are only interested in elements (1) and documents (9)
2461 var nodeType = node.nodeType;
2462 return nodeType === NODE_TYPE_ELEMENT || !nodeType || nodeType === NODE_TYPE_DOCUMENT;
2465 function jqLiteBuildFragment(html, context) {
2467 fragment = context.createDocumentFragment(),
2470 if (jqLiteIsTextNode(html)) {
2471 // Convert non-html into a text node
2472 nodes.push(context.createTextNode(html));
2474 // Convert html into DOM nodes
2475 tmp = tmp || fragment.appendChild(context.createElement("div"));
2476 tag = (TAG_NAME_REGEXP.exec(html) || ["", ""])[1].toLowerCase();
2477 wrap = wrapMap[tag] || wrapMap._default;
2478 tmp.innerHTML = wrap[1] + html.replace(XHTML_TAG_REGEXP, "<$1></$2>") + wrap[2];
2480 // Descend through wrappers to the right content
2483 tmp = tmp.lastChild;
2486 nodes = concat(nodes, tmp.childNodes);
2488 tmp = fragment.firstChild;
2489 tmp.textContent = "";
2492 // Remove wrapper from fragment
2493 fragment.textContent = "";
2494 fragment.innerHTML = ""; // Clear inner HTML
2495 forEach(nodes, function(node) {
2496 fragment.appendChild(node);
2502 function jqLiteParseHTML(html, context) {
2503 context = context || document;
2506 if ((parsed = SINGLE_TAG_REGEXP.exec(html))) {
2507 return [context.createElement(parsed[1])];
2510 if ((parsed = jqLiteBuildFragment(html, context))) {
2511 return parsed.childNodes;
2517 /////////////////////////////////////////////
2518 function JQLite(element) {
2519 if (element instanceof JQLite) {
2525 if (isString(element)) {
2526 element = trim(element);
2529 if (!(this instanceof JQLite)) {
2530 if (argIsString && element.charAt(0) != '<') {
2531 throw jqLiteMinErr('nosel', 'Looking up elements via selectors is not supported by jqLite! See: http://docs.angularjs.org/api/angular.element');
2533 return new JQLite(element);
2537 jqLiteAddNodes(this, jqLiteParseHTML(element));
2539 jqLiteAddNodes(this, element);
2543 function jqLiteClone(element) {
2544 return element.cloneNode(true);
2547 function jqLiteDealoc(element, onlyDescendants) {
2548 if (!onlyDescendants) jqLiteRemoveData(element);
2550 if (element.querySelectorAll) {
2551 var descendants = element.querySelectorAll('*');
2552 for (var i = 0, l = descendants.length; i < l; i++) {
2553 jqLiteRemoveData(descendants[i]);
2558 function jqLiteOff(element, type, fn, unsupported) {
2559 if (isDefined(unsupported)) throw jqLiteMinErr('offargs', 'jqLite#off() does not support the `selector` argument');
2561 var expandoStore = jqLiteExpandoStore(element);
2562 var events = expandoStore && expandoStore.events;
2563 var handle = expandoStore && expandoStore.handle;
2565 if (!handle) return; //no listeners registered
2568 for (type in events) {
2569 if (type !== '$destroy') {
2570 removeEventListenerFn(element, type, handle);
2572 delete events[type];
2575 forEach(type.split(' '), function(type) {
2576 if (isDefined(fn)) {
2577 var listenerFns = events[type];
2578 arrayRemove(listenerFns || [], fn);
2579 if (listenerFns && listenerFns.length > 0) {
2584 removeEventListenerFn(element, type, handle);
2585 delete events[type];
2590 function jqLiteRemoveData(element, name) {
2591 var expandoId = element.ng339;
2592 var expandoStore = expandoId && jqCache[expandoId];
2596 delete expandoStore.data[name];
2600 if (expandoStore.handle) {
2601 if (expandoStore.events.$destroy) {
2602 expandoStore.handle({}, '$destroy');
2606 delete jqCache[expandoId];
2607 element.ng339 = undefined; // don't delete DOM expandos. IE and Chrome don't like it
2612 function jqLiteExpandoStore(element, createIfNecessary) {
2613 var expandoId = element.ng339,
2614 expandoStore = expandoId && jqCache[expandoId];
2616 if (createIfNecessary && !expandoStore) {
2617 element.ng339 = expandoId = jqNextId();
2618 expandoStore = jqCache[expandoId] = {events: {}, data: {}, handle: undefined};
2621 return expandoStore;
2625 function jqLiteData(element, key, value) {
2626 if (jqLiteAcceptsData(element)) {
2628 var isSimpleSetter = isDefined(value);
2629 var isSimpleGetter = !isSimpleSetter && key && !isObject(key);
2630 var massGetter = !key;
2631 var expandoStore = jqLiteExpandoStore(element, !isSimpleGetter);
2632 var data = expandoStore && expandoStore.data;
2634 if (isSimpleSetter) { // data('key', value)
2637 if (massGetter) { // data()
2640 if (isSimpleGetter) { // data('key')
2641 // don't force creation of expandoStore if it doesn't exist yet
2642 return data && data[key];
2643 } else { // mass-setter: data({key1: val1, key2: val2})
2651 function jqLiteHasClass(element, selector) {
2652 if (!element.getAttribute) return false;
2653 return ((" " + (element.getAttribute('class') || '') + " ").replace(/[\n\t]/g, " ").
2654 indexOf(" " + selector + " ") > -1);
2657 function jqLiteRemoveClass(element, cssClasses) {
2658 if (cssClasses && element.setAttribute) {
2659 forEach(cssClasses.split(' '), function(cssClass) {
2660 element.setAttribute('class', trim(
2661 (" " + (element.getAttribute('class') || '') + " ")
2662 .replace(/[\n\t]/g, " ")
2663 .replace(" " + trim(cssClass) + " ", " "))
2669 function jqLiteAddClass(element, cssClasses) {
2670 if (cssClasses && element.setAttribute) {
2671 var existingClasses = (' ' + (element.getAttribute('class') || '') + ' ')
2672 .replace(/[\n\t]/g, " ");
2674 forEach(cssClasses.split(' '), function(cssClass) {
2675 cssClass = trim(cssClass);
2676 if (existingClasses.indexOf(' ' + cssClass + ' ') === -1) {
2677 existingClasses += cssClass + ' ';
2681 element.setAttribute('class', trim(existingClasses));
2686 function jqLiteAddNodes(root, elements) {
2687 // THIS CODE IS VERY HOT. Don't make changes without benchmarking.
2691 // if a Node (the most common case)
2692 if (elements.nodeType) {
2693 root[root.length++] = elements;
2695 var length = elements.length;
2697 // if an Array or NodeList and not a Window
2698 if (typeof length === 'number' && elements.window !== elements) {
2700 for (var i = 0; i < length; i++) {
2701 root[root.length++] = elements[i];
2705 root[root.length++] = elements;
2712 function jqLiteController(element, name) {
2713 return jqLiteInheritedData(element, '$' + (name || 'ngController') + 'Controller');
2716 function jqLiteInheritedData(element, name, value) {
2717 // if element is the document object work with the html element instead
2718 // this makes $(document).scope() possible
2719 if (element.nodeType == NODE_TYPE_DOCUMENT) {
2720 element = element.documentElement;
2722 var names = isArray(name) ? name : [name];
2725 for (var i = 0, ii = names.length; i < ii; i++) {
2726 if ((value = jqLite.data(element, names[i])) !== undefined) return value;
2729 // If dealing with a document fragment node with a host element, and no parent, use the host
2730 // element as the parent. This enables directives within a Shadow DOM or polyfilled Shadow DOM
2731 // to lookup parent controllers.
2732 element = element.parentNode || (element.nodeType === NODE_TYPE_DOCUMENT_FRAGMENT && element.host);
2736 function jqLiteEmpty(element) {
2737 jqLiteDealoc(element, true);
2738 while (element.firstChild) {
2739 element.removeChild(element.firstChild);
2743 function jqLiteRemove(element, keepData) {
2744 if (!keepData) jqLiteDealoc(element);
2745 var parent = element.parentNode;
2746 if (parent) parent.removeChild(element);
2750 function jqLiteDocumentLoaded(action, win) {
2751 win = win || window;
2752 if (win.document.readyState === 'complete') {
2753 // Force the action to be run async for consistent behaviour
2754 // from the action's point of view
2755 // i.e. it will definitely not be in a $apply
2756 win.setTimeout(action);
2758 // No need to unbind this handler as load is only ever called once
2759 jqLite(win).on('load', action);
2763 //////////////////////////////////////////
2764 // Functions which are declared directly.
2765 //////////////////////////////////////////
2766 var JQLitePrototype = JQLite.prototype = {
2767 ready: function(fn) {
2770 function trigger() {
2776 // check if document is already loaded
2777 if (document.readyState === 'complete') {
2778 setTimeout(trigger);
2780 this.on('DOMContentLoaded', trigger); // works for modern browsers and IE9
2781 // we can not use jqLite since we are not done loading and jQuery could be loaded later.
2783 JQLite(window).on('load', trigger); // fallback to window.onload for others
2787 toString: function() {
2789 forEach(this, function(e) { value.push('' + e);});
2790 return '[' + value.join(', ') + ']';
2793 eq: function(index) {
2794 return (index >= 0) ? jqLite(this[index]) : jqLite(this[this.length + index]);
2803 //////////////////////////////////////////
2804 // Functions iterating getter/setters.
2805 // these functions return self on setter and
2807 //////////////////////////////////////////
2808 var BOOLEAN_ATTR = {};
2809 forEach('multiple,selected,checked,disabled,readOnly,required,open'.split(','), function(value) {
2810 BOOLEAN_ATTR[lowercase(value)] = value;
2812 var BOOLEAN_ELEMENTS = {};
2813 forEach('input,select,option,textarea,button,form,details'.split(','), function(value) {
2814 BOOLEAN_ELEMENTS[value] = true;
2816 var ALIASED_ATTR = {
2817 'ngMinlength': 'minlength',
2818 'ngMaxlength': 'maxlength',
2821 'ngPattern': 'pattern'
2824 function getBooleanAttrName(element, name) {
2825 // check dom last since we will most likely fail on name
2826 var booleanAttr = BOOLEAN_ATTR[name.toLowerCase()];
2828 // booleanAttr is here twice to minimize DOM access
2829 return booleanAttr && BOOLEAN_ELEMENTS[nodeName_(element)] && booleanAttr;
2832 function getAliasedAttrName(element, name) {
2833 var nodeName = element.nodeName;
2834 return (nodeName === 'INPUT' || nodeName === 'TEXTAREA') && ALIASED_ATTR[name];
2839 removeData: jqLiteRemoveData
2840 }, function(fn, name) {
2846 inheritedData: jqLiteInheritedData,
2848 scope: function(element) {
2849 // Can't use jqLiteData here directly so we stay compatible with jQuery!
2850 return jqLite.data(element, '$scope') || jqLiteInheritedData(element.parentNode || element, ['$isolateScope', '$scope']);
2853 isolateScope: function(element) {
2854 // Can't use jqLiteData here directly so we stay compatible with jQuery!
2855 return jqLite.data(element, '$isolateScope') || jqLite.data(element, '$isolateScopeNoTemplate');
2858 controller: jqLiteController,
2860 injector: function(element) {
2861 return jqLiteInheritedData(element, '$injector');
2864 removeAttr: function(element, name) {
2865 element.removeAttribute(name);
2868 hasClass: jqLiteHasClass,
2870 css: function(element, name, value) {
2871 name = camelCase(name);
2873 if (isDefined(value)) {
2874 element.style[name] = value;
2876 return element.style[name];
2880 attr: function(element, name, value) {
2881 var nodeType = element.nodeType;
2882 if (nodeType === NODE_TYPE_TEXT || nodeType === NODE_TYPE_ATTRIBUTE || nodeType === NODE_TYPE_COMMENT) {
2885 var lowercasedName = lowercase(name);
2886 if (BOOLEAN_ATTR[lowercasedName]) {
2887 if (isDefined(value)) {
2889 element[name] = true;
2890 element.setAttribute(name, lowercasedName);
2892 element[name] = false;
2893 element.removeAttribute(lowercasedName);
2896 return (element[name] ||
2897 (element.attributes.getNamedItem(name) || noop).specified)
2901 } else if (isDefined(value)) {
2902 element.setAttribute(name, value);
2903 } else if (element.getAttribute) {
2904 // the extra argument "2" is to get the right thing for a.href in IE, see jQuery code
2905 // some elements (e.g. Document) don't have get attribute, so return undefined
2906 var ret = element.getAttribute(name, 2);
2907 // normalize non-existing attributes to undefined (as jQuery)
2908 return ret === null ? undefined : ret;
2912 prop: function(element, name, value) {
2913 if (isDefined(value)) {
2914 element[name] = value;
2916 return element[name];
2924 function getText(element, value) {
2925 if (isUndefined(value)) {
2926 var nodeType = element.nodeType;
2927 return (nodeType === NODE_TYPE_ELEMENT || nodeType === NODE_TYPE_TEXT) ? element.textContent : '';
2929 element.textContent = value;
2933 val: function(element, value) {
2934 if (isUndefined(value)) {
2935 if (element.multiple && nodeName_(element) === 'select') {
2937 forEach(element.options, function(option) {
2938 if (option.selected) {
2939 result.push(option.value || option.text);
2942 return result.length === 0 ? null : result;
2944 return element.value;
2946 element.value = value;
2949 html: function(element, value) {
2950 if (isUndefined(value)) {
2951 return element.innerHTML;
2953 jqLiteDealoc(element, true);
2954 element.innerHTML = value;
2958 }, function(fn, name) {
2960 * Properties: writes return selection, reads return first value
2962 JQLite.prototype[name] = function(arg1, arg2) {
2964 var nodeCount = this.length;
2966 // jqLiteHasClass has only two arguments, but is a getter-only fn, so we need to special-case it
2967 // in a way that survives minification.
2968 // jqLiteEmpty takes no arguments but is a setter.
2969 if (fn !== jqLiteEmpty &&
2970 (((fn.length == 2 && (fn !== jqLiteHasClass && fn !== jqLiteController)) ? arg1 : arg2) === undefined)) {
2971 if (isObject(arg1)) {
2973 // we are a write, but the object properties are the key/values
2974 for (i = 0; i < nodeCount; i++) {
2975 if (fn === jqLiteData) {
2976 // data() takes the whole object in jQuery
2980 fn(this[i], key, arg1[key]);
2984 // return self for chaining
2987 // we are a read, so read the first child.
2988 // TODO: do we still need this?
2990 // Only if we have $dv do we iterate over all, otherwise it is just the first element.
2991 var jj = (value === undefined) ? Math.min(nodeCount, 1) : nodeCount;
2992 for (var j = 0; j < jj; j++) {
2993 var nodeValue = fn(this[j], arg1, arg2);
2994 value = value ? value + nodeValue : nodeValue;
2999 // we are a write, so apply to all children
3000 for (i = 0; i < nodeCount; i++) {
3001 fn(this[i], arg1, arg2);
3003 // return self for chaining
3009 function createEventHandler(element, events) {
3010 var eventHandler = function(event, type) {
3011 // jQuery specific api
3012 event.isDefaultPrevented = function() {
3013 return event.defaultPrevented;
3016 var eventFns = events[type || event.type];
3017 var eventFnsLength = eventFns ? eventFns.length : 0;
3019 if (!eventFnsLength) return;
3021 if (isUndefined(event.immediatePropagationStopped)) {
3022 var originalStopImmediatePropagation = event.stopImmediatePropagation;
3023 event.stopImmediatePropagation = function() {
3024 event.immediatePropagationStopped = true;
3026 if (event.stopPropagation) {
3027 event.stopPropagation();
3030 if (originalStopImmediatePropagation) {
3031 originalStopImmediatePropagation.call(event);
3036 event.isImmediatePropagationStopped = function() {
3037 return event.immediatePropagationStopped === true;
3040 // Copy event handlers in case event handlers array is modified during execution.
3041 if ((eventFnsLength > 1)) {
3042 eventFns = shallowCopy(eventFns);
3045 for (var i = 0; i < eventFnsLength; i++) {
3046 if (!event.isImmediatePropagationStopped()) {
3047 eventFns[i].call(element, event);
3052 // TODO: this is a hack for angularMocks/clearDataCache that makes it possible to deregister all
3053 // events on `element`
3054 eventHandler.elem = element;
3055 return eventHandler;
3058 //////////////////////////////////////////
3059 // Functions iterating traversal.
3060 // These functions chain results into a single
3062 //////////////////////////////////////////
3064 removeData: jqLiteRemoveData,
3066 on: function jqLiteOn(element, type, fn, unsupported) {
3067 if (isDefined(unsupported)) throw jqLiteMinErr('onargs', 'jqLite#on() does not support the `selector` or `eventData` parameters');
3069 // Do not add event handlers to non-elements because they will not be cleaned up.
3070 if (!jqLiteAcceptsData(element)) {
3074 var expandoStore = jqLiteExpandoStore(element, true);
3075 var events = expandoStore.events;
3076 var handle = expandoStore.handle;
3079 handle = expandoStore.handle = createEventHandler(element, events);
3082 // http://jsperf.com/string-indexof-vs-split
3083 var types = type.indexOf(' ') >= 0 ? type.split(' ') : [type];
3084 var i = types.length;
3088 var eventFns = events[type];
3093 if (type === 'mouseenter' || type === 'mouseleave') {
3094 // Refer to jQuery's implementation of mouseenter & mouseleave
3095 // Read about mouseenter and mouseleave:
3096 // http://www.quirksmode.org/js/events_mouse.html#link8
3098 jqLiteOn(element, MOUSE_EVENT_MAP[type], function(event) {
3099 var target = this, related = event.relatedTarget;
3100 // For mousenter/leave call the handler if related is outside the target.
3101 // NB: No relatedTarget if the mouse left/entered the browser window
3102 if (!related || (related !== target && !target.contains(related))) {
3103 handle(event, type);
3108 if (type !== '$destroy') {
3109 addEventListenerFn(element, type, handle);
3112 eventFns = events[type];
3120 one: function(element, type, fn) {
3121 element = jqLite(element);
3123 //add the listener twice so that when it is called
3124 //you can remove the original function and still be
3125 //able to call element.off(ev, fn) normally
3126 element.on(type, function onFn() {
3127 element.off(type, fn);
3128 element.off(type, onFn);
3130 element.on(type, fn);
3133 replaceWith: function(element, replaceNode) {
3134 var index, parent = element.parentNode;
3135 jqLiteDealoc(element);
3136 forEach(new JQLite(replaceNode), function(node) {
3138 parent.insertBefore(node, index.nextSibling);
3140 parent.replaceChild(node, element);
3146 children: function(element) {
3148 forEach(element.childNodes, function(element) {
3149 if (element.nodeType === NODE_TYPE_ELEMENT)
3150 children.push(element);
3155 contents: function(element) {
3156 return element.contentDocument || element.childNodes || [];
3159 append: function(element, node) {
3160 var nodeType = element.nodeType;
3161 if (nodeType !== NODE_TYPE_ELEMENT && nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT) return;
3163 node = new JQLite(node);
3165 for (var i = 0, ii = node.length; i < ii; i++) {
3166 var child = node[i];
3167 element.appendChild(child);
3171 prepend: function(element, node) {
3172 if (element.nodeType === NODE_TYPE_ELEMENT) {
3173 var index = element.firstChild;
3174 forEach(new JQLite(node), function(child) {
3175 element.insertBefore(child, index);
3180 wrap: function(element, wrapNode) {
3181 wrapNode = jqLite(wrapNode).eq(0).clone()[0];
3182 var parent = element.parentNode;
3184 parent.replaceChild(wrapNode, element);
3186 wrapNode.appendChild(element);
3189 remove: jqLiteRemove,
3191 detach: function(element) {
3192 jqLiteRemove(element, true);
3195 after: function(element, newElement) {
3196 var index = element, parent = element.parentNode;
3197 newElement = new JQLite(newElement);
3199 for (var i = 0, ii = newElement.length; i < ii; i++) {
3200 var node = newElement[i];
3201 parent.insertBefore(node, index.nextSibling);
3206 addClass: jqLiteAddClass,
3207 removeClass: jqLiteRemoveClass,
3209 toggleClass: function(element, selector, condition) {
3211 forEach(selector.split(' '), function(className) {
3212 var classCondition = condition;
3213 if (isUndefined(classCondition)) {
3214 classCondition = !jqLiteHasClass(element, className);
3216 (classCondition ? jqLiteAddClass : jqLiteRemoveClass)(element, className);
3221 parent: function(element) {
3222 var parent = element.parentNode;
3223 return parent && parent.nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT ? parent : null;
3226 next: function(element) {
3227 return element.nextElementSibling;
3230 find: function(element, selector) {
3231 if (element.getElementsByTagName) {
3232 return element.getElementsByTagName(selector);
3240 triggerHandler: function(element, event, extraParameters) {
3242 var dummyEvent, eventFnsCopy, handlerArgs;
3243 var eventName = event.type || event;
3244 var expandoStore = jqLiteExpandoStore(element);
3245 var events = expandoStore && expandoStore.events;
3246 var eventFns = events && events[eventName];
3249 // Create a dummy event to pass to the handlers
3251 preventDefault: function() { this.defaultPrevented = true; },
3252 isDefaultPrevented: function() { return this.defaultPrevented === true; },
3253 stopImmediatePropagation: function() { this.immediatePropagationStopped = true; },
3254 isImmediatePropagationStopped: function() { return this.immediatePropagationStopped === true; },
3255 stopPropagation: noop,
3260 // If a custom event was provided then extend our dummy event with it
3262 dummyEvent = extend(dummyEvent, event);
3265 // Copy event handlers in case event handlers array is modified during execution.
3266 eventFnsCopy = shallowCopy(eventFns);
3267 handlerArgs = extraParameters ? [dummyEvent].concat(extraParameters) : [dummyEvent];
3269 forEach(eventFnsCopy, function(fn) {
3270 if (!dummyEvent.isImmediatePropagationStopped()) {
3271 fn.apply(element, handlerArgs);
3276 }, function(fn, name) {
3278 * chaining functions
3280 JQLite.prototype[name] = function(arg1, arg2, arg3) {
3283 for (var i = 0, ii = this.length; i < ii; i++) {
3284 if (isUndefined(value)) {
3285 value = fn(this[i], arg1, arg2, arg3);
3286 if (isDefined(value)) {
3287 // any function which returns a value needs to be wrapped
3288 value = jqLite(value);
3291 jqLiteAddNodes(value, fn(this[i], arg1, arg2, arg3));
3294 return isDefined(value) ? value : this;
3297 // bind legacy bind/unbind to on/off
3298 JQLite.prototype.bind = JQLite.prototype.on;
3299 JQLite.prototype.unbind = JQLite.prototype.off;
3303 // Provider for private $$jqLite service
3304 function $$jqLiteProvider() {
3305 this.$get = function $$jqLite() {
3306 return extend(JQLite, {
3307 hasClass: function(node, classes) {
3308 if (node.attr) node = node[0];
3309 return jqLiteHasClass(node, classes);
3311 addClass: function(node, classes) {
3312 if (node.attr) node = node[0];
3313 return jqLiteAddClass(node, classes);
3315 removeClass: function(node, classes) {
3316 if (node.attr) node = node[0];
3317 return jqLiteRemoveClass(node, classes);
3324 * Computes a hash of an 'obj'.
3327 * number is number as string
3328 * object is either result of calling $$hashKey function on the object or uniquely generated id,
3329 * that is also assigned to the $$hashKey property of the object.
3332 * @returns {string} hash string such that the same input will have the same hash string.
3333 * The resulting string key is in 'type:hashKey' format.
3335 function hashKey(obj, nextUidFn) {
3336 var key = obj && obj.$$hashKey;
3339 if (typeof key === 'function') {
3340 key = obj.$$hashKey();
3345 var objType = typeof obj;
3346 if (objType == 'function' || (objType == 'object' && obj !== null)) {
3347 key = obj.$$hashKey = objType + ':' + (nextUidFn || nextUid)();
3349 key = objType + ':' + obj;
3356 * HashMap which can use objects as keys
3358 function HashMap(array, isolatedUid) {
3361 this.nextUid = function() {
3365 forEach(array, this.put, this);
3367 HashMap.prototype = {
3369 * Store key value pair
3370 * @param key key to store can be any type
3371 * @param value value to store can be any type
3373 put: function(key, value) {
3374 this[hashKey(key, this.nextUid)] = value;
3379 * @returns {Object} the value for the key
3381 get: function(key) {
3382 return this[hashKey(key, this.nextUid)];
3386 * Remove the key/value pair
3389 remove: function(key) {
3390 var value = this[key = hashKey(key, this.nextUid)];
3399 * @name angular.injector
3403 * Creates an injector object that can be used for retrieving services as well as for
3404 * dependency injection (see {@link guide/di dependency injection}).
3406 * @param {Array.<string|Function>} modules A list of module functions or their aliases. See
3407 * {@link angular.module}. The `ng` module must be explicitly added.
3408 * @param {boolean=} [strictDi=false] Whether the injector should be in strict mode, which
3409 * disallows argument name annotation inference.
3410 * @returns {injector} Injector object. See {@link auto.$injector $injector}.
3415 * // create an injector
3416 * var $injector = angular.injector(['ng']);
3418 * // use the injector to kick off your application
3419 * // use the type inference to auto inject arguments, or use implicit injection
3420 * $injector.invoke(function($rootScope, $compile, $document) {
3421 * $compile($document)($rootScope);
3422 * $rootScope.$digest();
3426 * Sometimes you want to get access to the injector of a currently running Angular app
3427 * from outside Angular. Perhaps, you want to inject and compile some markup after the
3428 * application has been bootstrapped. You can do this using the extra `injector()` added
3429 * to JQuery/jqLite elements. See {@link angular.element}.
3431 * *This is fairly rare but could be the case if a third party library is injecting the
3434 * In the following example a new block of HTML containing a `ng-controller`
3435 * directive is added to the end of the document body by JQuery. We then compile and link
3436 * it into the current AngularJS scope.
3439 * var $div = $('<div ng-controller="MyCtrl">{{content.label}}</div>');
3440 * $(document.body).append($div);
3442 * angular.element(document).injector().invoke(function($compile) {
3443 * var scope = angular.element($div).scope();
3444 * $compile($div)(scope);
3455 * Implicit module which gets automatically added to each {@link auto.$injector $injector}.
3458 var FN_ARGS = /^function\s*[^\(]*\(\s*([^\)]*)\)/m;
3459 var FN_ARG_SPLIT = /,/;
3460 var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/;
3461 var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
3462 var $injectorMinErr = minErr('$injector');
3464 function anonFn(fn) {
3465 // For anonymous functions, showing at the very least the function signature can help in
3467 var fnText = fn.toString().replace(STRIP_COMMENTS, ''),
3468 args = fnText.match(FN_ARGS);
3470 return 'function(' + (args[1] || '').replace(/[\s\r\n]+/, ' ') + ')';
3475 function annotate(fn, strictDi, name) {
3481 if (typeof fn === 'function') {
3482 if (!($inject = fn.$inject)) {
3486 if (!isString(name) || !name) {
3487 name = fn.name || anonFn(fn);
3489 throw $injectorMinErr('strictdi',
3490 '{0} is not using explicit annotation and cannot be invoked in strict mode', name);
3492 fnText = fn.toString().replace(STRIP_COMMENTS, '');
3493 argDecl = fnText.match(FN_ARGS);
3494 forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg) {
3495 arg.replace(FN_ARG, function(all, underscore, name) {
3500 fn.$inject = $inject;
3502 } else if (isArray(fn)) {
3503 last = fn.length - 1;
3504 assertArgFn(fn[last], 'fn');
3505 $inject = fn.slice(0, last);
3507 assertArgFn(fn, 'fn', true);
3512 ///////////////////////////////////////
3520 * `$injector` is used to retrieve object instances as defined by
3521 * {@link auto.$provide provider}, instantiate types, invoke methods,
3524 * The following always holds true:
3527 * var $injector = angular.injector();
3528 * expect($injector.get('$injector')).toBe($injector);
3529 * expect($injector.invoke(function($injector) {
3531 * })).toBe($injector);
3534 * # Injection Function Annotation
3536 * JavaScript does not have annotations, and annotations are needed for dependency injection. The
3537 * following are all valid ways of annotating function with injection arguments and are equivalent.
3540 * // inferred (only works if code not minified/obfuscated)
3541 * $injector.invoke(function(serviceA){});
3544 * function explicit(serviceA) {};
3545 * explicit.$inject = ['serviceA'];
3546 * $injector.invoke(explicit);
3549 * $injector.invoke(['serviceA', function(serviceA){}]);
3554 * In JavaScript calling `toString()` on a function returns the function definition. The definition
3555 * can then be parsed and the function arguments can be extracted. This method of discovering
3556 * annotations is disallowed when the injector is in strict mode.
3557 * *NOTE:* This does not work with minification, and obfuscation tools since these tools change the
3560 * ## `$inject` Annotation
3561 * By adding an `$inject` property onto a function the injection parameters can be specified.
3564 * As an array of injection names, where the last item in the array is the function to call.
3569 * @name $injector#get
3572 * Return an instance of the service.
3574 * @param {string} name The name of the instance to retrieve.
3575 * @param {string=} caller An optional string to provide the origin of the function call for error messages.
3576 * @return {*} The instance.
3581 * @name $injector#invoke
3584 * Invoke the method and supply the method arguments from the `$injector`.
3586 * @param {Function|Array.<string|Function>} fn The injectable function to invoke. Function parameters are
3587 * injected according to the {@link guide/di $inject Annotation} rules.
3588 * @param {Object=} self The `this` for the invoked method.
3589 * @param {Object=} locals Optional object. If preset then any argument names are read from this
3590 * object first, before the `$injector` is consulted.
3591 * @returns {*} the value returned by the invoked `fn` function.
3596 * @name $injector#has
3599 * Allows the user to query if the particular service exists.
3601 * @param {string} name Name of the service to query.
3602 * @returns {boolean} `true` if injector has given service.
3607 * @name $injector#instantiate
3609 * Create a new instance of JS type. The method takes a constructor function, invokes the new
3610 * operator, and supplies all of the arguments to the constructor function as specified by the
3611 * constructor annotation.
3613 * @param {Function} Type Annotated constructor function.
3614 * @param {Object=} locals Optional object. If preset then any argument names are read from this
3615 * object first, before the `$injector` is consulted.
3616 * @returns {Object} new instance of `Type`.
3621 * @name $injector#annotate
3624 * Returns an array of service names which the function is requesting for injection. This API is
3625 * used by the injector to determine which services need to be injected into the function when the
3626 * function is invoked. There are three ways in which the function can be annotated with the needed
3631 * The simplest form is to extract the dependencies from the arguments of the function. This is done
3632 * by converting the function into a string using `toString()` method and extracting the argument
3636 * function MyController($scope, $route) {
3641 * expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
3644 * You can disallow this method by using strict injection mode.
3646 * This method does not work with code minification / obfuscation. For this reason the following
3647 * annotation strategies are supported.
3649 * # The `$inject` property
3651 * If a function has an `$inject` property and its value is an array of strings, then the strings
3652 * represent names of services to be injected into the function.
3655 * var MyController = function(obfuscatedScope, obfuscatedRoute) {
3658 * // Define function dependencies
3659 * MyController['$inject'] = ['$scope', '$route'];
3662 * expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
3665 * # The array notation
3667 * It is often desirable to inline Injected functions and that's when setting the `$inject` property
3668 * is very inconvenient. In these situations using the array notation to specify the dependencies in
3669 * a way that survives minification is a better choice:
3672 * // We wish to write this (not minification / obfuscation safe)
3673 * injector.invoke(function($compile, $rootScope) {
3677 * // We are forced to write break inlining
3678 * var tmpFn = function(obfuscatedCompile, obfuscatedRootScope) {
3681 * tmpFn.$inject = ['$compile', '$rootScope'];
3682 * injector.invoke(tmpFn);
3684 * // To better support inline function the inline annotation is supported
3685 * injector.invoke(['$compile', '$rootScope', function(obfCompile, obfRootScope) {
3690 * expect(injector.annotate(
3691 * ['$compile', '$rootScope', function(obfus_$compile, obfus_$rootScope) {}])
3692 * ).toEqual(['$compile', '$rootScope']);
3695 * @param {Function|Array.<string|Function>} fn Function for which dependent service names need to
3696 * be retrieved as described above.
3698 * @param {boolean=} [strictDi=false] Disallow argument name annotation inference.
3700 * @returns {Array.<string>} The names of the services which the function requires.
3712 * The {@link auto.$provide $provide} service has a number of methods for registering components
3713 * with the {@link auto.$injector $injector}. Many of these functions are also exposed on
3714 * {@link angular.Module}.
3716 * An Angular **service** is a singleton object created by a **service factory**. These **service
3717 * factories** are functions which, in turn, are created by a **service provider**.
3718 * The **service providers** are constructor functions. When instantiated they must contain a
3719 * property called `$get`, which holds the **service factory** function.
3721 * When you request a service, the {@link auto.$injector $injector} is responsible for finding the
3722 * correct **service provider**, instantiating it and then calling its `$get` **service factory**
3723 * function to get the instance of the **service**.
3725 * Often services have no configuration options and there is no need to add methods to the service
3726 * provider. The provider will be no more than a constructor function with a `$get` property. For
3727 * these cases the {@link auto.$provide $provide} service has additional helper methods to register
3728 * services without specifying a provider.
3730 * * {@link auto.$provide#provider provider(provider)} - registers a **service provider** with the
3731 * {@link auto.$injector $injector}
3732 * * {@link auto.$provide#constant constant(obj)} - registers a value/object that can be accessed by
3733 * providers and services.
3734 * * {@link auto.$provide#value value(obj)} - registers a value/object that can only be accessed by
3735 * services, not providers.
3736 * * {@link auto.$provide#factory factory(fn)} - registers a service **factory function**, `fn`,
3737 * that will be wrapped in a **service provider** object, whose `$get` property will contain the
3738 * given factory function.
3739 * * {@link auto.$provide#service service(class)} - registers a **constructor function**, `class`
3740 * that will be wrapped in a **service provider** object, whose `$get` property will instantiate
3741 * a new object using the given constructor function.
3743 * See the individual methods for more information and examples.
3748 * @name $provide#provider
3751 * Register a **provider function** with the {@link auto.$injector $injector}. Provider functions
3752 * are constructor functions, whose instances are responsible for "providing" a factory for a
3755 * Service provider names start with the name of the service they provide followed by `Provider`.
3756 * For example, the {@link ng.$log $log} service has a provider called
3757 * {@link ng.$logProvider $logProvider}.
3759 * Service provider objects can have additional methods which allow configuration of the provider
3760 * and its service. Importantly, you can configure what kind of service is created by the `$get`
3761 * method, or how that service will act. For example, the {@link ng.$logProvider $logProvider} has a
3762 * method {@link ng.$logProvider#debugEnabled debugEnabled}
3763 * which lets you specify whether the {@link ng.$log $log} service will log debug messages to the
3766 * @param {string} name The name of the instance. NOTE: the provider will be available under `name +
3768 * @param {(Object|function())} provider If the provider is:
3770 * - `Object`: then it should have a `$get` method. The `$get` method will be invoked using
3771 * {@link auto.$injector#invoke $injector.invoke()} when an instance needs to be created.
3772 * - `Constructor`: a new instance of the provider will be created using
3773 * {@link auto.$injector#instantiate $injector.instantiate()}, then treated as `object`.
3775 * @returns {Object} registered provider instance
3779 * The following example shows how to create a simple event tracking service and register it using
3780 * {@link auto.$provide#provider $provide.provider()}.
3783 * // Define the eventTracker provider
3784 * function EventTrackerProvider() {
3785 * var trackingUrl = '/track';
3787 * // A provider method for configuring where the tracked events should been saved
3788 * this.setTrackingUrl = function(url) {
3789 * trackingUrl = url;
3792 * // The service factory function
3793 * this.$get = ['$http', function($http) {
3794 * var trackedEvents = {};
3796 * // Call this to track an event
3797 * event: function(event) {
3798 * var count = trackedEvents[event] || 0;
3800 * trackedEvents[event] = count;
3803 * // Call this to save the tracked events to the trackingUrl
3804 * save: function() {
3805 * $http.post(trackingUrl, trackedEvents);
3811 * describe('eventTracker', function() {
3814 * beforeEach(module(function($provide) {
3815 * // Register the eventTracker provider
3816 * $provide.provider('eventTracker', EventTrackerProvider);
3819 * beforeEach(module(function(eventTrackerProvider) {
3820 * // Configure eventTracker provider
3821 * eventTrackerProvider.setTrackingUrl('/custom-track');
3824 * it('tracks events', inject(function(eventTracker) {
3825 * expect(eventTracker.event('login')).toEqual(1);
3826 * expect(eventTracker.event('login')).toEqual(2);
3829 * it('saves to the tracking url', inject(function(eventTracker, $http) {
3830 * postSpy = spyOn($http, 'post');
3831 * eventTracker.event('login');
3832 * eventTracker.save();
3833 * expect(postSpy).toHaveBeenCalled();
3834 * expect(postSpy.mostRecentCall.args[0]).not.toEqual('/track');
3835 * expect(postSpy.mostRecentCall.args[0]).toEqual('/custom-track');
3836 * expect(postSpy.mostRecentCall.args[1]).toEqual({ 'login': 1 });
3844 * @name $provide#factory
3847 * Register a **service factory**, which will be called to return the service instance.
3848 * This is short for registering a service where its provider consists of only a `$get` property,
3849 * which is the given service factory function.
3850 * You should use {@link auto.$provide#factory $provide.factory(getFn)} if you do not need to
3851 * configure your service in a provider.
3853 * @param {string} name The name of the instance.
3854 * @param {Function|Array.<string|Function>} $getFn The injectable $getFn for the instance creation.
3855 * Internally this is a short hand for `$provide.provider(name, {$get: $getFn})`.
3856 * @returns {Object} registered provider instance
3859 * Here is an example of registering a service
3861 * $provide.factory('ping', ['$http', function($http) {
3862 * return function ping() {
3863 * return $http.send('/ping');
3867 * You would then inject and use this service like this:
3869 * someModule.controller('Ctrl', ['ping', function(ping) {
3878 * @name $provide#service
3881 * Register a **service constructor**, which will be invoked with `new` to create the service
3883 * This is short for registering a service where its provider's `$get` property is the service
3884 * constructor function that will be used to instantiate the service instance.
3886 * You should use {@link auto.$provide#service $provide.service(class)} if you define your service
3889 * @param {string} name The name of the instance.
3890 * @param {Function|Array.<string|Function>} constructor An injectable class (constructor function)
3891 * that will be instantiated.
3892 * @returns {Object} registered provider instance
3895 * Here is an example of registering a service using
3896 * {@link auto.$provide#service $provide.service(class)}.
3898 * var Ping = function($http) {
3899 * this.$http = $http;
3902 * Ping.$inject = ['$http'];
3904 * Ping.prototype.send = function() {
3905 * return this.$http.get('/ping');
3907 * $provide.service('ping', Ping);
3909 * You would then inject and use this service like this:
3911 * someModule.controller('Ctrl', ['ping', function(ping) {
3920 * @name $provide#value
3923 * Register a **value service** with the {@link auto.$injector $injector}, such as a string, a
3924 * number, an array, an object or a function. This is short for registering a service where its
3925 * provider's `$get` property is a factory function that takes no arguments and returns the **value
3928 * Value services are similar to constant services, except that they cannot be injected into a
3929 * module configuration function (see {@link angular.Module#config}) but they can be overridden by
3931 * {@link auto.$provide#decorator decorator}.
3933 * @param {string} name The name of the instance.
3934 * @param {*} value The value.
3935 * @returns {Object} registered provider instance
3938 * Here are some examples of creating value services.
3940 * $provide.value('ADMIN_USER', 'admin');
3942 * $provide.value('RoleLookup', { admin: 0, writer: 1, reader: 2 });
3944 * $provide.value('halfOf', function(value) {
3953 * @name $provide#constant
3956 * Register a **constant service**, such as a string, a number, an array, an object or a function,
3957 * with the {@link auto.$injector $injector}. Unlike {@link auto.$provide#value value} it can be
3958 * injected into a module configuration function (see {@link angular.Module#config}) and it cannot
3959 * be overridden by an Angular {@link auto.$provide#decorator decorator}.
3961 * @param {string} name The name of the constant.
3962 * @param {*} value The constant value.
3963 * @returns {Object} registered instance
3966 * Here a some examples of creating constants:
3968 * $provide.constant('SHARD_HEIGHT', 306);
3970 * $provide.constant('MY_COLOURS', ['red', 'blue', 'grey']);
3972 * $provide.constant('double', function(value) {
3981 * @name $provide#decorator
3984 * Register a **service decorator** with the {@link auto.$injector $injector}. A service decorator
3985 * intercepts the creation of a service, allowing it to override or modify the behaviour of the
3986 * service. The object returned by the decorator may be the original service, or a new service
3987 * object which replaces or wraps and delegates to the original service.
3989 * @param {string} name The name of the service to decorate.
3990 * @param {Function|Array.<string|Function>} decorator This function will be invoked when the service needs to be
3991 * instantiated and should return the decorated service instance. The function is called using
3992 * the {@link auto.$injector#invoke injector.invoke} method and is therefore fully injectable.
3993 * Local injection arguments:
3995 * * `$delegate` - The original service instance, which can be monkey patched, configured,
3996 * decorated or delegated to.
3999 * Here we decorate the {@link ng.$log $log} service to convert warnings to errors by intercepting
4000 * calls to {@link ng.$log#error $log.warn()}.
4002 * $provide.decorator('$log', ['$delegate', function($delegate) {
4003 * $delegate.warn = $delegate.error;
4010 function createInjector(modulesToLoad, strictDi) {
4011 strictDi = (strictDi === true);
4012 var INSTANTIATING = {},
4013 providerSuffix = 'Provider',
4015 loadedModules = new HashMap([], true),
4018 provider: supportObject(provider),
4019 factory: supportObject(factory),
4020 service: supportObject(service),
4021 value: supportObject(value),
4022 constant: supportObject(constant),
4023 decorator: decorator
4026 providerInjector = (providerCache.$injector =
4027 createInternalInjector(providerCache, function(serviceName, caller) {
4028 if (angular.isString(caller)) {
4031 throw $injectorMinErr('unpr', "Unknown provider: {0}", path.join(' <- '));
4034 instanceInjector = (instanceCache.$injector =
4035 createInternalInjector(instanceCache, function(serviceName, caller) {
4036 var provider = providerInjector.get(serviceName + providerSuffix, caller);
4037 return instanceInjector.invoke(provider.$get, provider, undefined, serviceName);
4041 forEach(loadModules(modulesToLoad), function(fn) { instanceInjector.invoke(fn || noop); });
4043 return instanceInjector;
4045 ////////////////////////////////////
4047 ////////////////////////////////////
4049 function supportObject(delegate) {
4050 return function(key, value) {
4051 if (isObject(key)) {
4052 forEach(key, reverseParams(delegate));
4054 return delegate(key, value);
4059 function provider(name, provider_) {
4060 assertNotHasOwnProperty(name, 'service');
4061 if (isFunction(provider_) || isArray(provider_)) {
4062 provider_ = providerInjector.instantiate(provider_);
4064 if (!provider_.$get) {
4065 throw $injectorMinErr('pget', "Provider '{0}' must define $get factory method.", name);
4067 return providerCache[name + providerSuffix] = provider_;
4070 function enforceReturnValue(name, factory) {
4071 return function enforcedReturnValue() {
4072 var result = instanceInjector.invoke(factory, this);
4073 if (isUndefined(result)) {
4074 throw $injectorMinErr('undef', "Provider '{0}' must return a value from $get factory method.", name);
4080 function factory(name, factoryFn, enforce) {
4081 return provider(name, {
4082 $get: enforce !== false ? enforceReturnValue(name, factoryFn) : factoryFn
4086 function service(name, constructor) {
4087 return factory(name, ['$injector', function($injector) {
4088 return $injector.instantiate(constructor);
4092 function value(name, val) { return factory(name, valueFn(val), false); }
4094 function constant(name, value) {
4095 assertNotHasOwnProperty(name, 'constant');
4096 providerCache[name] = value;
4097 instanceCache[name] = value;
4100 function decorator(serviceName, decorFn) {
4101 var origProvider = providerInjector.get(serviceName + providerSuffix),
4102 orig$get = origProvider.$get;
4104 origProvider.$get = function() {
4105 var origInstance = instanceInjector.invoke(orig$get, origProvider);
4106 return instanceInjector.invoke(decorFn, null, {$delegate: origInstance});
4110 ////////////////////////////////////
4112 ////////////////////////////////////
4113 function loadModules(modulesToLoad) {
4114 var runBlocks = [], moduleFn;
4115 forEach(modulesToLoad, function(module) {
4116 if (loadedModules.get(module)) return;
4117 loadedModules.put(module, true);
4119 function runInvokeQueue(queue) {
4121 for (i = 0, ii = queue.length; i < ii; i++) {
4122 var invokeArgs = queue[i],
4123 provider = providerInjector.get(invokeArgs[0]);
4125 provider[invokeArgs[1]].apply(provider, invokeArgs[2]);
4130 if (isString(module)) {
4131 moduleFn = angularModule(module);
4132 runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks);
4133 runInvokeQueue(moduleFn._invokeQueue);
4134 runInvokeQueue(moduleFn._configBlocks);
4135 } else if (isFunction(module)) {
4136 runBlocks.push(providerInjector.invoke(module));
4137 } else if (isArray(module)) {
4138 runBlocks.push(providerInjector.invoke(module));
4140 assertArgFn(module, 'module');
4143 if (isArray(module)) {
4144 module = module[module.length - 1];
4146 if (e.message && e.stack && e.stack.indexOf(e.message) == -1) {
4147 // Safari & FF's stack traces don't contain error.message content
4148 // unlike those of Chrome and IE
4149 // So if stack doesn't contain message, we create a new string that contains both.
4150 // Since error.stack is read-only in Safari, I'm overriding e and not e.stack here.
4152 e = e.message + '\n' + e.stack;
4154 throw $injectorMinErr('modulerr', "Failed to instantiate module {0} due to:\n{1}",
4155 module, e.stack || e.message || e);
4161 ////////////////////////////////////
4162 // internal Injector
4163 ////////////////////////////////////
4165 function createInternalInjector(cache, factory) {
4167 function getService(serviceName, caller) {
4168 if (cache.hasOwnProperty(serviceName)) {
4169 if (cache[serviceName] === INSTANTIATING) {
4170 throw $injectorMinErr('cdep', 'Circular dependency found: {0}',
4171 serviceName + ' <- ' + path.join(' <- '));
4173 return cache[serviceName];
4176 path.unshift(serviceName);
4177 cache[serviceName] = INSTANTIATING;
4178 return cache[serviceName] = factory(serviceName, caller);
4180 if (cache[serviceName] === INSTANTIATING) {
4181 delete cache[serviceName];
4190 function invoke(fn, self, locals, serviceName) {
4191 if (typeof locals === 'string') {
4192 serviceName = locals;
4197 $inject = createInjector.$$annotate(fn, strictDi, serviceName),
4201 for (i = 0, length = $inject.length; i < length; i++) {
4203 if (typeof key !== 'string') {
4204 throw $injectorMinErr('itkn',
4205 'Incorrect injection token! Expected service name as string, got {0}', key);
4208 locals && locals.hasOwnProperty(key)
4210 : getService(key, serviceName)
4217 // http://jsperf.com/angularjs-invoke-apply-vs-switch
4219 return fn.apply(self, args);
4222 function instantiate(Type, locals, serviceName) {
4223 // Check if Type is annotated and use just the given function at n-1 as parameter
4224 // e.g. someModule.factory('greeter', ['$window', function(renamed$window) {}]);
4225 // Object creation: http://jsperf.com/create-constructor/2
4226 var instance = Object.create((isArray(Type) ? Type[Type.length - 1] : Type).prototype || null);
4227 var returnedValue = invoke(Type, instance, locals, serviceName);
4229 return isObject(returnedValue) || isFunction(returnedValue) ? returnedValue : instance;
4234 instantiate: instantiate,
4236 annotate: createInjector.$$annotate,
4237 has: function(name) {
4238 return providerCache.hasOwnProperty(name + providerSuffix) || cache.hasOwnProperty(name);
4244 createInjector.$$annotate = annotate;
4248 * @name $anchorScrollProvider
4251 * Use `$anchorScrollProvider` to disable automatic scrolling whenever
4252 * {@link ng.$location#hash $location.hash()} changes.
4254 function $AnchorScrollProvider() {
4256 var autoScrollingEnabled = true;
4260 * @name $anchorScrollProvider#disableAutoScrolling
4263 * By default, {@link ng.$anchorScroll $anchorScroll()} will automatically detect changes to
4264 * {@link ng.$location#hash $location.hash()} and scroll to the element matching the new hash.<br />
4265 * Use this method to disable automatic scrolling.
4267 * If automatic scrolling is disabled, one must explicitly call
4268 * {@link ng.$anchorScroll $anchorScroll()} in order to scroll to the element related to the
4271 this.disableAutoScrolling = function() {
4272 autoScrollingEnabled = false;
4277 * @name $anchorScroll
4280 * @requires $location
4281 * @requires $rootScope
4284 * When called, it checks the current value of {@link ng.$location#hash $location.hash()} and
4285 * scrolls to the related element, according to the rules specified in the
4286 * [Html5 spec](http://dev.w3.org/html5/spec/Overview.html#the-indicated-part-of-the-document).
4288 * It also watches the {@link ng.$location#hash $location.hash()} and automatically scrolls to
4289 * match any anchor whenever it changes. This can be disabled by calling
4290 * {@link ng.$anchorScrollProvider#disableAutoScrolling $anchorScrollProvider.disableAutoScrolling()}.
4292 * Additionally, you can use its {@link ng.$anchorScroll#yOffset yOffset} property to specify a
4293 * vertical scroll-offset (either fixed or dynamic).
4295 * @property {(number|function|jqLite)} yOffset
4296 * If set, specifies a vertical scroll-offset. This is often useful when there are fixed
4297 * positioned elements at the top of the page, such as navbars, headers etc.
4299 * `yOffset` can be specified in various ways:
4300 * - **number**: A fixed number of pixels to be used as offset.<br /><br />
4301 * - **function**: A getter function called everytime `$anchorScroll()` is executed. Must return
4302 * a number representing the offset (in pixels).<br /><br />
4303 * - **jqLite**: A jqLite/jQuery element to be used for specifying the offset. The distance from
4304 * the top of the page to the element's bottom will be used as offset.<br />
4305 * **Note**: The element will be taken into account only as long as its `position` is set to
4306 * `fixed`. This option is useful, when dealing with responsive navbars/headers that adjust
4307 * their height and/or positioning according to the viewport's size.
4310 * <div class="alert alert-warning">
4311 * In order for `yOffset` to work properly, scrolling should take place on the document's root and
4312 * not some child element.
4316 <example module="anchorScrollExample">
4317 <file name="index.html">
4318 <div id="scrollArea" ng-controller="ScrollController">
4319 <a ng-click="gotoBottom()">Go to bottom</a>
4320 <a id="bottom"></a> You're at the bottom!
4323 <file name="script.js">
4324 angular.module('anchorScrollExample', [])
4325 .controller('ScrollController', ['$scope', '$location', '$anchorScroll',
4326 function ($scope, $location, $anchorScroll) {
4327 $scope.gotoBottom = function() {
4328 // set the location.hash to the id of
4329 // the element you wish to scroll to.
4330 $location.hash('bottom');
4332 // call $anchorScroll()
4337 <file name="style.css">
4351 * The example below illustrates the use of a vertical scroll-offset (specified as a fixed value).
4352 * See {@link ng.$anchorScroll#yOffset $anchorScroll.yOffset} for more details.
4355 <example module="anchorScrollOffsetExample">
4356 <file name="index.html">
4357 <div class="fixed-header" ng-controller="headerCtrl">
4358 <a href="" ng-click="gotoAnchor(x)" ng-repeat="x in [1,2,3,4,5]">
4362 <div id="anchor{{x}}" class="anchor" ng-repeat="x in [1,2,3,4,5]">
4366 <file name="script.js">
4367 angular.module('anchorScrollOffsetExample', [])
4368 .run(['$anchorScroll', function($anchorScroll) {
4369 $anchorScroll.yOffset = 50; // always scroll by 50 extra pixels
4371 .controller('headerCtrl', ['$anchorScroll', '$location', '$scope',
4372 function ($anchorScroll, $location, $scope) {
4373 $scope.gotoAnchor = function(x) {
4374 var newHash = 'anchor' + x;
4375 if ($location.hash() !== newHash) {
4376 // set the $location.hash to `newHash` and
4377 // $anchorScroll will automatically scroll to it
4378 $location.hash('anchor' + x);
4380 // call $anchorScroll() explicitly,
4381 // since $location.hash hasn't changed
4388 <file name="style.css">
4394 border: 2px dashed DarkOrchid;
4395 padding: 10px 10px 200px 10px;
4399 background-color: rgba(0, 0, 0, 0.2);
4402 top: 0; left: 0; right: 0;
4406 display: inline-block;
4412 this.$get = ['$window', '$location', '$rootScope', function($window, $location, $rootScope) {
4413 var document = $window.document;
4415 // Helper function to get first anchor from a NodeList
4416 // (using `Array#some()` instead of `angular#forEach()` since it's more performant
4417 // and working in all supported browsers.)
4418 function getFirstAnchor(list) {
4420 Array.prototype.some.call(list, function(element) {
4421 if (nodeName_(element) === 'a') {
4429 function getYOffset() {
4431 var offset = scroll.yOffset;
4433 if (isFunction(offset)) {
4435 } else if (isElement(offset)) {
4436 var elem = offset[0];
4437 var style = $window.getComputedStyle(elem);
4438 if (style.position !== 'fixed') {
4441 offset = elem.getBoundingClientRect().bottom;
4443 } else if (!isNumber(offset)) {
4450 function scrollTo(elem) {
4452 elem.scrollIntoView();
4454 var offset = getYOffset();
4457 // `offset` is the number of pixels we should scroll UP in order to align `elem` properly.
4458 // This is true ONLY if the call to `elem.scrollIntoView()` initially aligns `elem` at the
4459 // top of the viewport.
4461 // IF the number of pixels from the top of `elem` to the end of the page's content is less
4462 // than the height of the viewport, then `elem.scrollIntoView()` will align the `elem` some
4463 // way down the page.
4465 // This is often the case for elements near the bottom of the page.
4467 // In such cases we do not need to scroll the whole `offset` up, just the difference between
4468 // the top of the element and the offset, which is enough to align the top of `elem` at the
4469 // desired position.
4470 var elemTop = elem.getBoundingClientRect().top;
4471 $window.scrollBy(0, elemTop - offset);
4474 $window.scrollTo(0, 0);
4479 var hash = $location.hash(), elm;
4481 // empty hash, scroll to the top of the page
4482 if (!hash) scrollTo(null);
4484 // element with given id
4485 else if ((elm = document.getElementById(hash))) scrollTo(elm);
4487 // first anchor with given name :-D
4488 else if ((elm = getFirstAnchor(document.getElementsByName(hash)))) scrollTo(elm);
4490 // no element and hash == 'top', scroll to the top of the page
4491 else if (hash === 'top') scrollTo(null);
4494 // does not scroll when user clicks on anchor link that is currently on
4495 // (no url change, no $location.hash() change), browser native does scroll
4496 if (autoScrollingEnabled) {
4497 $rootScope.$watch(function autoScrollWatch() {return $location.hash();},
4498 function autoScrollWatchAction(newVal, oldVal) {
4499 // skip the initial scroll if $location.hash is empty
4500 if (newVal === oldVal && newVal === '') return;
4502 jqLiteDocumentLoaded(function() {
4503 $rootScope.$evalAsync(scroll);
4512 var $animateMinErr = minErr('$animate');
4516 * @name $animateProvider
4519 * Default implementation of $animate that doesn't perform any animations, instead just
4520 * synchronously performs DOM
4521 * updates and calls done() callbacks.
4523 * In order to enable animations the ngAnimate module has to be loaded.
4525 * To see the functional implementation check out src/ngAnimate/animate.js
4527 var $AnimateProvider = ['$provide', function($provide) {
4530 this.$$selectors = {};
4535 * @name $animateProvider#register
4538 * Registers a new injectable animation factory function. The factory function produces the
4539 * animation object which contains callback functions for each event that is expected to be
4542 * * `eventFn`: `function(Element, doneFunction)` The element to animate, the `doneFunction`
4543 * must be called once the element animation is complete. If a function is returned then the
4544 * animation service will use this function to cancel the animation whenever a cancel event is
4550 * eventFn : function(element, done) {
4551 * //code to run the animation
4552 * //once complete, then run done()
4553 * return function cancellationFunction() {
4554 * //code to cancel the animation
4560 * @param {string} name The name of the animation.
4561 * @param {Function} factory The factory function that will be executed to return the animation
4564 this.register = function(name, factory) {
4565 var key = name + '-animation';
4566 if (name && name.charAt(0) != '.') throw $animateMinErr('notcsel',
4567 "Expecting class selector starting with '.' got '{0}'.", name);
4568 this.$$selectors[name.substr(1)] = key;
4569 $provide.factory(key, factory);
4574 * @name $animateProvider#classNameFilter
4577 * Sets and/or returns the CSS class regular expression that is checked when performing
4578 * an animation. Upon bootstrap the classNameFilter value is not set at all and will
4579 * therefore enable $animate to attempt to perform an animation on any element.
4580 * When setting the classNameFilter value, animations will only be performed on elements
4581 * that successfully match the filter expression. This in turn can boost performance
4582 * for low-powered devices as well as applications containing a lot of structural operations.
4583 * @param {RegExp=} expression The className expression which will be checked against all animations
4584 * @return {RegExp} The current CSS className expression value. If null then there is no expression value
4586 this.classNameFilter = function(expression) {
4587 if (arguments.length === 1) {
4588 this.$$classNameFilter = (expression instanceof RegExp) ? expression : null;
4590 return this.$$classNameFilter;
4593 this.$get = ['$$q', '$$asyncCallback', '$rootScope', function($$q, $$asyncCallback, $rootScope) {
4597 function runAnimationPostDigest(fn) {
4598 var cancelFn, defer = $$q.defer();
4599 defer.promise.$$cancelFn = function ngAnimateMaybeCancel() {
4600 cancelFn && cancelFn();
4603 $rootScope.$$postDigest(function ngAnimatePostDigest() {
4604 cancelFn = fn(function ngAnimateNotifyComplete() {
4609 return defer.promise;
4612 function resolveElementClasses(element, classes) {
4613 var toAdd = [], toRemove = [];
4615 var hasClasses = createMap();
4616 forEach((element.attr('class') || '').split(/\s+/), function(className) {
4617 hasClasses[className] = true;
4620 forEach(classes, function(status, className) {
4621 var hasClass = hasClasses[className];
4623 // If the most recent class manipulation (via $animate) was to remove the class, and the
4624 // element currently has the class, the class is scheduled for removal. Otherwise, if
4625 // the most recent class manipulation (via $animate) was to add the class, and the
4626 // element does not currently have the class, the class is scheduled to be added.
4627 if (status === false && hasClass) {
4628 toRemove.push(className);
4629 } else if (status === true && !hasClass) {
4630 toAdd.push(className);
4634 return (toAdd.length + toRemove.length) > 0 &&
4635 [toAdd.length ? toAdd : null, toRemove.length ? toRemove : null];
4638 function cachedClassManipulation(cache, classes, op) {
4639 for (var i=0, ii = classes.length; i < ii; ++i) {
4640 var className = classes[i];
4641 cache[className] = op;
4645 function asyncPromise() {
4646 // only serve one instance of a promise in order to save CPU cycles
4647 if (!currentDefer) {
4648 currentDefer = $$q.defer();
4649 $$asyncCallback(function() {
4650 currentDefer.resolve();
4651 currentDefer = null;
4654 return currentDefer.promise;
4657 function applyStyles(element, options) {
4658 if (angular.isObject(options)) {
4659 var styles = extend(options.from || {}, options.to || {});
4660 element.css(styles);
4668 * @description The $animate service provides rudimentary DOM manipulation functions to
4669 * insert, remove and move elements within the DOM, as well as adding and removing classes.
4670 * This service is the core service used by the ngAnimate $animator service which provides
4671 * high-level animation hooks for CSS and JavaScript.
4673 * $animate is available in the AngularJS core, however, the ngAnimate module must be included
4674 * to enable full out animation support. Otherwise, $animate will only perform simple DOM
4675 * manipulation operations.
4677 * To learn more about enabling animation support, click here to visit the {@link ngAnimate
4678 * ngAnimate module page} as well as the {@link ngAnimate.$animate ngAnimate $animate service
4682 animate: function(element, from, to) {
4683 applyStyles(element, { from: from, to: to });
4684 return asyncPromise();
4690 * @name $animate#enter
4692 * @description Inserts the element into the DOM either after the `after` element or
4693 * as the first child within the `parent` element. When the function is called a promise
4694 * is returned that will be resolved at a later time.
4695 * @param {DOMElement} element the element which will be inserted into the DOM
4696 * @param {DOMElement} parent the parent element which will append the element as
4697 * a child (if the after element is not present)
4698 * @param {DOMElement} after the sibling element which will append the element
4700 * @param {object=} options an optional collection of styles that will be applied to the element.
4701 * @return {Promise} the animation callback promise
4703 enter: function(element, parent, after, options) {
4704 applyStyles(element, options);
4705 after ? after.after(element)
4706 : parent.prepend(element);
4707 return asyncPromise();
4713 * @name $animate#leave
4715 * @description Removes the element from the DOM. When the function is called a promise
4716 * is returned that will be resolved at a later time.
4717 * @param {DOMElement} element the element which will be removed from the DOM
4718 * @param {object=} options an optional collection of options that will be applied to the element.
4719 * @return {Promise} the animation callback promise
4721 leave: function(element, options) {
4722 applyStyles(element, options);
4724 return asyncPromise();
4730 * @name $animate#move
4732 * @description Moves the position of the provided element within the DOM to be placed
4733 * either after the `after` element or inside of the `parent` element. When the function
4734 * is called a promise is returned that will be resolved at a later time.
4736 * @param {DOMElement} element the element which will be moved around within the
4738 * @param {DOMElement} parent the parent element where the element will be
4739 * inserted into (if the after element is not present)
4740 * @param {DOMElement} after the sibling element where the element will be
4741 * positioned next to
4742 * @param {object=} options an optional collection of options that will be applied to the element.
4743 * @return {Promise} the animation callback promise
4745 move: function(element, parent, after, options) {
4746 // Do not remove element before insert. Removing will cause data associated with the
4747 // element to be dropped. Insert will implicitly do the remove.
4748 return this.enter(element, parent, after, options);
4754 * @name $animate#addClass
4756 * @description Adds the provided className CSS class value to the provided element.
4757 * When the function is called a promise is returned that will be resolved at a later time.
4758 * @param {DOMElement} element the element which will have the className value
4760 * @param {string} className the CSS class which will be added to the element
4761 * @param {object=} options an optional collection of options that will be applied to the element.
4762 * @return {Promise} the animation callback promise
4764 addClass: function(element, className, options) {
4765 return this.setClass(element, className, [], options);
4768 $$addClassImmediately: function(element, className, options) {
4769 element = jqLite(element);
4770 className = !isString(className)
4771 ? (isArray(className) ? className.join(' ') : '')
4773 forEach(element, function(element) {
4774 jqLiteAddClass(element, className);
4776 applyStyles(element, options);
4777 return asyncPromise();
4783 * @name $animate#removeClass
4785 * @description Removes the provided className CSS class value from the provided element.
4786 * When the function is called a promise is returned that will be resolved at a later time.
4787 * @param {DOMElement} element the element which will have the className value
4789 * @param {string} className the CSS class which will be removed from the element
4790 * @param {object=} options an optional collection of options that will be applied to the element.
4791 * @return {Promise} the animation callback promise
4793 removeClass: function(element, className, options) {
4794 return this.setClass(element, [], className, options);
4797 $$removeClassImmediately: function(element, className, options) {
4798 element = jqLite(element);
4799 className = !isString(className)
4800 ? (isArray(className) ? className.join(' ') : '')
4802 forEach(element, function(element) {
4803 jqLiteRemoveClass(element, className);
4805 applyStyles(element, options);
4806 return asyncPromise();
4812 * @name $animate#setClass
4814 * @description Adds and/or removes the given CSS classes to and from the element.
4815 * When the function is called a promise is returned that will be resolved at a later time.
4816 * @param {DOMElement} element the element which will have its CSS classes changed
4818 * @param {string} add the CSS classes which will be added to the element
4819 * @param {string} remove the CSS class which will be removed from the element
4820 * @param {object=} options an optional collection of options that will be applied to the element.
4821 * @return {Promise} the animation callback promise
4823 setClass: function(element, add, remove, options) {
4825 var STORAGE_KEY = '$$animateClasses';
4826 var createdCache = false;
4827 element = jqLite(element);
4829 var cache = element.data(STORAGE_KEY);
4835 createdCache = true;
4836 } else if (options && cache.options) {
4837 cache.options = angular.extend(cache.options || {}, options);
4840 var classes = cache.classes;
4842 add = isArray(add) ? add : add.split(' ');
4843 remove = isArray(remove) ? remove : remove.split(' ');
4844 cachedClassManipulation(classes, add, true);
4845 cachedClassManipulation(classes, remove, false);
4848 cache.promise = runAnimationPostDigest(function(done) {
4849 var cache = element.data(STORAGE_KEY);
4850 element.removeData(STORAGE_KEY);
4852 // in the event that the element is removed before postDigest
4853 // is run then the cache will be undefined and there will be
4854 // no need anymore to add or remove and of the element classes
4856 var classes = resolveElementClasses(element, cache.classes);
4858 self.$$setClassImmediately(element, classes[0], classes[1], cache.options);
4864 element.data(STORAGE_KEY, cache);
4867 return cache.promise;
4870 $$setClassImmediately: function(element, add, remove, options) {
4871 add && this.$$addClassImmediately(element, add);
4872 remove && this.$$removeClassImmediately(element, remove);
4873 applyStyles(element, options);
4874 return asyncPromise();
4883 function $$AsyncCallbackProvider() {
4884 this.$get = ['$$rAF', '$timeout', function($$rAF, $timeout) {
4885 return $$rAF.supported
4886 ? function(fn) { return $$rAF(fn); }
4888 return $timeout(fn, 0, false);
4893 /* global stripHash: true */
4896 * ! This is a private undocumented service !
4901 * This object has two goals:
4903 * - hide all the global state in the browser caused by the window object
4904 * - abstract away all the browser specific features and inconsistencies
4906 * For tests we provide {@link ngMock.$browser mock implementation} of the `$browser`
4907 * service, which can be used for convenient testing of the application without the interaction with
4908 * the real browser apis.
4911 * @param {object} window The global window object.
4912 * @param {object} document jQuery wrapped document.
4913 * @param {object} $log window.console or an object with the same interface.
4914 * @param {object} $sniffer $sniffer service
4916 function Browser(window, document, $log, $sniffer) {
4918 rawDocument = document[0],
4919 location = window.location,
4920 history = window.history,
4921 setTimeout = window.setTimeout,
4922 clearTimeout = window.clearTimeout,
4923 pendingDeferIds = {};
4925 self.isMock = false;
4927 var outstandingRequestCount = 0;
4928 var outstandingRequestCallbacks = [];
4930 // TODO(vojta): remove this temporary api
4931 self.$$completeOutstandingRequest = completeOutstandingRequest;
4932 self.$$incOutstandingRequestCount = function() { outstandingRequestCount++; };
4935 * Executes the `fn` function(supports currying) and decrements the `outstandingRequestCallbacks`
4936 * counter. If the counter reaches 0, all the `outstandingRequestCallbacks` are executed.
4938 function completeOutstandingRequest(fn) {
4940 fn.apply(null, sliceArgs(arguments, 1));
4942 outstandingRequestCount--;
4943 if (outstandingRequestCount === 0) {
4944 while (outstandingRequestCallbacks.length) {
4946 outstandingRequestCallbacks.pop()();
4955 function getHash(url) {
4956 var index = url.indexOf('#');
4957 return index === -1 ? '' : url.substr(index);
4962 * Note: this method is used only by scenario runner
4963 * TODO(vojta): prefix this method with $$ ?
4964 * @param {function()} callback Function that will be called when no outstanding request
4966 self.notifyWhenNoOutstandingRequests = function(callback) {
4967 // force browser to execute all pollFns - this is needed so that cookies and other pollers fire
4968 // at some deterministic time in respect to the test runner's actions. Leaving things up to the
4969 // regular poller would result in flaky tests.
4970 forEach(pollFns, function(pollFn) { pollFn(); });
4972 if (outstandingRequestCount === 0) {
4975 outstandingRequestCallbacks.push(callback);
4979 //////////////////////////////////////////////////////////////
4981 //////////////////////////////////////////////////////////////
4986 * @name $browser#addPollFn
4988 * @param {function()} fn Poll function to add
4991 * Adds a function to the list of functions that poller periodically executes,
4992 * and starts polling if not started yet.
4994 * @returns {function()} the added function
4996 self.addPollFn = function(fn) {
4997 if (isUndefined(pollTimeout)) startPoller(100, setTimeout);
5003 * @param {number} interval How often should browser call poll functions (ms)
5004 * @param {function()} setTimeout Reference to a real or fake `setTimeout` function.
5007 * Configures the poller to run in the specified intervals, using the specified
5008 * setTimeout fn and kicks it off.
5010 function startPoller(interval, setTimeout) {
5012 forEach(pollFns, function(pollFn) { pollFn(); });
5013 pollTimeout = setTimeout(check, interval);
5017 //////////////////////////////////////////////////////////////
5019 //////////////////////////////////////////////////////////////
5021 var cachedState, lastHistoryState,
5022 lastBrowserUrl = location.href,
5023 baseElement = document.find('base'),
5024 reloadLocation = null;
5027 lastHistoryState = cachedState;
5030 * @name $browser#url
5034 * Without any argument, this method just returns current value of location.href.
5037 * With at least one argument, this method sets url to new value.
5038 * If html5 history api supported, pushState/replaceState is used, otherwise
5039 * location.href/location.replace is used.
5040 * Returns its own instance to allow chaining
5042 * NOTE: this api is intended for use only by the $location service. Please use the
5043 * {@link ng.$location $location service} to change url.
5045 * @param {string} url New url (when used as setter)
5046 * @param {boolean=} replace Should new url replace current history record?
5047 * @param {object=} state object to use with pushState/replaceState
5049 self.url = function(url, replace, state) {
5050 // In modern browsers `history.state` is `null` by default; treating it separately
5051 // from `undefined` would cause `$browser.url('/foo')` to change `history.state`
5052 // to undefined via `pushState`. Instead, let's change `undefined` to `null` here.
5053 if (isUndefined(state)) {
5057 // Android Browser BFCache causes location, history reference to become stale.
5058 if (location !== window.location) location = window.location;
5059 if (history !== window.history) history = window.history;
5063 var sameState = lastHistoryState === state;
5065 // Don't change anything if previous and current URLs and states match. This also prevents
5066 // IE<10 from getting into redirect loop when in LocationHashbangInHtml5Url mode.
5067 // See https://github.com/angular/angular.js/commit/ffb2701
5068 if (lastBrowserUrl === url && (!$sniffer.history || sameState)) {
5071 var sameBase = lastBrowserUrl && stripHash(lastBrowserUrl) === stripHash(url);
5072 lastBrowserUrl = url;
5073 lastHistoryState = state;
5074 // Don't use history API if only the hash changed
5075 // due to a bug in IE10/IE11 which leads
5076 // to not firing a `hashchange` nor `popstate` event
5077 // in some cases (see #9143).
5078 if ($sniffer.history && (!sameBase || !sameState)) {
5079 history[replace ? 'replaceState' : 'pushState'](state, '', url);
5081 // Do the assignment again so that those two variables are referentially identical.
5082 lastHistoryState = cachedState;
5084 if (!sameBase || reloadLocation) {
5085 reloadLocation = url;
5088 location.replace(url);
5089 } else if (!sameBase) {
5090 location.href = url;
5092 location.hash = getHash(url);
5098 // - reloadLocation is needed as browsers don't allow to read out
5099 // the new location.href if a reload happened.
5100 // - the replacement is a workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=407172
5101 return reloadLocation || location.href.replace(/%27/g,"'");
5106 * @name $browser#state
5109 * This method is a getter.
5111 * Return history.state or null if history.state is undefined.
5113 * @returns {object} state
5115 self.state = function() {
5119 var urlChangeListeners = [],
5120 urlChangeInit = false;
5122 function cacheStateAndFireUrlChange() {
5127 function getCurrentState() {
5129 return history.state;
5131 // MSIE can reportedly throw when there is no state (UNCONFIRMED).
5135 // This variable should be used *only* inside the cacheState function.
5136 var lastCachedState = null;
5137 function cacheState() {
5138 // This should be the only place in $browser where `history.state` is read.
5139 cachedState = getCurrentState();
5140 cachedState = isUndefined(cachedState) ? null : cachedState;
5142 // Prevent callbacks fo fire twice if both hashchange & popstate were fired.
5143 if (equals(cachedState, lastCachedState)) {
5144 cachedState = lastCachedState;
5146 lastCachedState = cachedState;
5149 function fireUrlChange() {
5150 if (lastBrowserUrl === self.url() && lastHistoryState === cachedState) {
5154 lastBrowserUrl = self.url();
5155 lastHistoryState = cachedState;
5156 forEach(urlChangeListeners, function(listener) {
5157 listener(self.url(), cachedState);
5162 * @name $browser#onUrlChange
5165 * Register callback function that will be called, when url changes.
5167 * It's only called when the url is changed from outside of angular:
5168 * - user types different url into address bar
5169 * - user clicks on history (forward/back) button
5170 * - user clicks on a link
5172 * It's not called when url is changed by $browser.url() method
5174 * The listener gets called with new url as parameter.
5176 * NOTE: this api is intended for use only by the $location service. Please use the
5177 * {@link ng.$location $location service} to monitor url changes in angular apps.
5179 * @param {function(string)} listener Listener function to be called when url changes.
5180 * @return {function(string)} Returns the registered listener fn - handy if the fn is anonymous.
5182 self.onUrlChange = function(callback) {
5183 // TODO(vojta): refactor to use node's syntax for events
5184 if (!urlChangeInit) {
5185 // We listen on both (hashchange/popstate) when available, as some browsers (e.g. Opera)
5186 // don't fire popstate when user change the address bar and don't fire hashchange when url
5187 // changed by push/replaceState
5189 // html5 history api - popstate event
5190 if ($sniffer.history) jqLite(window).on('popstate', cacheStateAndFireUrlChange);
5192 jqLite(window).on('hashchange', cacheStateAndFireUrlChange);
5194 urlChangeInit = true;
5197 urlChangeListeners.push(callback);
5202 * Checks whether the url has changed outside of Angular.
5203 * Needs to be exported to be able to check for changes that have been done in sync,
5204 * as hashchange/popstate events fire in async.
5206 self.$$checkUrlChange = fireUrlChange;
5208 //////////////////////////////////////////////////////////////
5210 //////////////////////////////////////////////////////////////
5213 * @name $browser#baseHref
5216 * Returns current <base href>
5217 * (always relative - without domain)
5219 * @returns {string} The current base href
5221 self.baseHref = function() {
5222 var href = baseElement.attr('href');
5223 return href ? href.replace(/^(https?\:)?\/\/[^\/]*/, '') : '';
5226 //////////////////////////////////////////////////////////////
5228 //////////////////////////////////////////////////////////////
5229 var lastCookies = {};
5230 var lastCookieString = '';
5231 var cookiePath = self.baseHref();
5233 function safeDecodeURIComponent(str) {
5235 return decodeURIComponent(str);
5242 * @name $browser#cookies
5244 * @param {string=} name Cookie name
5245 * @param {string=} value Cookie value
5248 * The cookies method provides a 'private' low level access to browser cookies.
5249 * It is not meant to be used directly, use the $cookie service instead.
5251 * The return values vary depending on the arguments that the method was called with as follows:
5253 * - cookies() -> hash of all cookies, this is NOT a copy of the internal state, so do not modify
5255 * - cookies(name, value) -> set name to value, if value is undefined delete the cookie
5256 * - cookies(name) -> the same as (name, undefined) == DELETES (no one calls it right now that
5259 * @returns {Object} Hash of all cookies (if called without any parameter)
5261 self.cookies = function(name, value) {
5262 var cookieLength, cookieArray, cookie, i, index;
5265 if (value === undefined) {
5266 rawDocument.cookie = encodeURIComponent(name) + "=;path=" + cookiePath +
5267 ";expires=Thu, 01 Jan 1970 00:00:00 GMT";
5269 if (isString(value)) {
5270 cookieLength = (rawDocument.cookie = encodeURIComponent(name) + '=' + encodeURIComponent(value) +
5271 ';path=' + cookiePath).length + 1;
5273 // per http://www.ietf.org/rfc/rfc2109.txt browser must allow at minimum:
5275 // - 20 cookies per unique domain
5276 // - 4096 bytes per cookie
5277 if (cookieLength > 4096) {
5278 $log.warn("Cookie '" + name +
5279 "' possibly not set or overflowed because it was too large (" +
5280 cookieLength + " > 4096 bytes)!");
5285 if (rawDocument.cookie !== lastCookieString) {
5286 lastCookieString = rawDocument.cookie;
5287 cookieArray = lastCookieString.split("; ");
5290 for (i = 0; i < cookieArray.length; i++) {
5291 cookie = cookieArray[i];
5292 index = cookie.indexOf('=');
5293 if (index > 0) { //ignore nameless cookies
5294 name = safeDecodeURIComponent(cookie.substring(0, index));
5295 // the first value that is seen for a cookie is the most
5296 // specific one. values for the same cookie name that
5297 // follow are for less specific paths.
5298 if (lastCookies[name] === undefined) {
5299 lastCookies[name] = safeDecodeURIComponent(cookie.substring(index + 1));
5310 * @name $browser#defer
5311 * @param {function()} fn A function, who's execution should be deferred.
5312 * @param {number=} [delay=0] of milliseconds to defer the function execution.
5313 * @returns {*} DeferId that can be used to cancel the task via `$browser.defer.cancel()`.
5316 * Executes a fn asynchronously via `setTimeout(fn, delay)`.
5318 * Unlike when calling `setTimeout` directly, in test this function is mocked and instead of using
5319 * `setTimeout` in tests, the fns are queued in an array, which can be programmatically flushed
5320 * via `$browser.defer.flush()`.
5323 self.defer = function(fn, delay) {
5325 outstandingRequestCount++;
5326 timeoutId = setTimeout(function() {
5327 delete pendingDeferIds[timeoutId];
5328 completeOutstandingRequest(fn);
5330 pendingDeferIds[timeoutId] = true;
5336 * @name $browser#defer.cancel
5339 * Cancels a deferred task identified with `deferId`.
5341 * @param {*} deferId Token returned by the `$browser.defer` function.
5342 * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully
5345 self.defer.cancel = function(deferId) {
5346 if (pendingDeferIds[deferId]) {
5347 delete pendingDeferIds[deferId];
5348 clearTimeout(deferId);
5349 completeOutstandingRequest(noop);
5357 function $BrowserProvider() {
5358 this.$get = ['$window', '$log', '$sniffer', '$document',
5359 function($window, $log, $sniffer, $document) {
5360 return new Browser($window, $document, $log, $sniffer);
5366 * @name $cacheFactory
5369 * Factory that constructs {@link $cacheFactory.Cache Cache} objects and gives access to
5374 * var cache = $cacheFactory('cacheId');
5375 * expect($cacheFactory.get('cacheId')).toBe(cache);
5376 * expect($cacheFactory.get('noSuchCacheId')).not.toBeDefined();
5378 * cache.put("key", "value");
5379 * cache.put("another key", "another value");
5381 * // We've specified no options on creation
5382 * expect(cache.info()).toEqual({id: 'cacheId', size: 2});
5387 * @param {string} cacheId Name or id of the newly created cache.
5388 * @param {object=} options Options object that specifies the cache behavior. Properties:
5390 * - `{number=}` `capacity` — turns the cache into LRU cache.
5392 * @returns {object} Newly created cache object with the following set of methods:
5394 * - `{object}` `info()` — Returns id, size, and options of cache.
5395 * - `{{*}}` `put({string} key, {*} value)` — Puts a new key-value pair into the cache and returns
5397 * - `{{*}}` `get({string} key)` — Returns cached value for `key` or undefined for cache miss.
5398 * - `{void}` `remove({string} key)` — Removes a key-value pair from the cache.
5399 * - `{void}` `removeAll()` — Removes all cached values.
5400 * - `{void}` `destroy()` — Removes references to this cache from $cacheFactory.
5403 <example module="cacheExampleApp">
5404 <file name="index.html">
5405 <div ng-controller="CacheController">
5406 <input ng-model="newCacheKey" placeholder="Key">
5407 <input ng-model="newCacheValue" placeholder="Value">
5408 <button ng-click="put(newCacheKey, newCacheValue)">Cache</button>
5410 <p ng-if="keys.length">Cached Values</p>
5411 <div ng-repeat="key in keys">
5412 <span ng-bind="key"></span>
5414 <b ng-bind="cache.get(key)"></b>
5418 <div ng-repeat="(key, value) in cache.info()">
5419 <span ng-bind="key"></span>
5421 <b ng-bind="value"></b>
5425 <file name="script.js">
5426 angular.module('cacheExampleApp', []).
5427 controller('CacheController', ['$scope', '$cacheFactory', function($scope, $cacheFactory) {
5429 $scope.cache = $cacheFactory('cacheId');
5430 $scope.put = function(key, value) {
5431 if ($scope.cache.get(key) === undefined) {
5432 $scope.keys.push(key);
5434 $scope.cache.put(key, value === undefined ? null : value);
5438 <file name="style.css">
5445 function $CacheFactoryProvider() {
5447 this.$get = function() {
5450 function cacheFactory(cacheId, options) {
5451 if (cacheId in caches) {
5452 throw minErr('$cacheFactory')('iid', "CacheId '{0}' is already taken!", cacheId);
5456 stats = extend({}, options, {id: cacheId}),
5458 capacity = (options && options.capacity) || Number.MAX_VALUE,
5465 * @name $cacheFactory.Cache
5468 * A cache object used to store and retrieve data, primarily used by
5469 * {@link $http $http} and the {@link ng.directive:script script} directive to cache
5470 * templates and other data.
5473 * angular.module('superCache')
5474 * .factory('superCache', ['$cacheFactory', function($cacheFactory) {
5475 * return $cacheFactory('super-cache');
5482 * it('should behave like a cache', inject(function(superCache) {
5483 * superCache.put('key', 'value');
5484 * superCache.put('another key', 'another value');
5486 * expect(superCache.info()).toEqual({
5487 * id: 'super-cache',
5491 * superCache.remove('another key');
5492 * expect(superCache.get('another key')).toBeUndefined();
5494 * superCache.removeAll();
5495 * expect(superCache.info()).toEqual({
5496 * id: 'super-cache',
5502 return caches[cacheId] = {
5506 * @name $cacheFactory.Cache#put
5510 * Inserts a named entry into the {@link $cacheFactory.Cache Cache} object to be
5511 * retrieved later, and incrementing the size of the cache if the key was not already
5512 * present in the cache. If behaving like an LRU cache, it will also remove stale
5513 * entries from the set.
5515 * It will not insert undefined values into the cache.
5517 * @param {string} key the key under which the cached data is stored.
5518 * @param {*} value the value to store alongside the key. If it is undefined, the key
5519 * will not be stored.
5520 * @returns {*} the value stored.
5522 put: function(key, value) {
5523 if (capacity < Number.MAX_VALUE) {
5524 var lruEntry = lruHash[key] || (lruHash[key] = {key: key});
5529 if (isUndefined(value)) return;
5530 if (!(key in data)) size++;
5533 if (size > capacity) {
5534 this.remove(staleEnd.key);
5542 * @name $cacheFactory.Cache#get
5546 * Retrieves named data stored in the {@link $cacheFactory.Cache Cache} object.
5548 * @param {string} key the key of the data to be retrieved
5549 * @returns {*} the value stored.
5551 get: function(key) {
5552 if (capacity < Number.MAX_VALUE) {
5553 var lruEntry = lruHash[key];
5555 if (!lruEntry) return;
5566 * @name $cacheFactory.Cache#remove
5570 * Removes an entry from the {@link $cacheFactory.Cache Cache} object.
5572 * @param {string} key the key of the entry to be removed
5574 remove: function(key) {
5575 if (capacity < Number.MAX_VALUE) {
5576 var lruEntry = lruHash[key];
5578 if (!lruEntry) return;
5580 if (lruEntry == freshEnd) freshEnd = lruEntry.p;
5581 if (lruEntry == staleEnd) staleEnd = lruEntry.n;
5582 link(lruEntry.n,lruEntry.p);
5584 delete lruHash[key];
5594 * @name $cacheFactory.Cache#removeAll
5598 * Clears the cache object of any entries.
5600 removeAll: function() {
5604 freshEnd = staleEnd = null;
5610 * @name $cacheFactory.Cache#destroy
5614 * Destroys the {@link $cacheFactory.Cache Cache} object entirely,
5615 * removing it from the {@link $cacheFactory $cacheFactory} set.
5617 destroy: function() {
5621 delete caches[cacheId];
5627 * @name $cacheFactory.Cache#info
5631 * Retrieve information regarding a particular {@link $cacheFactory.Cache Cache}.
5633 * @returns {object} an object with the following properties:
5635 * <li>**id**: the id of the cache instance</li>
5636 * <li>**size**: the number of entries kept in the cache instance</li>
5637 * <li>**...**: any additional properties from the options object when creating the
5642 return extend({}, stats, {size: size});
5648 * makes the `entry` the freshEnd of the LRU linked list
5650 function refresh(entry) {
5651 if (entry != freshEnd) {
5654 } else if (staleEnd == entry) {
5658 link(entry.n, entry.p);
5659 link(entry, freshEnd);
5667 * bidirectionally links two entries of the LRU linked list
5669 function link(nextEntry, prevEntry) {
5670 if (nextEntry != prevEntry) {
5671 if (nextEntry) nextEntry.p = prevEntry; //p stands for previous, 'prev' didn't minify
5672 if (prevEntry) prevEntry.n = nextEntry; //n stands for next, 'next' didn't minify
5680 * @name $cacheFactory#info
5683 * Get information about all the caches that have been created
5685 * @returns {Object} - key-value map of `cacheId` to the result of calling `cache#info`
5687 cacheFactory.info = function() {
5689 forEach(caches, function(cache, cacheId) {
5690 info[cacheId] = cache.info();
5698 * @name $cacheFactory#get
5701 * Get access to a cache object by the `cacheId` used when it was created.
5703 * @param {string} cacheId Name or id of a cache to access.
5704 * @returns {object} Cache object identified by the cacheId or undefined if no such cache.
5706 cacheFactory.get = function(cacheId) {
5707 return caches[cacheId];
5711 return cacheFactory;
5717 * @name $templateCache
5720 * The first time a template is used, it is loaded in the template cache for quick retrieval. You
5721 * can load templates directly into the cache in a `script` tag, or by consuming the
5722 * `$templateCache` service directly.
5724 * Adding via the `script` tag:
5727 * <script type="text/ng-template" id="templateId.html">
5728 * <p>This is the content of the template</p>
5732 * **Note:** the `script` tag containing the template does not need to be included in the `head` of
5733 * the document, but it must be a descendent of the {@link ng.$rootElement $rootElement} (IE,
5734 * element with ng-app attribute), otherwise the template will be ignored.
5736 * Adding via the `$templateCache` service:
5739 * var myApp = angular.module('myApp', []);
5740 * myApp.run(function($templateCache) {
5741 * $templateCache.put('templateId.html', 'This is the content of the template');
5745 * To retrieve the template later, simply use it in your HTML:
5747 * <div ng-include=" 'templateId.html' "></div>
5750 * or get it via Javascript:
5752 * $templateCache.get('templateId.html')
5755 * See {@link ng.$cacheFactory $cacheFactory}.
5758 function $TemplateCacheProvider() {
5759 this.$get = ['$cacheFactory', function($cacheFactory) {
5760 return $cacheFactory('templates');
5764 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
5765 * Any commits to this file should be reviewed with security in mind. *
5766 * Changes to this file can potentially create security vulnerabilities. *
5767 * An approval from 2 Core members with history of modifying *
5768 * this file is required. *
5770 * Does the change somehow allow for arbitrary javascript to be executed? *
5771 * Or allows for someone to change the prototype of built-in objects? *
5772 * Or gives undesired access to variables likes document or window? *
5773 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
5775 /* ! VARIABLE/FUNCTION NAMING CONVENTIONS THAT APPLY TO THIS FILE!
5777 * DOM-related variables:
5779 * - "node" - DOM Node
5780 * - "element" - DOM Element or Node
5781 * - "$node" or "$element" - jqLite-wrapped node or element
5784 * Compiler related stuff:
5786 * - "linkFn" - linking fn of a single directive
5787 * - "nodeLinkFn" - function that aggregates all linking fns for a particular node
5788 * - "childLinkFn" - function that aggregates all linking fns for child nodes of a particular node
5789 * - "compositeLinkFn" - function that aggregates all linking fns for a compilation root (nodeList)
5799 * Compiles an HTML string or DOM into a template and produces a template function, which
5800 * can then be used to link {@link ng.$rootScope.Scope `scope`} and the template together.
5802 * The compilation is a process of walking the DOM tree and matching DOM elements to
5803 * {@link ng.$compileProvider#directive directives}.
5805 * <div class="alert alert-warning">
5806 * **Note:** This document is an in-depth reference of all directive options.
5807 * For a gentle introduction to directives with examples of common use cases,
5808 * see the {@link guide/directive directive guide}.
5811 * ## Comprehensive Directive API
5813 * There are many different options for a directive.
5815 * The difference resides in the return value of the factory function.
5816 * You can either return a "Directive Definition Object" (see below) that defines the directive properties,
5817 * or just the `postLink` function (all other properties will have the default values).
5819 * <div class="alert alert-success">
5820 * **Best Practice:** It's recommended to use the "directive definition object" form.
5823 * Here's an example directive declared with a Directive Definition Object:
5826 * var myModule = angular.module(...);
5828 * myModule.directive('directiveName', function factory(injectables) {
5829 * var directiveDefinitionObject = {
5831 * template: '<div></div>', // or // function(tElement, tAttrs) { ... },
5833 * // templateUrl: 'directive.html', // or // function(tElement, tAttrs) { ... },
5834 * transclude: false,
5836 * templateNamespace: 'html',
5838 * controller: function($scope, $element, $attrs, $transclude, otherInjectables) { ... },
5839 * controllerAs: 'stringIdentifier',
5840 * bindToController: false,
5841 * require: 'siblingDirectiveName', // or // ['^parentDirectiveName', '?optionalDirectiveName', '?^optionalParent'],
5842 * compile: function compile(tElement, tAttrs, transclude) {
5844 * pre: function preLink(scope, iElement, iAttrs, controller) { ... },
5845 * post: function postLink(scope, iElement, iAttrs, controller) { ... }
5848 * // return function postLink( ... ) { ... }
5852 * // pre: function preLink(scope, iElement, iAttrs, controller) { ... },
5853 * // post: function postLink(scope, iElement, iAttrs, controller) { ... }
5856 * // link: function postLink( ... ) { ... }
5858 * return directiveDefinitionObject;
5862 * <div class="alert alert-warning">
5863 * **Note:** Any unspecified options will use the default value. You can see the default values below.
5866 * Therefore the above can be simplified as:
5869 * var myModule = angular.module(...);
5871 * myModule.directive('directiveName', function factory(injectables) {
5872 * var directiveDefinitionObject = {
5873 * link: function postLink(scope, iElement, iAttrs) { ... }
5875 * return directiveDefinitionObject;
5877 * // return function postLink(scope, iElement, iAttrs) { ... }
5883 * ### Directive Definition Object
5885 * The directive definition object provides instructions to the {@link ng.$compile
5886 * compiler}. The attributes are:
5888 * #### `multiElement`
5889 * When this property is set to true, the HTML compiler will collect DOM nodes between
5890 * nodes with the attributes `directive-name-start` and `directive-name-end`, and group them
5891 * together as the directive elements. It is recommended that this feature be used on directives
5892 * which are not strictly behavioural (such as {@link ngClick}), and which
5893 * do not manipulate or replace child nodes (such as {@link ngInclude}).
5896 * When there are multiple directives defined on a single DOM element, sometimes it
5897 * is necessary to specify the order in which the directives are applied. The `priority` is used
5898 * to sort the directives before their `compile` functions get called. Priority is defined as a
5899 * number. Directives with greater numerical `priority` are compiled first. Pre-link functions
5900 * are also run in priority order, but post-link functions are run in reverse order. The order
5901 * of directives with the same priority is undefined. The default priority is `0`.
5904 * If set to true then the current `priority` will be the last set of directives
5905 * which will execute (any directives at the current priority will still execute
5906 * as the order of execution on same `priority` is undefined). Note that expressions
5907 * and other directives used in the directive's template will also be excluded from execution.
5910 * **If set to `true`,** then a new scope will be created for this directive. If multiple directives on the
5911 * same element request a new scope, only one new scope is created. The new scope rule does not
5912 * apply for the root of the template since the root of the template always gets a new scope.
5914 * **If set to `{}` (object hash),** then a new "isolate" scope is created. The 'isolate' scope differs from
5915 * normal scope in that it does not prototypically inherit from the parent scope. This is useful
5916 * when creating reusable components, which should not accidentally read or modify data in the
5919 * The 'isolate' scope takes an object hash which defines a set of local scope properties
5920 * derived from the parent scope. These local properties are useful for aliasing values for
5921 * templates. Locals definition is a hash of local scope property to its source:
5923 * * `@` or `@attr` - bind a local scope property to the value of DOM attribute. The result is
5924 * always a string since DOM attributes are strings. If no `attr` name is specified then the
5925 * attribute name is assumed to be the same as the local name.
5926 * Given `<widget my-attr="hello {{name}}">` and widget definition
5927 * of `scope: { localName:'@myAttr' }`, then widget scope property `localName` will reflect
5928 * the interpolated value of `hello {{name}}`. As the `name` attribute changes so will the
5929 * `localName` property on the widget scope. The `name` is read from the parent scope (not
5932 * * `=` or `=attr` - set up bi-directional binding between a local scope property and the
5933 * parent scope property of name defined via the value of the `attr` attribute. If no `attr`
5934 * name is specified then the attribute name is assumed to be the same as the local name.
5935 * Given `<widget my-attr="parentModel">` and widget definition of
5936 * `scope: { localModel:'=myAttr' }`, then widget scope property `localModel` will reflect the
5937 * value of `parentModel` on the parent scope. Any changes to `parentModel` will be reflected
5938 * in `localModel` and any changes in `localModel` will reflect in `parentModel`. If the parent
5939 * scope property doesn't exist, it will throw a NON_ASSIGNABLE_MODEL_EXPRESSION exception. You
5940 * can avoid this behavior using `=?` or `=?attr` in order to flag the property as optional. If
5941 * you want to shallow watch for changes (i.e. $watchCollection instead of $watch) you can use
5942 * `=*` or `=*attr` (`=*?` or `=*?attr` if the property is optional).
5944 * * `&` or `&attr` - provides a way to execute an expression in the context of the parent scope.
5945 * If no `attr` name is specified then the attribute name is assumed to be the same as the
5946 * local name. Given `<widget my-attr="count = count + value">` and widget definition of
5947 * `scope: { localFn:'&myAttr' }`, then isolate scope property `localFn` will point to
5948 * a function wrapper for the `count = count + value` expression. Often it's desirable to
5949 * pass data from the isolated scope via an expression to the parent scope, this can be
5950 * done by passing a map of local variable names and values into the expression wrapper fn.
5951 * For example, if the expression is `increment(amount)` then we can specify the amount value
5952 * by calling the `localFn` as `localFn({amount: 22})`.
5955 * #### `bindToController`
5956 * When an isolate scope is used for a component (see above), and `controllerAs` is used, `bindToController: true` will
5957 * allow a component to have its properties bound to the controller, rather than to scope. When the controller
5958 * is instantiated, the initial values of the isolate scope bindings are already available.
5961 * Controller constructor function. The controller is instantiated before the
5962 * pre-linking phase and it is shared with other directives (see
5963 * `require` attribute). This allows the directives to communicate with each other and augment
5964 * each other's behavior. The controller is injectable (and supports bracket notation) with the following locals:
5966 * * `$scope` - Current scope associated with the element
5967 * * `$element` - Current element
5968 * * `$attrs` - Current attributes object for the element
5969 * * `$transclude` - A transclude linking function pre-bound to the correct transclusion scope:
5970 * `function([scope], cloneLinkingFn, futureParentElement)`.
5971 * * `scope`: optional argument to override the scope.
5972 * * `cloneLinkingFn`: optional argument to create clones of the original transcluded content.
5973 * * `futureParentElement`:
5974 * * defines the parent to which the `cloneLinkingFn` will add the cloned elements.
5975 * * default: `$element.parent()` resp. `$element` for `transclude:'element'` resp. `transclude:true`.
5976 * * only needed for transcludes that are allowed to contain non html elements (e.g. SVG elements)
5977 * and when the `cloneLinkinFn` is passed,
5978 * as those elements need to created and cloned in a special way when they are defined outside their
5979 * usual containers (e.g. like `<svg>`).
5980 * * See also the `directive.templateNamespace` property.
5984 * Require another directive and inject its controller as the fourth argument to the linking function. The
5985 * `require` takes a string name (or array of strings) of the directive(s) to pass in. If an array is used, the
5986 * injected argument will be an array in corresponding order. If no such directive can be
5987 * found, or if the directive does not have a controller, then an error is raised (unless no link function
5988 * is specified, in which case error checking is skipped). The name can be prefixed with:
5990 * * (no prefix) - Locate the required controller on the current element. Throw an error if not found.
5991 * * `?` - Attempt to locate the required controller or pass `null` to the `link` fn if not found.
5992 * * `^` - Locate the required controller by searching the element and its parents. Throw an error if not found.
5993 * * `^^` - Locate the required controller by searching the element's parents. Throw an error if not found.
5994 * * `?^` - Attempt to locate the required controller by searching the element and its parents or pass
5995 * `null` to the `link` fn if not found.
5996 * * `?^^` - Attempt to locate the required controller by searching the element's parents, or pass
5997 * `null` to the `link` fn if not found.
6000 * #### `controllerAs`
6001 * Controller alias at the directive scope. An alias for the controller so it
6002 * can be referenced at the directive template. The directive needs to define a scope for this
6003 * configuration to be used. Useful in the case when directive is used as component.
6007 * String of subset of `EACM` which restricts the directive to a specific directive
6008 * declaration style. If omitted, the defaults (elements and attributes) are used.
6010 * * `E` - Element name (default): `<my-directive></my-directive>`
6011 * * `A` - Attribute (default): `<div my-directive="exp"></div>`
6012 * * `C` - Class: `<div class="my-directive: exp;"></div>`
6013 * * `M` - Comment: `<!-- directive: my-directive exp -->`
6016 * #### `templateNamespace`
6017 * String representing the document type used by the markup in the template.
6018 * AngularJS needs this information as those elements need to be created and cloned
6019 * in a special way when they are defined outside their usual containers like `<svg>` and `<math>`.
6021 * * `html` - All root nodes in the template are HTML. Root nodes may also be
6022 * top-level elements such as `<svg>` or `<math>`.
6023 * * `svg` - The root nodes in the template are SVG elements (excluding `<math>`).
6024 * * `math` - The root nodes in the template are MathML elements (excluding `<svg>`).
6026 * If no `templateNamespace` is specified, then the namespace is considered to be `html`.
6029 * HTML markup that may:
6030 * * Replace the contents of the directive's element (default).
6031 * * Replace the directive's element itself (if `replace` is true - DEPRECATED).
6032 * * Wrap the contents of the directive's element (if `transclude` is true).
6036 * * A string. For example `<div red-on-hover>{{delete_str}}</div>`.
6037 * * A function which takes two arguments `tElement` and `tAttrs` (described in the `compile`
6038 * function api below) and returns a string value.
6041 * #### `templateUrl`
6042 * This is similar to `template` but the template is loaded from the specified URL, asynchronously.
6044 * Because template loading is asynchronous the compiler will suspend compilation of directives on that element
6045 * for later when the template has been resolved. In the meantime it will continue to compile and link
6046 * sibling and parent elements as though this element had not contained any directives.
6048 * The compiler does not suspend the entire compilation to wait for templates to be loaded because this
6049 * would result in the whole app "stalling" until all templates are loaded asynchronously - even in the
6050 * case when only one deeply nested directive has `templateUrl`.
6052 * Template loading is asynchronous even if the template has been preloaded into the {@link $templateCache}
6054 * You can specify `templateUrl` as a string representing the URL or as a function which takes two
6055 * arguments `tElement` and `tAttrs` (described in the `compile` function api below) and returns
6056 * a string value representing the url. In either case, the template URL is passed through {@link
6057 * $sce#getTrustedResourceUrl $sce.getTrustedResourceUrl}.
6060 * #### `replace` ([*DEPRECATED*!], will be removed in next major release - i.e. v2.0)
6061 * specify what the template should replace. Defaults to `false`.
6063 * * `true` - the template will replace the directive's element.
6064 * * `false` - the template will replace the contents of the directive's element.
6066 * The replacement process migrates all of the attributes / classes from the old element to the new
6067 * one. See the {@link guide/directive#template-expanding-directive
6068 * Directives Guide} for an example.
6070 * There are very few scenarios where element replacement is required for the application function,
6071 * the main one being reusable custom components that are used within SVG contexts
6072 * (because SVG doesn't work with custom elements in the DOM tree).
6075 * Extract the contents of the element where the directive appears and make it available to the directive.
6076 * The contents are compiled and provided to the directive as a **transclusion function**. See the
6077 * {@link $compile#transclusion Transclusion} section below.
6079 * There are two kinds of transclusion depending upon whether you want to transclude just the contents of the
6080 * directive's element or the entire element:
6082 * * `true` - transclude the content (i.e. the child nodes) of the directive's element.
6083 * * `'element'` - transclude the whole of the directive's element including any directives on this
6084 * element that defined at a lower priority than this directive. When used, the `template`
6085 * property is ignored.
6091 * function compile(tElement, tAttrs, transclude) { ... }
6094 * The compile function deals with transforming the template DOM. Since most directives do not do
6095 * template transformation, it is not used often. The compile function takes the following arguments:
6097 * * `tElement` - template element - The element where the directive has been declared. It is
6098 * safe to do template transformation on the element and child elements only.
6100 * * `tAttrs` - template attributes - Normalized list of attributes declared on this element shared
6101 * between all directive compile functions.
6103 * * `transclude` - [*DEPRECATED*!] A transclude linking function: `function(scope, cloneLinkingFn)`
6105 * <div class="alert alert-warning">
6106 * **Note:** The template instance and the link instance may be different objects if the template has
6107 * been cloned. For this reason it is **not** safe to do anything other than DOM transformations that
6108 * apply to all cloned DOM nodes within the compile function. Specifically, DOM listener registration
6109 * should be done in a linking function rather than in a compile function.
6112 * <div class="alert alert-warning">
6113 * **Note:** The compile function cannot handle directives that recursively use themselves in their
6114 * own templates or compile functions. Compiling these directives results in an infinite loop and a
6115 * stack overflow errors.
6117 * This can be avoided by manually using $compile in the postLink function to imperatively compile
6118 * a directive's template instead of relying on automatic template compilation via `template` or
6119 * `templateUrl` declaration or manual compilation inside the compile function.
6122 * <div class="alert alert-error">
6123 * **Note:** The `transclude` function that is passed to the compile function is deprecated, as it
6124 * e.g. does not know about the right outer scope. Please use the transclude function that is passed
6125 * to the link function instead.
6128 * A compile function can have a return value which can be either a function or an object.
6130 * * returning a (post-link) function - is equivalent to registering the linking function via the
6131 * `link` property of the config object when the compile function is empty.
6133 * * returning an object with function(s) registered via `pre` and `post` properties - allows you to
6134 * control when a linking function should be called during the linking phase. See info about
6135 * pre-linking and post-linking functions below.
6139 * This property is used only if the `compile` property is not defined.
6142 * function link(scope, iElement, iAttrs, controller, transcludeFn) { ... }
6145 * The link function is responsible for registering DOM listeners as well as updating the DOM. It is
6146 * executed after the template has been cloned. This is where most of the directive logic will be
6149 * * `scope` - {@link ng.$rootScope.Scope Scope} - The scope to be used by the
6150 * directive for registering {@link ng.$rootScope.Scope#$watch watches}.
6152 * * `iElement` - instance element - The element where the directive is to be used. It is safe to
6153 * manipulate the children of the element only in `postLink` function since the children have
6154 * already been linked.
6156 * * `iAttrs` - instance attributes - Normalized list of attributes declared on this element shared
6157 * between all directive linking functions.
6159 * * `controller` - the directive's required controller instance(s) - Instances are shared
6160 * among all directives, which allows the directives to use the controllers as a communication
6161 * channel. The exact value depends on the directive's `require` property:
6162 * * `string`: the controller instance
6163 * * `array`: array of controller instances
6164 * * no controller(s) required: `undefined`
6166 * If a required controller cannot be found, and it is optional, the instance is `null`,
6167 * otherwise the {@link error:$compile:ctreq Missing Required Controller} error is thrown.
6169 * * `transcludeFn` - A transclude linking function pre-bound to the correct transclusion scope.
6170 * This is the same as the `$transclude`
6171 * parameter of directive controllers, see there for details.
6172 * `function([scope], cloneLinkingFn, futureParentElement)`.
6174 * #### Pre-linking function
6176 * Executed before the child elements are linked. Not safe to do DOM transformation since the
6177 * compiler linking function will fail to locate the correct elements for linking.
6179 * #### Post-linking function
6181 * Executed after the child elements are linked.
6183 * Note that child elements that contain `templateUrl` directives will not have been compiled
6184 * and linked since they are waiting for their template to load asynchronously and their own
6185 * compilation and linking has been suspended until that occurs.
6187 * It is safe to do DOM transformation in the post-linking function on elements that are not waiting
6188 * for their async templates to be resolved.
6193 * Transclusion is the process of extracting a collection of DOM elements from one part of the DOM and
6194 * copying them to another part of the DOM, while maintaining their connection to the original AngularJS
6195 * scope from where they were taken.
6197 * Transclusion is used (often with {@link ngTransclude}) to insert the
6198 * original contents of a directive's element into a specified place in the template of the directive.
6199 * The benefit of transclusion, over simply moving the DOM elements manually, is that the transcluded
6200 * content has access to the properties on the scope from which it was taken, even if the directive
6201 * has isolated scope.
6202 * See the {@link guide/directive#creating-a-directive-that-wraps-other-elements Directives Guide}.
6204 * This makes it possible for the widget to have private state for its template, while the transcluded
6205 * content has access to its originating scope.
6207 * <div class="alert alert-warning">
6208 * **Note:** When testing an element transclude directive you must not place the directive at the root of the
6209 * DOM fragment that is being compiled. See {@link guide/unit-testing#testing-transclusion-directives
6210 * Testing Transclusion Directives}.
6213 * #### Transclusion Functions
6215 * When a directive requests transclusion, the compiler extracts its contents and provides a **transclusion
6216 * function** to the directive's `link` function and `controller`. This transclusion function is a special
6217 * **linking function** that will return the compiled contents linked to a new transclusion scope.
6219 * <div class="alert alert-info">
6220 * If you are just using {@link ngTransclude} then you don't need to worry about this function, since
6221 * ngTransclude will deal with it for us.
6224 * If you want to manually control the insertion and removal of the transcluded content in your directive
6225 * then you must use this transclude function. When you call a transclude function it returns a a jqLite/JQuery
6226 * object that contains the compiled DOM, which is linked to the correct transclusion scope.
6228 * When you call a transclusion function you can pass in a **clone attach function**. This function accepts
6229 * two parameters, `function(clone, scope) { ... }`, where the `clone` is a fresh compiled copy of your transcluded
6230 * content and the `scope` is the newly created transclusion scope, to which the clone is bound.
6232 * <div class="alert alert-info">
6233 * **Best Practice**: Always provide a `cloneFn` (clone attach function) when you call a translude function
6234 * since you then get a fresh clone of the original DOM and also have access to the new transclusion scope.
6237 * It is normal practice to attach your transcluded content (`clone`) to the DOM inside your **clone
6238 * attach function**:
6241 * var transcludedContent, transclusionScope;
6243 * $transclude(function(clone, scope) {
6244 * element.append(clone);
6245 * transcludedContent = clone;
6246 * transclusionScope = scope;
6250 * Later, if you want to remove the transcluded content from your DOM then you should also destroy the
6251 * associated transclusion scope:
6254 * transcludedContent.remove();
6255 * transclusionScope.$destroy();
6258 * <div class="alert alert-info">
6259 * **Best Practice**: if you intend to add and remove transcluded content manually in your directive
6260 * (by calling the transclude function to get the DOM and and calling `element.remove()` to remove it),
6261 * then you are also responsible for calling `$destroy` on the transclusion scope.
6264 * The built-in DOM manipulation directives, such as {@link ngIf}, {@link ngSwitch} and {@link ngRepeat}
6265 * automatically destroy their transluded clones as necessary so you do not need to worry about this if
6266 * you are simply using {@link ngTransclude} to inject the transclusion into your directive.
6269 * #### Transclusion Scopes
6271 * When you call a transclude function it returns a DOM fragment that is pre-bound to a **transclusion
6272 * scope**. This scope is special, in that it is a child of the directive's scope (and so gets destroyed
6273 * when the directive's scope gets destroyed) but it inherits the properties of the scope from which it
6276 * For example consider a directive that uses transclusion and isolated scope. The DOM hierarchy might look
6282 * <div transclusion>
6288 * The `$parent` scope hierarchy will look like this:
6296 * but the scopes will inherit prototypically from different scopes to their `$parent`.
6307 * The {@link ng.$compile.directive.Attributes Attributes} object - passed as a parameter in the
6308 * `link()` or `compile()` functions. It has a variety of uses.
6310 * accessing *Normalized attribute names:*
6311 * Directives like 'ngBind' can be expressed in many ways: 'ng:bind', `data-ng-bind`, or 'x-ng-bind'.
6312 * the attributes object allows for normalized access to
6315 * * *Directive inter-communication:* All directives share the same instance of the attributes
6316 * object which allows the directives to use the attributes object as inter directive
6319 * * *Supports interpolation:* Interpolation attributes are assigned to the attribute object
6320 * allowing other directives to read the interpolated value.
6322 * * *Observing interpolated attributes:* Use `$observe` to observe the value changes of attributes
6323 * that contain interpolation (e.g. `src="{{bar}}"`). Not only is this very efficient but it's also
6324 * the only way to easily get the actual value because during the linking phase the interpolation
6325 * hasn't been evaluated yet and so the value is at this time set to `undefined`.
6328 * function linkingFn(scope, elm, attrs, ctrl) {
6329 * // get the attribute value
6330 * console.log(attrs.ngModel);
6332 * // change the attribute
6333 * attrs.$set('ngModel', 'new value');
6335 * // observe changes to interpolated attribute
6336 * attrs.$observe('ngModel', function(value) {
6337 * console.log('ngModel has changed value to ' + value);
6344 * <div class="alert alert-warning">
6345 * **Note**: Typically directives are registered with `module.directive`. The example below is
6346 * to illustrate how `$compile` works.
6349 <example module="compileExample">
6350 <file name="index.html">
6352 angular.module('compileExample', [], function($compileProvider) {
6353 // configure new 'compile' directive by passing a directive
6354 // factory function. The factory function injects the '$compile'
6355 $compileProvider.directive('compile', function($compile) {
6356 // directive factory creates a link function
6357 return function(scope, element, attrs) {
6360 // watch the 'compile' expression for changes
6361 return scope.$eval(attrs.compile);
6364 // when the 'compile' expression changes
6365 // assign it into the current DOM
6366 element.html(value);
6368 // compile the new DOM and link it to the current
6370 // NOTE: we only compile .childNodes so that
6371 // we don't get into infinite loop compiling ourselves
6372 $compile(element.contents())(scope);
6378 .controller('GreeterController', ['$scope', function($scope) {
6379 $scope.name = 'Angular';
6380 $scope.html = 'Hello {{name}}';
6383 <div ng-controller="GreeterController">
6384 <input ng-model="name"> <br>
6385 <textarea ng-model="html"></textarea> <br>
6386 <div compile="html"></div>
6389 <file name="protractor.js" type="protractor">
6390 it('should auto compile', function() {
6391 var textarea = $('textarea');
6392 var output = $('div[compile]');
6393 // The initial state reads 'Hello Angular'.
6394 expect(output.getText()).toBe('Hello Angular');
6396 textarea.sendKeys('{{name}}!');
6397 expect(output.getText()).toBe('Angular!');
6404 * @param {string|DOMElement} element Element or HTML string to compile into a template function.
6405 * @param {function(angular.Scope, cloneAttachFn=)} transclude function available to directives - DEPRECATED.
6407 * <div class="alert alert-error">
6408 * **Note:** Passing a `transclude` function to the $compile function is deprecated, as it
6409 * e.g. will not use the right outer scope. Please pass the transclude function as a
6410 * `parentBoundTranscludeFn` to the link function instead.
6413 * @param {number} maxPriority only apply directives lower than given priority (Only effects the
6414 * root element(s), not their children)
6415 * @returns {function(scope, cloneAttachFn=, options=)} a link function which is used to bind template
6416 * (a DOM element/tree) to a scope. Where:
6418 * * `scope` - A {@link ng.$rootScope.Scope Scope} to bind to.
6419 * * `cloneAttachFn` - If `cloneAttachFn` is provided, then the link function will clone the
6420 * `template` and call the `cloneAttachFn` function allowing the caller to attach the
6421 * cloned elements to the DOM document at the appropriate place. The `cloneAttachFn` is
6422 * called as: <br> `cloneAttachFn(clonedElement, scope)` where:
6424 * * `clonedElement` - is a clone of the original `element` passed into the compiler.
6425 * * `scope` - is the current scope with which the linking function is working with.
6427 * * `options` - An optional object hash with linking options. If `options` is provided, then the following
6428 * keys may be used to control linking behavior:
6430 * * `parentBoundTranscludeFn` - the transclude function made available to
6431 * directives; if given, it will be passed through to the link functions of
6432 * directives found in `element` during compilation.
6433 * * `transcludeControllers` - an object hash with keys that map controller names
6434 * to controller instances; if given, it will make the controllers
6435 * available to directives.
6436 * * `futureParentElement` - defines the parent to which the `cloneAttachFn` will add
6437 * the cloned elements; only needed for transcludes that are allowed to contain non html
6438 * elements (e.g. SVG elements). See also the directive.controller property.
6440 * Calling the linking function returns the element of the template. It is either the original
6441 * element passed in, or the clone of the element if the `cloneAttachFn` is provided.
6443 * After linking the view is not updated until after a call to $digest which typically is done by
6444 * Angular automatically.
6446 * If you need access to the bound view, there are two ways to do it:
6448 * - If you are not asking the linking function to clone the template, create the DOM element(s)
6449 * before you send them to the compiler and keep this reference around.
6451 * var element = $compile('<p>{{total}}</p>')(scope);
6454 * - if on the other hand, you need the element to be cloned, the view reference from the original
6455 * example would not point to the clone, but rather to the original template that was cloned. In
6456 * this case, you can access the clone via the cloneAttachFn:
6458 * var templateElement = angular.element('<p>{{total}}</p>'),
6461 * var clonedElement = $compile(templateElement)(scope, function(clonedElement, scope) {
6462 * //attach the clone to DOM document at the right place
6465 * //now we have reference to the cloned DOM via `clonedElement`
6469 * For information on how the compiler works, see the
6470 * {@link guide/compiler Angular HTML Compiler} section of the Developer Guide.
6473 var $compileMinErr = minErr('$compile');
6477 * @name $compileProvider
6481 $CompileProvider.$inject = ['$provide', '$$sanitizeUriProvider'];
6482 function $CompileProvider($provide, $$sanitizeUriProvider) {
6483 var hasDirectives = {},
6484 Suffix = 'Directive',
6485 COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\w\-]+)\s+(.*)$/,
6486 CLASS_DIRECTIVE_REGEXP = /(([\w\-]+)(?:\:([^;]+))?;?)/,
6487 ALL_OR_NOTHING_ATTRS = makeMap('ngSrc,ngSrcset,src,srcset'),
6488 REQUIRE_PREFIX_REGEXP = /^(?:(\^\^?)?(\?)?(\^\^?)?)?/;
6490 // Ref: http://developers.whatwg.org/webappapis.html#event-handler-idl-attributes
6491 // The assumption is that future DOM event attribute names will begin with
6492 // 'on' and be composed of only English letters.
6493 var EVENT_HANDLER_ATTR_REGEXP = /^(on[a-z]+|formaction)$/;
6495 function parseIsolateBindings(scope, directiveName) {
6496 var LOCAL_REGEXP = /^\s*([@&]|=(\*?))(\??)\s*(\w*)\s*$/;
6500 forEach(scope, function(definition, scopeName) {
6501 var match = definition.match(LOCAL_REGEXP);
6504 throw $compileMinErr('iscp',
6505 "Invalid isolate scope definition for directive '{0}'." +
6506 " Definition: {... {1}: '{2}' ...}",
6507 directiveName, scopeName, definition);
6510 bindings[scopeName] = {
6512 collection: match[2] === '*',
6513 optional: match[3] === '?',
6514 attrName: match[4] || scopeName
6523 * @name $compileProvider#directive
6527 * Register a new directive with the compiler.
6529 * @param {string|Object} name Name of the directive in camel-case (i.e. <code>ngBind</code> which
6530 * will match as <code>ng-bind</code>), or an object map of directives where the keys are the
6531 * names and the values are the factories.
6532 * @param {Function|Array} directiveFactory An injectable directive factory function. See
6533 * {@link guide/directive} for more info.
6534 * @returns {ng.$compileProvider} Self for chaining.
6536 this.directive = function registerDirective(name, directiveFactory) {
6537 assertNotHasOwnProperty(name, 'directive');
6538 if (isString(name)) {
6539 assertArg(directiveFactory, 'directiveFactory');
6540 if (!hasDirectives.hasOwnProperty(name)) {
6541 hasDirectives[name] = [];
6542 $provide.factory(name + Suffix, ['$injector', '$exceptionHandler',
6543 function($injector, $exceptionHandler) {
6544 var directives = [];
6545 forEach(hasDirectives[name], function(directiveFactory, index) {
6547 var directive = $injector.invoke(directiveFactory);
6548 if (isFunction(directive)) {
6549 directive = { compile: valueFn(directive) };
6550 } else if (!directive.compile && directive.link) {
6551 directive.compile = valueFn(directive.link);
6553 directive.priority = directive.priority || 0;
6554 directive.index = index;
6555 directive.name = directive.name || name;
6556 directive.require = directive.require || (directive.controller && directive.name);
6557 directive.restrict = directive.restrict || 'EA';
6558 if (isObject(directive.scope)) {
6559 directive.$$isolateBindings = parseIsolateBindings(directive.scope, directive.name);
6561 directives.push(directive);
6563 $exceptionHandler(e);
6569 hasDirectives[name].push(directiveFactory);
6571 forEach(name, reverseParams(registerDirective));
6579 * @name $compileProvider#aHrefSanitizationWhitelist
6583 * Retrieves or overrides the default regular expression that is used for whitelisting of safe
6584 * urls during a[href] sanitization.
6586 * The sanitization is a security measure aimed at preventing XSS attacks via html links.
6588 * Any url about to be assigned to a[href] via data-binding is first normalized and turned into
6589 * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist`
6590 * regular expression. If a match is found, the original url is written into the dom. Otherwise,
6591 * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
6593 * @param {RegExp=} regexp New regexp to whitelist urls with.
6594 * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
6595 * chaining otherwise.
6597 this.aHrefSanitizationWhitelist = function(regexp) {
6598 if (isDefined(regexp)) {
6599 $$sanitizeUriProvider.aHrefSanitizationWhitelist(regexp);
6602 return $$sanitizeUriProvider.aHrefSanitizationWhitelist();
6609 * @name $compileProvider#imgSrcSanitizationWhitelist
6613 * Retrieves or overrides the default regular expression that is used for whitelisting of safe
6614 * urls during img[src] sanitization.
6616 * The sanitization is a security measure aimed at prevent XSS attacks via html links.
6618 * Any url about to be assigned to img[src] via data-binding is first normalized and turned into
6619 * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist`
6620 * regular expression. If a match is found, the original url is written into the dom. Otherwise,
6621 * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
6623 * @param {RegExp=} regexp New regexp to whitelist urls with.
6624 * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
6625 * chaining otherwise.
6627 this.imgSrcSanitizationWhitelist = function(regexp) {
6628 if (isDefined(regexp)) {
6629 $$sanitizeUriProvider.imgSrcSanitizationWhitelist(regexp);
6632 return $$sanitizeUriProvider.imgSrcSanitizationWhitelist();
6638 * @name $compileProvider#debugInfoEnabled
6640 * @param {boolean=} enabled update the debugInfoEnabled state if provided, otherwise just return the
6641 * current debugInfoEnabled state
6642 * @returns {*} current value if used as getter or itself (chaining) if used as setter
6647 * Call this method to enable/disable various debug runtime information in the compiler such as adding
6648 * binding information and a reference to the current scope on to DOM elements.
6649 * If enabled, the compiler will add the following to DOM elements that have been bound to the scope
6650 * * `ng-binding` CSS class
6651 * * `$binding` data property containing an array of the binding expressions
6653 * You may want to disable this in production for a significant performance boost. See
6654 * {@link guide/production#disabling-debug-data Disabling Debug Data} for more.
6656 * The default value is true.
6658 var debugInfoEnabled = true;
6659 this.debugInfoEnabled = function(enabled) {
6660 if (isDefined(enabled)) {
6661 debugInfoEnabled = enabled;
6664 return debugInfoEnabled;
6668 '$injector', '$interpolate', '$exceptionHandler', '$templateRequest', '$parse',
6669 '$controller', '$rootScope', '$document', '$sce', '$animate', '$$sanitizeUri',
6670 function($injector, $interpolate, $exceptionHandler, $templateRequest, $parse,
6671 $controller, $rootScope, $document, $sce, $animate, $$sanitizeUri) {
6673 var Attributes = function(element, attributesToCopy) {
6674 if (attributesToCopy) {
6675 var keys = Object.keys(attributesToCopy);
6678 for (i = 0, l = keys.length; i < l; i++) {
6680 this[key] = attributesToCopy[key];
6686 this.$$element = element;
6689 Attributes.prototype = {
6692 * @name $compile.directive.Attributes#$normalize
6696 * Converts an attribute name (e.g. dash/colon/underscore-delimited string, optionally prefixed with `x-` or
6697 * `data-`) to its normalized, camelCase form.
6699 * Also there is special case for Moz prefix starting with upper case letter.
6701 * For further information check out the guide on {@link guide/directive#matching-directives Matching Directives}
6703 * @param {string} name Name to normalize
6705 $normalize: directiveNormalize,
6710 * @name $compile.directive.Attributes#$addClass
6714 * Adds the CSS class value specified by the classVal parameter to the element. If animations
6715 * are enabled then an animation will be triggered for the class addition.
6717 * @param {string} classVal The className value that will be added to the element
6719 $addClass: function(classVal) {
6720 if (classVal && classVal.length > 0) {
6721 $animate.addClass(this.$$element, classVal);
6727 * @name $compile.directive.Attributes#$removeClass
6731 * Removes the CSS class value specified by the classVal parameter from the element. If
6732 * animations are enabled then an animation will be triggered for the class removal.
6734 * @param {string} classVal The className value that will be removed from the element
6736 $removeClass: function(classVal) {
6737 if (classVal && classVal.length > 0) {
6738 $animate.removeClass(this.$$element, classVal);
6744 * @name $compile.directive.Attributes#$updateClass
6748 * Adds and removes the appropriate CSS class values to the element based on the difference
6749 * between the new and old CSS class values (specified as newClasses and oldClasses).
6751 * @param {string} newClasses The current CSS className value
6752 * @param {string} oldClasses The former CSS className value
6754 $updateClass: function(newClasses, oldClasses) {
6755 var toAdd = tokenDifference(newClasses, oldClasses);
6756 if (toAdd && toAdd.length) {
6757 $animate.addClass(this.$$element, toAdd);
6760 var toRemove = tokenDifference(oldClasses, newClasses);
6761 if (toRemove && toRemove.length) {
6762 $animate.removeClass(this.$$element, toRemove);
6767 * Set a normalized attribute on the element in a way such that all directives
6768 * can share the attribute. This function properly handles boolean attributes.
6769 * @param {string} key Normalized key. (ie ngAttribute)
6770 * @param {string|boolean} value The value to set. If `null` attribute will be deleted.
6771 * @param {boolean=} writeAttr If false, does not write the value to DOM element attribute.
6773 * @param {string=} attrName Optional none normalized name. Defaults to key.
6775 $set: function(key, value, writeAttr, attrName) {
6776 // TODO: decide whether or not to throw an error if "class"
6777 //is set through this function since it may cause $updateClass to
6780 var node = this.$$element[0],
6781 booleanKey = getBooleanAttrName(node, key),
6782 aliasedKey = getAliasedAttrName(node, key),
6787 this.$$element.prop(key, value);
6788 attrName = booleanKey;
6789 } else if (aliasedKey) {
6790 this[aliasedKey] = value;
6791 observer = aliasedKey;
6796 // translate normalized key to actual key
6798 this.$attr[key] = attrName;
6800 attrName = this.$attr[key];
6802 this.$attr[key] = attrName = snake_case(key, '-');
6806 nodeName = nodeName_(this.$$element);
6808 if ((nodeName === 'a' && key === 'href') ||
6809 (nodeName === 'img' && key === 'src')) {
6810 // sanitize a[href] and img[src] values
6811 this[key] = value = $$sanitizeUri(value, key === 'src');
6812 } else if (nodeName === 'img' && key === 'srcset') {
6813 // sanitize img[srcset] values
6816 // first check if there are spaces because it's not the same pattern
6817 var trimmedSrcset = trim(value);
6818 // ( 999x ,| 999w ,| ,|, )
6819 var srcPattern = /(\s+\d+x\s*,|\s+\d+w\s*,|\s+,|,\s+)/;
6820 var pattern = /\s/.test(trimmedSrcset) ? srcPattern : /(,)/;
6822 // split srcset into tuple of uri and descriptor except for the last item
6823 var rawUris = trimmedSrcset.split(pattern);
6826 var nbrUrisWith2parts = Math.floor(rawUris.length / 2);
6827 for (var i = 0; i < nbrUrisWith2parts; i++) {
6828 var innerIdx = i * 2;
6830 result += $$sanitizeUri(trim(rawUris[innerIdx]), true);
6831 // add the descriptor
6832 result += (" " + trim(rawUris[innerIdx + 1]));
6835 // split the last item into uri and descriptor
6836 var lastTuple = trim(rawUris[i * 2]).split(/\s/);
6838 // sanitize the last uri
6839 result += $$sanitizeUri(trim(lastTuple[0]), true);
6841 // and add the last descriptor if any
6842 if (lastTuple.length === 2) {
6843 result += (" " + trim(lastTuple[1]));
6845 this[key] = value = result;
6848 if (writeAttr !== false) {
6849 if (value === null || value === undefined) {
6850 this.$$element.removeAttr(attrName);
6852 this.$$element.attr(attrName, value);
6857 var $$observers = this.$$observers;
6858 $$observers && forEach($$observers[observer], function(fn) {
6862 $exceptionHandler(e);
6870 * @name $compile.directive.Attributes#$observe
6874 * Observes an interpolated attribute.
6876 * The observer function will be invoked once during the next `$digest` following
6877 * compilation. The observer is then invoked whenever the interpolated value
6880 * @param {string} key Normalized key. (ie ngAttribute) .
6881 * @param {function(interpolatedValue)} fn Function that will be called whenever
6882 the interpolated value of the attribute changes.
6883 * See the {@link guide/directive#text-and-attribute-bindings Directives} guide for more info.
6884 * @returns {function()} Returns a deregistration function for this observer.
6886 $observe: function(key, fn) {
6888 $$observers = (attrs.$$observers || (attrs.$$observers = createMap())),
6889 listeners = ($$observers[key] || ($$observers[key] = []));
6892 $rootScope.$evalAsync(function() {
6893 if (!listeners.$$inter && attrs.hasOwnProperty(key)) {
6894 // no one registered attribute interpolation function, so lets call it manually
6900 arrayRemove(listeners, fn);
6906 function safeAddClass($element, className) {
6908 $element.addClass(className);
6910 // ignore, since it means that we are trying to set class on
6911 // SVG element, where class name is read-only.
6916 var startSymbol = $interpolate.startSymbol(),
6917 endSymbol = $interpolate.endSymbol(),
6918 denormalizeTemplate = (startSymbol == '{{' || endSymbol == '}}')
6920 : function denormalizeTemplate(template) {
6921 return template.replace(/\{\{/g, startSymbol).replace(/}}/g, endSymbol);
6923 NG_ATTR_BINDING = /^ngAttr[A-Z]/;
6925 compile.$$addBindingInfo = debugInfoEnabled ? function $$addBindingInfo($element, binding) {
6926 var bindings = $element.data('$binding') || [];
6928 if (isArray(binding)) {
6929 bindings = bindings.concat(binding);
6931 bindings.push(binding);
6934 $element.data('$binding', bindings);
6937 compile.$$addBindingClass = debugInfoEnabled ? function $$addBindingClass($element) {
6938 safeAddClass($element, 'ng-binding');
6941 compile.$$addScopeInfo = debugInfoEnabled ? function $$addScopeInfo($element, scope, isolated, noTemplate) {
6942 var dataName = isolated ? (noTemplate ? '$isolateScopeNoTemplate' : '$isolateScope') : '$scope';
6943 $element.data(dataName, scope);
6946 compile.$$addScopeClass = debugInfoEnabled ? function $$addScopeClass($element, isolated) {
6947 safeAddClass($element, isolated ? 'ng-isolate-scope' : 'ng-scope');
6952 //================================
6954 function compile($compileNodes, transcludeFn, maxPriority, ignoreDirective,
6955 previousCompileContext) {
6956 if (!($compileNodes instanceof jqLite)) {
6957 // jquery always rewraps, whereas we need to preserve the original selector so that we can
6959 $compileNodes = jqLite($compileNodes);
6961 // We can not compile top level text elements since text nodes can be merged and we will
6962 // not be able to attach scope data to them, so we will wrap them in <span>
6963 forEach($compileNodes, function(node, index) {
6964 if (node.nodeType == NODE_TYPE_TEXT && node.nodeValue.match(/\S+/) /* non-empty */ ) {
6965 $compileNodes[index] = jqLite(node).wrap('<span></span>').parent()[0];
6968 var compositeLinkFn =
6969 compileNodes($compileNodes, transcludeFn, $compileNodes,
6970 maxPriority, ignoreDirective, previousCompileContext);
6971 compile.$$addScopeClass($compileNodes);
6972 var namespace = null;
6973 return function publicLinkFn(scope, cloneConnectFn, options) {
6974 assertArg(scope, 'scope');
6976 options = options || {};
6977 var parentBoundTranscludeFn = options.parentBoundTranscludeFn,
6978 transcludeControllers = options.transcludeControllers,
6979 futureParentElement = options.futureParentElement;
6981 // When `parentBoundTranscludeFn` is passed, it is a
6982 // `controllersBoundTransclude` function (it was previously passed
6983 // as `transclude` to directive.link) so we must unwrap it to get
6984 // its `boundTranscludeFn`
6985 if (parentBoundTranscludeFn && parentBoundTranscludeFn.$$boundTransclude) {
6986 parentBoundTranscludeFn = parentBoundTranscludeFn.$$boundTransclude;
6990 namespace = detectNamespaceForChildElements(futureParentElement);
6993 if (namespace !== 'html') {
6994 // When using a directive with replace:true and templateUrl the $compileNodes
6995 // (or a child element inside of them)
6996 // might change, so we need to recreate the namespace adapted compileNodes
6997 // for call to the link function.
6998 // Note: This will already clone the nodes...
7000 wrapTemplate(namespace, jqLite('<div>').append($compileNodes).html())
7002 } else if (cloneConnectFn) {
7003 // important!!: we must call our jqLite.clone() since the jQuery one is trying to be smart
7004 // and sometimes changes the structure of the DOM.
7005 $linkNode = JQLitePrototype.clone.call($compileNodes);
7007 $linkNode = $compileNodes;
7010 if (transcludeControllers) {
7011 for (var controllerName in transcludeControllers) {
7012 $linkNode.data('$' + controllerName + 'Controller', transcludeControllers[controllerName].instance);
7016 compile.$$addScopeInfo($linkNode, scope);
7018 if (cloneConnectFn) cloneConnectFn($linkNode, scope);
7019 if (compositeLinkFn) compositeLinkFn(scope, $linkNode, $linkNode, parentBoundTranscludeFn);
7024 function detectNamespaceForChildElements(parentElement) {
7025 // TODO: Make this detect MathML as well...
7026 var node = parentElement && parentElement[0];
7030 return nodeName_(node) !== 'foreignobject' && node.toString().match(/SVG/) ? 'svg' : 'html';
7035 * Compile function matches each node in nodeList against the directives. Once all directives
7036 * for a particular node are collected their compile functions are executed. The compile
7037 * functions return values - the linking functions - are combined into a composite linking
7038 * function, which is the a linking function for the node.
7040 * @param {NodeList} nodeList an array of nodes or NodeList to compile
7041 * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the
7042 * scope argument is auto-generated to the new child of the transcluded parent scope.
7043 * @param {DOMElement=} $rootElement If the nodeList is the root of the compilation tree then
7044 * the rootElement must be set the jqLite collection of the compile root. This is
7045 * needed so that the jqLite collection items can be replaced with widgets.
7046 * @param {number=} maxPriority Max directive priority.
7047 * @returns {Function} A composite linking function of all of the matched directives or null.
7049 function compileNodes(nodeList, transcludeFn, $rootElement, maxPriority, ignoreDirective,
7050 previousCompileContext) {
7052 attrs, directives, nodeLinkFn, childNodes, childLinkFn, linkFnFound, nodeLinkFnFound;
7054 for (var i = 0; i < nodeList.length; i++) {
7055 attrs = new Attributes();
7057 // we must always refer to nodeList[i] since the nodes can be replaced underneath us.
7058 directives = collectDirectives(nodeList[i], [], attrs, i === 0 ? maxPriority : undefined,
7061 nodeLinkFn = (directives.length)
7062 ? applyDirectivesToNode(directives, nodeList[i], attrs, transcludeFn, $rootElement,
7063 null, [], [], previousCompileContext)
7066 if (nodeLinkFn && nodeLinkFn.scope) {
7067 compile.$$addScopeClass(attrs.$$element);
7070 childLinkFn = (nodeLinkFn && nodeLinkFn.terminal ||
7071 !(childNodes = nodeList[i].childNodes) ||
7074 : compileNodes(childNodes,
7076 (nodeLinkFn.transcludeOnThisElement || !nodeLinkFn.templateOnThisElement)
7077 && nodeLinkFn.transclude) : transcludeFn);
7079 if (nodeLinkFn || childLinkFn) {
7080 linkFns.push(i, nodeLinkFn, childLinkFn);
7082 nodeLinkFnFound = nodeLinkFnFound || nodeLinkFn;
7085 //use the previous context only for the first element in the virtual group
7086 previousCompileContext = null;
7089 // return a linking function if we have found anything, null otherwise
7090 return linkFnFound ? compositeLinkFn : null;
7092 function compositeLinkFn(scope, nodeList, $rootElement, parentBoundTranscludeFn) {
7093 var nodeLinkFn, childLinkFn, node, childScope, i, ii, idx, childBoundTranscludeFn;
7097 if (nodeLinkFnFound) {
7098 // copy nodeList so that if a nodeLinkFn removes or adds an element at this DOM level our
7099 // offsets don't get screwed up
7100 var nodeListLength = nodeList.length;
7101 stableNodeList = new Array(nodeListLength);
7103 // create a sparse array by only copying the elements which have a linkFn
7104 for (i = 0; i < linkFns.length; i+=3) {
7106 stableNodeList[idx] = nodeList[idx];
7109 stableNodeList = nodeList;
7112 for (i = 0, ii = linkFns.length; i < ii;) {
7113 node = stableNodeList[linkFns[i++]];
7114 nodeLinkFn = linkFns[i++];
7115 childLinkFn = linkFns[i++];
7118 if (nodeLinkFn.scope) {
7119 childScope = scope.$new();
7120 compile.$$addScopeInfo(jqLite(node), childScope);
7125 if (nodeLinkFn.transcludeOnThisElement) {
7126 childBoundTranscludeFn = createBoundTranscludeFn(
7127 scope, nodeLinkFn.transclude, parentBoundTranscludeFn,
7128 nodeLinkFn.elementTranscludeOnThisElement);
7130 } else if (!nodeLinkFn.templateOnThisElement && parentBoundTranscludeFn) {
7131 childBoundTranscludeFn = parentBoundTranscludeFn;
7133 } else if (!parentBoundTranscludeFn && transcludeFn) {
7134 childBoundTranscludeFn = createBoundTranscludeFn(scope, transcludeFn);
7137 childBoundTranscludeFn = null;
7140 nodeLinkFn(childLinkFn, childScope, node, $rootElement, childBoundTranscludeFn);
7142 } else if (childLinkFn) {
7143 childLinkFn(scope, node.childNodes, undefined, parentBoundTranscludeFn);
7149 function createBoundTranscludeFn(scope, transcludeFn, previousBoundTranscludeFn, elementTransclusion) {
7151 var boundTranscludeFn = function(transcludedScope, cloneFn, controllers, futureParentElement, containingScope) {
7153 if (!transcludedScope) {
7154 transcludedScope = scope.$new(false, containingScope);
7155 transcludedScope.$$transcluded = true;
7158 return transcludeFn(transcludedScope, cloneFn, {
7159 parentBoundTranscludeFn: previousBoundTranscludeFn,
7160 transcludeControllers: controllers,
7161 futureParentElement: futureParentElement
7165 return boundTranscludeFn;
7169 * Looks for directives on the given node and adds them to the directive collection which is
7172 * @param node Node to search.
7173 * @param directives An array to which the directives are added to. This array is sorted before
7174 * the function returns.
7175 * @param attrs The shared attrs object which is used to populate the normalized attributes.
7176 * @param {number=} maxPriority Max directive priority.
7178 function collectDirectives(node, directives, attrs, maxPriority, ignoreDirective) {
7179 var nodeType = node.nodeType,
7180 attrsMap = attrs.$attr,
7185 case NODE_TYPE_ELEMENT: /* Element */
7186 // use the node name: <directive>
7187 addDirective(directives,
7188 directiveNormalize(nodeName_(node)), 'E', maxPriority, ignoreDirective);
7190 // iterate over the attributes
7191 for (var attr, name, nName, ngAttrName, value, isNgAttr, nAttrs = node.attributes,
7192 j = 0, jj = nAttrs && nAttrs.length; j < jj; j++) {
7193 var attrStartName = false;
7194 var attrEndName = false;
7198 value = trim(attr.value);
7200 // support ngAttr attribute binding
7201 ngAttrName = directiveNormalize(name);
7202 if (isNgAttr = NG_ATTR_BINDING.test(ngAttrName)) {
7203 name = name.replace(PREFIX_REGEXP, '')
7204 .substr(8).replace(/_(.)/g, function(match, letter) {
7205 return letter.toUpperCase();
7209 var directiveNName = ngAttrName.replace(/(Start|End)$/, '');
7210 if (directiveIsMultiElement(directiveNName)) {
7211 if (ngAttrName === directiveNName + 'Start') {
7212 attrStartName = name;
7213 attrEndName = name.substr(0, name.length - 5) + 'end';
7214 name = name.substr(0, name.length - 6);
7218 nName = directiveNormalize(name.toLowerCase());
7219 attrsMap[nName] = name;
7220 if (isNgAttr || !attrs.hasOwnProperty(nName)) {
7221 attrs[nName] = value;
7222 if (getBooleanAttrName(node, nName)) {
7223 attrs[nName] = true; // presence means true
7226 addAttrInterpolateDirective(node, directives, value, nName, isNgAttr);
7227 addDirective(directives, nName, 'A', maxPriority, ignoreDirective, attrStartName,
7231 // use class as directive
7232 className = node.className;
7233 if (isObject(className)) {
7234 // Maybe SVGAnimatedString
7235 className = className.animVal;
7237 if (isString(className) && className !== '') {
7238 while (match = CLASS_DIRECTIVE_REGEXP.exec(className)) {
7239 nName = directiveNormalize(match[2]);
7240 if (addDirective(directives, nName, 'C', maxPriority, ignoreDirective)) {
7241 attrs[nName] = trim(match[3]);
7243 className = className.substr(match.index + match[0].length);
7247 case NODE_TYPE_TEXT: /* Text Node */
7248 addTextInterpolateDirective(directives, node.nodeValue);
7250 case NODE_TYPE_COMMENT: /* Comment */
7252 match = COMMENT_DIRECTIVE_REGEXP.exec(node.nodeValue);
7254 nName = directiveNormalize(match[1]);
7255 if (addDirective(directives, nName, 'M', maxPriority, ignoreDirective)) {
7256 attrs[nName] = trim(match[2]);
7260 // turns out that under some circumstances IE9 throws errors when one attempts to read
7261 // comment's node value.
7262 // Just ignore it and continue. (Can't seem to reproduce in test case.)
7267 directives.sort(byPriority);
7272 * Given a node with an directive-start it collects all of the siblings until it finds
7279 function groupScan(node, attrStart, attrEnd) {
7282 if (attrStart && node.hasAttribute && node.hasAttribute(attrStart)) {
7285 throw $compileMinErr('uterdir',
7286 "Unterminated attribute, found '{0}' but no matching '{1}' found.",
7287 attrStart, attrEnd);
7289 if (node.nodeType == NODE_TYPE_ELEMENT) {
7290 if (node.hasAttribute(attrStart)) depth++;
7291 if (node.hasAttribute(attrEnd)) depth--;
7294 node = node.nextSibling;
7295 } while (depth > 0);
7300 return jqLite(nodes);
7304 * Wrapper for linking function which converts normal linking function into a grouped
7309 * @returns {Function}
7311 function groupElementsLinkFnWrapper(linkFn, attrStart, attrEnd) {
7312 return function(scope, element, attrs, controllers, transcludeFn) {
7313 element = groupScan(element[0], attrStart, attrEnd);
7314 return linkFn(scope, element, attrs, controllers, transcludeFn);
7319 * Once the directives have been collected, their compile functions are executed. This method
7320 * is responsible for inlining directive templates as well as terminating the application
7321 * of the directives if the terminal directive has been reached.
7323 * @param {Array} directives Array of collected directives to execute their compile function.
7324 * this needs to be pre-sorted by priority order.
7325 * @param {Node} compileNode The raw DOM node to apply the compile functions to
7326 * @param {Object} templateAttrs The shared attribute function
7327 * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the
7328 * scope argument is auto-generated to the new
7329 * child of the transcluded parent scope.
7330 * @param {JQLite} jqCollection If we are working on the root of the compile tree then this
7331 * argument has the root jqLite array so that we can replace nodes
7333 * @param {Object=} originalReplaceDirective An optional directive that will be ignored when
7334 * compiling the transclusion.
7335 * @param {Array.<Function>} preLinkFns
7336 * @param {Array.<Function>} postLinkFns
7337 * @param {Object} previousCompileContext Context used for previous compilation of the current
7339 * @returns {Function} linkFn
7341 function applyDirectivesToNode(directives, compileNode, templateAttrs, transcludeFn,
7342 jqCollection, originalReplaceDirective, preLinkFns, postLinkFns,
7343 previousCompileContext) {
7344 previousCompileContext = previousCompileContext || {};
7346 var terminalPriority = -Number.MAX_VALUE,
7348 controllerDirectives = previousCompileContext.controllerDirectives,
7350 newIsolateScopeDirective = previousCompileContext.newIsolateScopeDirective,
7351 templateDirective = previousCompileContext.templateDirective,
7352 nonTlbTranscludeDirective = previousCompileContext.nonTlbTranscludeDirective,
7353 hasTranscludeDirective = false,
7354 hasTemplate = false,
7355 hasElementTranscludeDirective = previousCompileContext.hasElementTranscludeDirective,
7356 $compileNode = templateAttrs.$$element = jqLite(compileNode),
7360 replaceDirective = originalReplaceDirective,
7361 childTranscludeFn = transcludeFn,
7365 // executes all directives on the current element
7366 for (var i = 0, ii = directives.length; i < ii; i++) {
7367 directive = directives[i];
7368 var attrStart = directive.$$start;
7369 var attrEnd = directive.$$end;
7371 // collect multiblock sections
7373 $compileNode = groupScan(compileNode, attrStart, attrEnd);
7375 $template = undefined;
7377 if (terminalPriority > directive.priority) {
7378 break; // prevent further processing of directives
7381 if (directiveValue = directive.scope) {
7383 // skip the check for directives with async templates, we'll check the derived sync
7384 // directive when the template arrives
7385 if (!directive.templateUrl) {
7386 if (isObject(directiveValue)) {
7387 // This directive is trying to add an isolated scope.
7388 // Check that there is no scope of any kind already
7389 assertNoDuplicate('new/isolated scope', newIsolateScopeDirective || newScopeDirective,
7390 directive, $compileNode);
7391 newIsolateScopeDirective = directive;
7393 // This directive is trying to add a child scope.
7394 // Check that there is no isolated scope already
7395 assertNoDuplicate('new/isolated scope', newIsolateScopeDirective, directive,
7400 newScopeDirective = newScopeDirective || directive;
7403 directiveName = directive.name;
7405 if (!directive.templateUrl && directive.controller) {
7406 directiveValue = directive.controller;
7407 controllerDirectives = controllerDirectives || {};
7408 assertNoDuplicate("'" + directiveName + "' controller",
7409 controllerDirectives[directiveName], directive, $compileNode);
7410 controllerDirectives[directiveName] = directive;
7413 if (directiveValue = directive.transclude) {
7414 hasTranscludeDirective = true;
7416 // Special case ngIf and ngRepeat so that we don't complain about duplicate transclusion.
7417 // This option should only be used by directives that know how to safely handle element transclusion,
7418 // where the transcluded nodes are added or replaced after linking.
7419 if (!directive.$$tlb) {
7420 assertNoDuplicate('transclusion', nonTlbTranscludeDirective, directive, $compileNode);
7421 nonTlbTranscludeDirective = directive;
7424 if (directiveValue == 'element') {
7425 hasElementTranscludeDirective = true;
7426 terminalPriority = directive.priority;
7427 $template = $compileNode;
7428 $compileNode = templateAttrs.$$element =
7429 jqLite(document.createComment(' ' + directiveName + ': ' +
7430 templateAttrs[directiveName] + ' '));
7431 compileNode = $compileNode[0];
7432 replaceWith(jqCollection, sliceArgs($template), compileNode);
7434 childTranscludeFn = compile($template, transcludeFn, terminalPriority,
7435 replaceDirective && replaceDirective.name, {
7437 // - controllerDirectives - otherwise we'll create duplicates controllers
7438 // - newIsolateScopeDirective or templateDirective - combining templates with
7439 // element transclusion doesn't make sense.
7441 // We need only nonTlbTranscludeDirective so that we prevent putting transclusion
7442 // on the same element more than once.
7443 nonTlbTranscludeDirective: nonTlbTranscludeDirective
7446 $template = jqLite(jqLiteClone(compileNode)).contents();
7447 $compileNode.empty(); // clear contents
7448 childTranscludeFn = compile($template, transcludeFn);
7452 if (directive.template) {
7454 assertNoDuplicate('template', templateDirective, directive, $compileNode);
7455 templateDirective = directive;
7457 directiveValue = (isFunction(directive.template))
7458 ? directive.template($compileNode, templateAttrs)
7459 : directive.template;
7461 directiveValue = denormalizeTemplate(directiveValue);
7463 if (directive.replace) {
7464 replaceDirective = directive;
7465 if (jqLiteIsTextNode(directiveValue)) {
7468 $template = removeComments(wrapTemplate(directive.templateNamespace, trim(directiveValue)));
7470 compileNode = $template[0];
7472 if ($template.length != 1 || compileNode.nodeType !== NODE_TYPE_ELEMENT) {
7473 throw $compileMinErr('tplrt',
7474 "Template for directive '{0}' must have exactly one root element. {1}",
7478 replaceWith(jqCollection, $compileNode, compileNode);
7480 var newTemplateAttrs = {$attr: {}};
7482 // combine directives from the original node and from the template:
7483 // - take the array of directives for this element
7484 // - split it into two parts, those that already applied (processed) and those that weren't (unprocessed)
7485 // - collect directives from the template and sort them by priority
7486 // - combine directives as: processed + template + unprocessed
7487 var templateDirectives = collectDirectives(compileNode, [], newTemplateAttrs);
7488 var unprocessedDirectives = directives.splice(i + 1, directives.length - (i + 1));
7490 if (newIsolateScopeDirective) {
7491 markDirectivesAsIsolate(templateDirectives);
7493 directives = directives.concat(templateDirectives).concat(unprocessedDirectives);
7494 mergeTemplateAttributes(templateAttrs, newTemplateAttrs);
7496 ii = directives.length;
7498 $compileNode.html(directiveValue);
7502 if (directive.templateUrl) {
7504 assertNoDuplicate('template', templateDirective, directive, $compileNode);
7505 templateDirective = directive;
7507 if (directive.replace) {
7508 replaceDirective = directive;
7511 nodeLinkFn = compileTemplateUrl(directives.splice(i, directives.length - i), $compileNode,
7512 templateAttrs, jqCollection, hasTranscludeDirective && childTranscludeFn, preLinkFns, postLinkFns, {
7513 controllerDirectives: controllerDirectives,
7514 newIsolateScopeDirective: newIsolateScopeDirective,
7515 templateDirective: templateDirective,
7516 nonTlbTranscludeDirective: nonTlbTranscludeDirective
7518 ii = directives.length;
7519 } else if (directive.compile) {
7521 linkFn = directive.compile($compileNode, templateAttrs, childTranscludeFn);
7522 if (isFunction(linkFn)) {
7523 addLinkFns(null, linkFn, attrStart, attrEnd);
7524 } else if (linkFn) {
7525 addLinkFns(linkFn.pre, linkFn.post, attrStart, attrEnd);
7528 $exceptionHandler(e, startingTag($compileNode));
7532 if (directive.terminal) {
7533 nodeLinkFn.terminal = true;
7534 terminalPriority = Math.max(terminalPriority, directive.priority);
7539 nodeLinkFn.scope = newScopeDirective && newScopeDirective.scope === true;
7540 nodeLinkFn.transcludeOnThisElement = hasTranscludeDirective;
7541 nodeLinkFn.elementTranscludeOnThisElement = hasElementTranscludeDirective;
7542 nodeLinkFn.templateOnThisElement = hasTemplate;
7543 nodeLinkFn.transclude = childTranscludeFn;
7545 previousCompileContext.hasElementTranscludeDirective = hasElementTranscludeDirective;
7547 // might be normal or delayed nodeLinkFn depending on if templateUrl is present
7550 ////////////////////
7552 function addLinkFns(pre, post, attrStart, attrEnd) {
7554 if (attrStart) pre = groupElementsLinkFnWrapper(pre, attrStart, attrEnd);
7555 pre.require = directive.require;
7556 pre.directiveName = directiveName;
7557 if (newIsolateScopeDirective === directive || directive.$$isolateScope) {
7558 pre = cloneAndAnnotateFn(pre, {isolateScope: true});
7560 preLinkFns.push(pre);
7563 if (attrStart) post = groupElementsLinkFnWrapper(post, attrStart, attrEnd);
7564 post.require = directive.require;
7565 post.directiveName = directiveName;
7566 if (newIsolateScopeDirective === directive || directive.$$isolateScope) {
7567 post = cloneAndAnnotateFn(post, {isolateScope: true});
7569 postLinkFns.push(post);
7574 function getControllers(directiveName, require, $element, elementControllers) {
7575 var value, retrievalMethod = 'data', optional = false;
7576 var $searchElement = $element;
7578 if (isString(require)) {
7579 match = require.match(REQUIRE_PREFIX_REGEXP);
7580 require = require.substring(match[0].length);
7583 if (match[1]) match[3] = null;
7584 else match[1] = match[3];
7586 if (match[1] === '^') {
7587 retrievalMethod = 'inheritedData';
7588 } else if (match[1] === '^^') {
7589 retrievalMethod = 'inheritedData';
7590 $searchElement = $element.parent();
7592 if (match[2] === '?') {
7598 if (elementControllers && retrievalMethod === 'data') {
7599 if (value = elementControllers[require]) {
7600 value = value.instance;
7603 value = value || $searchElement[retrievalMethod]('$' + require + 'Controller');
7605 if (!value && !optional) {
7606 throw $compileMinErr('ctreq',
7607 "Controller '{0}', required by directive '{1}', can't be found!",
7608 require, directiveName);
7610 return value || null;
7611 } else if (isArray(require)) {
7613 forEach(require, function(require) {
7614 value.push(getControllers(directiveName, require, $element, elementControllers));
7621 function nodeLinkFn(childLinkFn, scope, linkNode, $rootElement, boundTranscludeFn) {
7622 var i, ii, linkFn, controller, isolateScope, elementControllers, transcludeFn, $element,
7625 if (compileNode === linkNode) {
7626 attrs = templateAttrs;
7627 $element = templateAttrs.$$element;
7629 $element = jqLite(linkNode);
7630 attrs = new Attributes($element, templateAttrs);
7633 if (newIsolateScopeDirective) {
7634 isolateScope = scope.$new(true);
7637 if (boundTranscludeFn) {
7638 // track `boundTranscludeFn` so it can be unwrapped if `transcludeFn`
7639 // is later passed as `parentBoundTranscludeFn` to `publicLinkFn`
7640 transcludeFn = controllersBoundTransclude;
7641 transcludeFn.$$boundTransclude = boundTranscludeFn;
7644 if (controllerDirectives) {
7645 // TODO: merge `controllers` and `elementControllers` into single object.
7647 elementControllers = {};
7648 forEach(controllerDirectives, function(directive) {
7650 $scope: directive === newIsolateScopeDirective || directive.$$isolateScope ? isolateScope : scope,
7653 $transclude: transcludeFn
7654 }, controllerInstance;
7656 controller = directive.controller;
7657 if (controller == '@') {
7658 controller = attrs[directive.name];
7661 controllerInstance = $controller(controller, locals, true, directive.controllerAs);
7663 // For directives with element transclusion the element is a comment,
7664 // but jQuery .data doesn't support attaching data to comment nodes as it's hard to
7665 // clean up (http://bugs.jquery.com/ticket/8335).
7666 // Instead, we save the controllers for the element in a local hash and attach to .data
7667 // later, once we have the actual element.
7668 elementControllers[directive.name] = controllerInstance;
7669 if (!hasElementTranscludeDirective) {
7670 $element.data('$' + directive.name + 'Controller', controllerInstance.instance);
7673 controllers[directive.name] = controllerInstance;
7677 if (newIsolateScopeDirective) {
7678 compile.$$addScopeInfo($element, isolateScope, true, !(templateDirective && (templateDirective === newIsolateScopeDirective ||
7679 templateDirective === newIsolateScopeDirective.$$originalDirective)));
7680 compile.$$addScopeClass($element, true);
7682 var isolateScopeController = controllers && controllers[newIsolateScopeDirective.name];
7683 var isolateBindingContext = isolateScope;
7684 if (isolateScopeController && isolateScopeController.identifier &&
7685 newIsolateScopeDirective.bindToController === true) {
7686 isolateBindingContext = isolateScopeController.instance;
7689 forEach(isolateScope.$$isolateBindings = newIsolateScopeDirective.$$isolateBindings, function(definition, scopeName) {
7690 var attrName = definition.attrName,
7691 optional = definition.optional,
7692 mode = definition.mode, // @, =, or &
7694 parentGet, parentSet, compare;
7699 attrs.$observe(attrName, function(value) {
7700 isolateBindingContext[scopeName] = value;
7702 attrs.$$observers[attrName].$$scope = scope;
7703 if (attrs[attrName]) {
7704 // If the attribute has been provided then we trigger an interpolation to ensure
7705 // the value is there for use in the link fn
7706 isolateBindingContext[scopeName] = $interpolate(attrs[attrName])(scope);
7711 if (optional && !attrs[attrName]) {
7714 parentGet = $parse(attrs[attrName]);
7715 if (parentGet.literal) {
7718 compare = function(a, b) { return a === b || (a !== a && b !== b); };
7720 parentSet = parentGet.assign || function() {
7721 // reset the change, or we will throw this exception on every $digest
7722 lastValue = isolateBindingContext[scopeName] = parentGet(scope);
7723 throw $compileMinErr('nonassign',
7724 "Expression '{0}' used with directive '{1}' is non-assignable!",
7725 attrs[attrName], newIsolateScopeDirective.name);
7727 lastValue = isolateBindingContext[scopeName] = parentGet(scope);
7728 var parentValueWatch = function parentValueWatch(parentValue) {
7729 if (!compare(parentValue, isolateBindingContext[scopeName])) {
7730 // we are out of sync and need to copy
7731 if (!compare(parentValue, lastValue)) {
7732 // parent changed and it has precedence
7733 isolateBindingContext[scopeName] = parentValue;
7735 // if the parent can be assigned then do so
7736 parentSet(scope, parentValue = isolateBindingContext[scopeName]);
7739 return lastValue = parentValue;
7741 parentValueWatch.$stateful = true;
7743 if (definition.collection) {
7744 unwatch = scope.$watchCollection(attrs[attrName], parentValueWatch);
7746 unwatch = scope.$watch($parse(attrs[attrName], parentValueWatch), null, parentGet.literal);
7748 isolateScope.$on('$destroy', unwatch);
7752 parentGet = $parse(attrs[attrName]);
7753 isolateBindingContext[scopeName] = function(locals) {
7754 return parentGet(scope, locals);
7761 forEach(controllers, function(controller) {
7768 for (i = 0, ii = preLinkFns.length; i < ii; i++) {
7769 linkFn = preLinkFns[i];
7770 invokeLinkFn(linkFn,
7771 linkFn.isolateScope ? isolateScope : scope,
7774 linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers),
7780 // We only pass the isolate scope, if the isolate directive has a template,
7781 // otherwise the child elements do not belong to the isolate directive.
7782 var scopeToChild = scope;
7783 if (newIsolateScopeDirective && (newIsolateScopeDirective.template || newIsolateScopeDirective.templateUrl === null)) {
7784 scopeToChild = isolateScope;
7786 childLinkFn && childLinkFn(scopeToChild, linkNode.childNodes, undefined, boundTranscludeFn);
7789 for (i = postLinkFns.length - 1; i >= 0; i--) {
7790 linkFn = postLinkFns[i];
7791 invokeLinkFn(linkFn,
7792 linkFn.isolateScope ? isolateScope : scope,
7795 linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers),
7800 // This is the function that is injected as `$transclude`.
7801 // Note: all arguments are optional!
7802 function controllersBoundTransclude(scope, cloneAttachFn, futureParentElement) {
7803 var transcludeControllers;
7805 // No scope passed in:
7806 if (!isScope(scope)) {
7807 futureParentElement = cloneAttachFn;
7808 cloneAttachFn = scope;
7812 if (hasElementTranscludeDirective) {
7813 transcludeControllers = elementControllers;
7815 if (!futureParentElement) {
7816 futureParentElement = hasElementTranscludeDirective ? $element.parent() : $element;
7818 return boundTranscludeFn(scope, cloneAttachFn, transcludeControllers, futureParentElement, scopeToChild);
7823 function markDirectivesAsIsolate(directives) {
7824 // mark all directives as needing isolate scope.
7825 for (var j = 0, jj = directives.length; j < jj; j++) {
7826 directives[j] = inherit(directives[j], {$$isolateScope: true});
7831 * looks up the directive and decorates it with exception handling and proper parameters. We
7832 * call this the boundDirective.
7834 * @param {string} name name of the directive to look up.
7835 * @param {string} location The directive must be found in specific format.
7836 * String containing any of theses characters:
7838 * * `E`: element name
7842 * @returns {boolean} true if directive was added.
7844 function addDirective(tDirectives, name, location, maxPriority, ignoreDirective, startAttrName,
7846 if (name === ignoreDirective) return null;
7848 if (hasDirectives.hasOwnProperty(name)) {
7849 for (var directive, directives = $injector.get(name + Suffix),
7850 i = 0, ii = directives.length; i < ii; i++) {
7852 directive = directives[i];
7853 if ((maxPriority === undefined || maxPriority > directive.priority) &&
7854 directive.restrict.indexOf(location) != -1) {
7855 if (startAttrName) {
7856 directive = inherit(directive, {$$start: startAttrName, $$end: endAttrName});
7858 tDirectives.push(directive);
7861 } catch (e) { $exceptionHandler(e); }
7869 * looks up the directive and returns true if it is a multi-element directive,
7870 * and therefore requires DOM nodes between -start and -end markers to be grouped
7873 * @param {string} name name of the directive to look up.
7874 * @returns true if directive was registered as multi-element.
7876 function directiveIsMultiElement(name) {
7877 if (hasDirectives.hasOwnProperty(name)) {
7878 for (var directive, directives = $injector.get(name + Suffix),
7879 i = 0, ii = directives.length; i < ii; i++) {
7880 directive = directives[i];
7881 if (directive.multiElement) {
7890 * When the element is replaced with HTML template then the new attributes
7891 * on the template need to be merged with the existing attributes in the DOM.
7892 * The desired effect is to have both of the attributes present.
7894 * @param {object} dst destination attributes (original DOM)
7895 * @param {object} src source attributes (from the directive template)
7897 function mergeTemplateAttributes(dst, src) {
7898 var srcAttr = src.$attr,
7899 dstAttr = dst.$attr,
7900 $element = dst.$$element;
7902 // reapply the old attributes to the new element
7903 forEach(dst, function(value, key) {
7904 if (key.charAt(0) != '$') {
7905 if (src[key] && src[key] !== value) {
7906 value += (key === 'style' ? ';' : ' ') + src[key];
7908 dst.$set(key, value, true, srcAttr[key]);
7912 // copy the new attributes on the old attrs object
7913 forEach(src, function(value, key) {
7914 if (key == 'class') {
7915 safeAddClass($element, value);
7916 dst['class'] = (dst['class'] ? dst['class'] + ' ' : '') + value;
7917 } else if (key == 'style') {
7918 $element.attr('style', $element.attr('style') + ';' + value);
7919 dst['style'] = (dst['style'] ? dst['style'] + ';' : '') + value;
7920 // `dst` will never contain hasOwnProperty as DOM parser won't let it.
7921 // You will get an "InvalidCharacterError: DOM Exception 5" error if you
7922 // have an attribute like "has-own-property" or "data-has-own-property", etc.
7923 } else if (key.charAt(0) != '$' && !dst.hasOwnProperty(key)) {
7925 dstAttr[key] = srcAttr[key];
7931 function compileTemplateUrl(directives, $compileNode, tAttrs,
7932 $rootElement, childTranscludeFn, preLinkFns, postLinkFns, previousCompileContext) {
7934 afterTemplateNodeLinkFn,
7935 afterTemplateChildLinkFn,
7936 beforeTemplateCompileNode = $compileNode[0],
7937 origAsyncDirective = directives.shift(),
7938 derivedSyncDirective = inherit(origAsyncDirective, {
7939 templateUrl: null, transclude: null, replace: null, $$originalDirective: origAsyncDirective
7941 templateUrl = (isFunction(origAsyncDirective.templateUrl))
7942 ? origAsyncDirective.templateUrl($compileNode, tAttrs)
7943 : origAsyncDirective.templateUrl,
7944 templateNamespace = origAsyncDirective.templateNamespace;
7946 $compileNode.empty();
7948 $templateRequest(templateUrl)
7949 .then(function(content) {
7950 var compileNode, tempTemplateAttrs, $template, childBoundTranscludeFn;
7952 content = denormalizeTemplate(content);
7954 if (origAsyncDirective.replace) {
7955 if (jqLiteIsTextNode(content)) {
7958 $template = removeComments(wrapTemplate(templateNamespace, trim(content)));
7960 compileNode = $template[0];
7962 if ($template.length != 1 || compileNode.nodeType !== NODE_TYPE_ELEMENT) {
7963 throw $compileMinErr('tplrt',
7964 "Template for directive '{0}' must have exactly one root element. {1}",
7965 origAsyncDirective.name, templateUrl);
7968 tempTemplateAttrs = {$attr: {}};
7969 replaceWith($rootElement, $compileNode, compileNode);
7970 var templateDirectives = collectDirectives(compileNode, [], tempTemplateAttrs);
7972 if (isObject(origAsyncDirective.scope)) {
7973 markDirectivesAsIsolate(templateDirectives);
7975 directives = templateDirectives.concat(directives);
7976 mergeTemplateAttributes(tAttrs, tempTemplateAttrs);
7978 compileNode = beforeTemplateCompileNode;
7979 $compileNode.html(content);
7982 directives.unshift(derivedSyncDirective);
7984 afterTemplateNodeLinkFn = applyDirectivesToNode(directives, compileNode, tAttrs,
7985 childTranscludeFn, $compileNode, origAsyncDirective, preLinkFns, postLinkFns,
7986 previousCompileContext);
7987 forEach($rootElement, function(node, i) {
7988 if (node == compileNode) {
7989 $rootElement[i] = $compileNode[0];
7992 afterTemplateChildLinkFn = compileNodes($compileNode[0].childNodes, childTranscludeFn);
7994 while (linkQueue.length) {
7995 var scope = linkQueue.shift(),
7996 beforeTemplateLinkNode = linkQueue.shift(),
7997 linkRootElement = linkQueue.shift(),
7998 boundTranscludeFn = linkQueue.shift(),
7999 linkNode = $compileNode[0];
8001 if (scope.$$destroyed) continue;
8003 if (beforeTemplateLinkNode !== beforeTemplateCompileNode) {
8004 var oldClasses = beforeTemplateLinkNode.className;
8006 if (!(previousCompileContext.hasElementTranscludeDirective &&
8007 origAsyncDirective.replace)) {
8008 // it was cloned therefore we have to clone as well.
8009 linkNode = jqLiteClone(compileNode);
8011 replaceWith(linkRootElement, jqLite(beforeTemplateLinkNode), linkNode);
8013 // Copy in CSS classes from original node
8014 safeAddClass(jqLite(linkNode), oldClasses);
8016 if (afterTemplateNodeLinkFn.transcludeOnThisElement) {
8017 childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn);
8019 childBoundTranscludeFn = boundTranscludeFn;
8021 afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, linkNode, $rootElement,
8022 childBoundTranscludeFn);
8027 return function delayedNodeLinkFn(ignoreChildLinkFn, scope, node, rootElement, boundTranscludeFn) {
8028 var childBoundTranscludeFn = boundTranscludeFn;
8029 if (scope.$$destroyed) return;
8031 linkQueue.push(scope,
8034 childBoundTranscludeFn);
8036 if (afterTemplateNodeLinkFn.transcludeOnThisElement) {
8037 childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn);
8039 afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, node, rootElement, childBoundTranscludeFn);
8046 * Sorting function for bound directives.
8048 function byPriority(a, b) {
8049 var diff = b.priority - a.priority;
8050 if (diff !== 0) return diff;
8051 if (a.name !== b.name) return (a.name < b.name) ? -1 : 1;
8052 return a.index - b.index;
8056 function assertNoDuplicate(what, previousDirective, directive, element) {
8057 if (previousDirective) {
8058 throw $compileMinErr('multidir', 'Multiple directives [{0}, {1}] asking for {2} on: {3}',
8059 previousDirective.name, directive.name, what, startingTag(element));
8064 function addTextInterpolateDirective(directives, text) {
8065 var interpolateFn = $interpolate(text, true);
8066 if (interpolateFn) {
8069 compile: function textInterpolateCompileFn(templateNode) {
8070 var templateNodeParent = templateNode.parent(),
8071 hasCompileParent = !!templateNodeParent.length;
8073 // When transcluding a template that has bindings in the root
8074 // we don't have a parent and thus need to add the class during linking fn.
8075 if (hasCompileParent) compile.$$addBindingClass(templateNodeParent);
8077 return function textInterpolateLinkFn(scope, node) {
8078 var parent = node.parent();
8079 if (!hasCompileParent) compile.$$addBindingClass(parent);
8080 compile.$$addBindingInfo(parent, interpolateFn.expressions);
8081 scope.$watch(interpolateFn, function interpolateFnWatchAction(value) {
8082 node[0].nodeValue = value;
8091 function wrapTemplate(type, template) {
8092 type = lowercase(type || 'html');
8096 var wrapper = document.createElement('div');
8097 wrapper.innerHTML = '<' + type + '>' + template + '</' + type + '>';
8098 return wrapper.childNodes[0].childNodes;
8105 function getTrustedContext(node, attrNormalizedName) {
8106 if (attrNormalizedName == "srcdoc") {
8109 var tag = nodeName_(node);
8110 // maction[xlink:href] can source SVG. It's not limited to <maction>.
8111 if (attrNormalizedName == "xlinkHref" ||
8112 (tag == "form" && attrNormalizedName == "action") ||
8113 (tag != "img" && (attrNormalizedName == "src" ||
8114 attrNormalizedName == "ngSrc"))) {
8115 return $sce.RESOURCE_URL;
8120 function addAttrInterpolateDirective(node, directives, value, name, allOrNothing) {
8121 var trustedContext = getTrustedContext(node, name);
8122 allOrNothing = ALL_OR_NOTHING_ATTRS[name] || allOrNothing;
8124 var interpolateFn = $interpolate(value, true, trustedContext, allOrNothing);
8126 // no interpolation found -> ignore
8127 if (!interpolateFn) return;
8130 if (name === "multiple" && nodeName_(node) === "select") {
8131 throw $compileMinErr("selmulti",
8132 "Binding to the 'multiple' attribute is not supported. Element: {0}",
8138 compile: function() {
8140 pre: function attrInterpolatePreLinkFn(scope, element, attr) {
8141 var $$observers = (attr.$$observers || (attr.$$observers = {}));
8143 if (EVENT_HANDLER_ATTR_REGEXP.test(name)) {
8144 throw $compileMinErr('nodomevents',
8145 "Interpolations for HTML DOM event attributes are disallowed. Please use the " +
8146 "ng- versions (such as ng-click instead of onclick) instead.");
8149 // If the attribute has changed since last $interpolate()ed
8150 var newValue = attr[name];
8151 if (newValue !== value) {
8152 // we need to interpolate again since the attribute value has been updated
8153 // (e.g. by another directive's compile function)
8154 // ensure unset/empty values make interpolateFn falsy
8155 interpolateFn = newValue && $interpolate(newValue, true, trustedContext, allOrNothing);
8159 // if attribute was updated so that there is no interpolation going on we don't want to
8160 // register any observers
8161 if (!interpolateFn) return;
8163 // initialize attr object so that it's ready in case we need the value for isolate
8164 // scope initialization, otherwise the value would not be available from isolate
8165 // directive's linking fn during linking phase
8166 attr[name] = interpolateFn(scope);
8168 ($$observers[name] || ($$observers[name] = [])).$$inter = true;
8169 (attr.$$observers && attr.$$observers[name].$$scope || scope).
8170 $watch(interpolateFn, function interpolateFnWatchAction(newValue, oldValue) {
8171 //special case for class attribute addition + removal
8172 //so that class changes can tap into the animation
8173 //hooks provided by the $animate service. Be sure to
8174 //skip animations when the first digest occurs (when
8175 //both the new and the old values are the same) since
8176 //the CSS classes are the non-interpolated values
8177 if (name === 'class' && newValue != oldValue) {
8178 attr.$updateClass(newValue, oldValue);
8180 attr.$set(name, newValue);
8191 * This is a special jqLite.replaceWith, which can replace items which
8192 * have no parents, provided that the containing jqLite collection is provided.
8194 * @param {JqLite=} $rootElement The root of the compile tree. Used so that we can replace nodes
8195 * in the root of the tree.
8196 * @param {JqLite} elementsToRemove The jqLite element which we are going to replace. We keep
8197 * the shell, but replace its DOM node reference.
8198 * @param {Node} newNode The new DOM node.
8200 function replaceWith($rootElement, elementsToRemove, newNode) {
8201 var firstElementToRemove = elementsToRemove[0],
8202 removeCount = elementsToRemove.length,
8203 parent = firstElementToRemove.parentNode,
8207 for (i = 0, ii = $rootElement.length; i < ii; i++) {
8208 if ($rootElement[i] == firstElementToRemove) {
8209 $rootElement[i++] = newNode;
8210 for (var j = i, j2 = j + removeCount - 1,
8211 jj = $rootElement.length;
8212 j < jj; j++, j2++) {
8214 $rootElement[j] = $rootElement[j2];
8216 delete $rootElement[j];
8219 $rootElement.length -= removeCount - 1;
8221 // If the replaced element is also the jQuery .context then replace it
8222 // .context is a deprecated jQuery api, so we should set it only when jQuery set it
8223 // http://api.jquery.com/context/
8224 if ($rootElement.context === firstElementToRemove) {
8225 $rootElement.context = newNode;
8233 parent.replaceChild(newNode, firstElementToRemove);
8236 // TODO(perf): what's this document fragment for? is it needed? can we at least reuse it?
8237 var fragment = document.createDocumentFragment();
8238 fragment.appendChild(firstElementToRemove);
8240 // Copy over user data (that includes Angular's $scope etc.). Don't copy private
8241 // data here because there's no public interface in jQuery to do that and copying over
8242 // event listeners (which is the main use of private data) wouldn't work anyway.
8243 jqLite(newNode).data(jqLite(firstElementToRemove).data());
8245 // Remove data of the replaced element. We cannot just call .remove()
8246 // on the element it since that would deallocate scope that is needed
8247 // for the new node. Instead, remove the data "manually".
8249 delete jqLite.cache[firstElementToRemove[jqLite.expando]];
8251 // jQuery 2.x doesn't expose the data storage. Use jQuery.cleanData to clean up after
8252 // the replaced element. The cleanData version monkey-patched by Angular would cause
8253 // the scope to be trashed and we do need the very same scope to work with the new
8254 // element. However, we cannot just cache the non-patched version and use it here as
8255 // that would break if another library patches the method after Angular does (one
8256 // example is jQuery UI). Instead, set a flag indicating scope destroying should be
8257 // skipped this one time.
8258 skipDestroyOnNextJQueryCleanData = true;
8259 jQuery.cleanData([firstElementToRemove]);
8262 for (var k = 1, kk = elementsToRemove.length; k < kk; k++) {
8263 var element = elementsToRemove[k];
8264 jqLite(element).remove(); // must do this way to clean up expando
8265 fragment.appendChild(element);
8266 delete elementsToRemove[k];
8269 elementsToRemove[0] = newNode;
8270 elementsToRemove.length = 1;
8274 function cloneAndAnnotateFn(fn, annotation) {
8275 return extend(function() { return fn.apply(null, arguments); }, fn, annotation);
8279 function invokeLinkFn(linkFn, scope, $element, attrs, controllers, transcludeFn) {
8281 linkFn(scope, $element, attrs, controllers, transcludeFn);
8283 $exceptionHandler(e, startingTag($element));
8289 var PREFIX_REGEXP = /^((?:x|data)[\:\-_])/i;
8291 * Converts all accepted directives format into proper directive name.
8292 * @param name Name to normalize
8294 function directiveNormalize(name) {
8295 return camelCase(name.replace(PREFIX_REGEXP, ''));
8300 * @name $compile.directive.Attributes
8303 * A shared object between directive compile / linking functions which contains normalized DOM
8304 * element attributes. The values reflect current binding state `{{ }}`. The normalization is
8305 * needed since all of these are treated as equivalent in Angular:
8308 * <span ng:bind="a" ng-bind="a" data-ng-bind="a" x-ng-bind="a">
8314 * @name $compile.directive.Attributes#$attr
8317 * A map of DOM element attribute names to the normalized name. This is
8318 * needed to do reverse lookup from normalized name back to actual name.
8324 * @name $compile.directive.Attributes#$set
8328 * Set DOM element attribute value.
8331 * @param {string} name Normalized element attribute name of the property to modify. The name is
8332 * reverse-translated using the {@link ng.$compile.directive.Attributes#$attr $attr}
8333 * property to the original name.
8334 * @param {string} value Value to set the attribute to. The value can be an interpolated string.
8340 * Closure compiler type information
8343 function nodesetLinkingFn(
8344 /* angular.Scope */ scope,
8345 /* NodeList */ nodeList,
8346 /* Element */ rootElement,
8347 /* function(Function) */ boundTranscludeFn
8350 function directiveLinkingFn(
8351 /* nodesetLinkingFn */ nodesetLinkingFn,
8352 /* angular.Scope */ scope,
8354 /* Element */ rootElement,
8355 /* function(Function) */ boundTranscludeFn
8358 function tokenDifference(str1, str2) {
8360 tokens1 = str1.split(/\s+/),
8361 tokens2 = str2.split(/\s+/);
8364 for (var i = 0; i < tokens1.length; i++) {
8365 var token = tokens1[i];
8366 for (var j = 0; j < tokens2.length; j++) {
8367 if (token == tokens2[j]) continue outer;
8369 values += (values.length > 0 ? ' ' : '') + token;
8374 function removeComments(jqNodes) {
8375 jqNodes = jqLite(jqNodes);
8376 var i = jqNodes.length;
8383 var node = jqNodes[i];
8384 if (node.nodeType === NODE_TYPE_COMMENT) {
8385 splice.call(jqNodes, i, 1);
8391 var $controllerMinErr = minErr('$controller');
8395 * @name $controllerProvider
8397 * The {@link ng.$controller $controller service} is used by Angular to create new
8400 * This provider allows controller registration via the
8401 * {@link ng.$controllerProvider#register register} method.
8403 function $ControllerProvider() {
8404 var controllers = {},
8406 CNTRL_REG = /^(\S+)(\s+as\s+(\w+))?$/;
8411 * @name $controllerProvider#register
8412 * @param {string|Object} name Controller name, or an object map of controllers where the keys are
8413 * the names and the values are the constructors.
8414 * @param {Function|Array} constructor Controller constructor fn (optionally decorated with DI
8415 * annotations in the array notation).
8417 this.register = function(name, constructor) {
8418 assertNotHasOwnProperty(name, 'controller');
8419 if (isObject(name)) {
8420 extend(controllers, name);
8422 controllers[name] = constructor;
8428 * @name $controllerProvider#allowGlobals
8429 * @description If called, allows `$controller` to find controller constructors on `window`
8431 this.allowGlobals = function() {
8436 this.$get = ['$injector', '$window', function($injector, $window) {
8441 * @requires $injector
8443 * @param {Function|string} constructor If called with a function then it's considered to be the
8444 * controller constructor function. Otherwise it's considered to be a string which is used
8445 * to retrieve the controller constructor using the following steps:
8447 * * check if a controller with given name is registered via `$controllerProvider`
8448 * * check if evaluating the string on the current scope returns a constructor
8449 * * if $controllerProvider#allowGlobals, check `window[constructor]` on the global
8450 * `window` object (not recommended)
8452 * The string can use the `controller as property` syntax, where the controller instance is published
8453 * as the specified property on the `scope`; the `scope` must be injected into `locals` param for this
8454 * to work correctly.
8456 * @param {Object} locals Injection locals for Controller.
8457 * @return {Object} Instance of given controller.
8460 * `$controller` service is responsible for instantiating controllers.
8462 * It's just a simple call to {@link auto.$injector $injector}, but extracted into
8463 * a service, so that one can override this service with [BC version](https://gist.github.com/1649788).
8465 return function(expression, locals, later, ident) {
8467 // param `later` --- indicates that the controller's constructor is invoked at a later time.
8468 // If true, $controller will allocate the object with the correct
8469 // prototype chain, but will not invoke the controller until a returned
8470 // callback is invoked.
8471 // param `ident` --- An optional label which overrides the label parsed from the controller
8472 // expression, if any.
8473 var instance, match, constructor, identifier;
8474 later = later === true;
8475 if (ident && isString(ident)) {
8479 if (isString(expression)) {
8480 match = expression.match(CNTRL_REG);
8482 throw $controllerMinErr('ctrlfmt',
8483 "Badly formed controller string '{0}'. " +
8484 "Must match `__name__ as __id__` or `__name__`.", expression);
8486 constructor = match[1],
8487 identifier = identifier || match[3];
8488 expression = controllers.hasOwnProperty(constructor)
8489 ? controllers[constructor]
8490 : getter(locals.$scope, constructor, true) ||
8491 (globals ? getter($window, constructor, true) : undefined);
8493 assertArgFn(expression, constructor, true);
8497 // Instantiate controller later:
8498 // This machinery is used to create an instance of the object before calling the
8499 // controller's constructor itself.
8501 // This allows properties to be added to the controller before the constructor is
8502 // invoked. Primarily, this is used for isolate scope bindings in $compile.
8504 // This feature is not intended for use by applications, and is thus not documented
8506 // Object creation: http://jsperf.com/create-constructor/2
8507 var controllerPrototype = (isArray(expression) ?
8508 expression[expression.length - 1] : expression).prototype;
8509 instance = Object.create(controllerPrototype || null);
8512 addIdentifier(locals, identifier, instance, constructor || expression.name);
8515 return extend(function() {
8516 $injector.invoke(expression, instance, locals, constructor);
8520 identifier: identifier
8524 instance = $injector.instantiate(expression, locals, constructor);
8527 addIdentifier(locals, identifier, instance, constructor || expression.name);
8533 function addIdentifier(locals, identifier, instance, name) {
8534 if (!(locals && isObject(locals.$scope))) {
8535 throw minErr('$controller')('noscp',
8536 "Cannot export controller '{0}' as '{1}'! No $scope object provided via `locals`.",
8540 locals.$scope[identifier] = instance;
8551 * A {@link angular.element jQuery or jqLite} wrapper for the browser's `window.document` object.
8554 <example module="documentExample">
8555 <file name="index.html">
8556 <div ng-controller="ExampleController">
8557 <p>$document title: <b ng-bind="title"></b></p>
8558 <p>window.document title: <b ng-bind="windowTitle"></b></p>
8561 <file name="script.js">
8562 angular.module('documentExample', [])
8563 .controller('ExampleController', ['$scope', '$document', function($scope, $document) {
8564 $scope.title = $document[0].title;
8565 $scope.windowTitle = angular.element(window.document)[0].title;
8570 function $DocumentProvider() {
8571 this.$get = ['$window', function(window) {
8572 return jqLite(window.document);
8578 * @name $exceptionHandler
8582 * Any uncaught exception in angular expressions is delegated to this service.
8583 * The default implementation simply delegates to `$log.error` which logs it into
8584 * the browser console.
8586 * In unit tests, if `angular-mocks.js` is loaded, this service is overridden by
8587 * {@link ngMock.$exceptionHandler mock $exceptionHandler} which aids in testing.
8592 * angular.module('exceptionOverride', []).factory('$exceptionHandler', function() {
8593 * return function(exception, cause) {
8594 * exception.message += ' (caused by "' + cause + '")';
8600 * This example will override the normal action of `$exceptionHandler`, to make angular
8601 * exceptions fail hard when they happen, instead of just logging to the console.
8604 * Note, that code executed in event-listeners (even those registered using jqLite's `on`/`bind`
8605 * methods) does not delegate exceptions to the {@link ng.$exceptionHandler $exceptionHandler}
8606 * (unless executed during a digest).
8608 * If you wish, you can manually delegate exceptions, e.g.
8609 * `try { ... } catch(e) { $exceptionHandler(e); }`
8611 * @param {Error} exception Exception associated with the error.
8612 * @param {string=} cause optional information about the context in which
8613 * the error was thrown.
8616 function $ExceptionHandlerProvider() {
8617 this.$get = ['$log', function($log) {
8618 return function(exception, cause) {
8619 $log.error.apply($log, arguments);
8624 var APPLICATION_JSON = 'application/json';
8625 var CONTENT_TYPE_APPLICATION_JSON = {'Content-Type': APPLICATION_JSON + ';charset=utf-8'};
8626 var JSON_START = /^\[|^\{(?!\{)/;
8631 var JSON_PROTECTION_PREFIX = /^\)\]\}',?\n/;
8633 function defaultHttpResponseTransform(data, headers) {
8634 if (isString(data)) {
8635 // Strip json vulnerability protection prefix and trim whitespace
8636 var tempData = data.replace(JSON_PROTECTION_PREFIX, '').trim();
8639 var contentType = headers('Content-Type');
8640 if ((contentType && (contentType.indexOf(APPLICATION_JSON) === 0)) || isJsonLike(tempData)) {
8641 data = fromJson(tempData);
8649 function isJsonLike(str) {
8650 var jsonStart = str.match(JSON_START);
8651 return jsonStart && JSON_ENDS[jsonStart[0]].test(str);
8655 * Parse headers into key value object
8657 * @param {string} headers Raw headers as a string
8658 * @returns {Object} Parsed headers as key value object
8660 function parseHeaders(headers) {
8661 var parsed = createMap(), key, val, i;
8663 if (!headers) return parsed;
8665 forEach(headers.split('\n'), function(line) {
8666 i = line.indexOf(':');
8667 key = lowercase(trim(line.substr(0, i)));
8668 val = trim(line.substr(i + 1));
8671 parsed[key] = parsed[key] ? parsed[key] + ', ' + val : val;
8680 * Returns a function that provides access to parsed headers.
8682 * Headers are lazy parsed when first requested.
8685 * @param {(string|Object)} headers Headers to provide access to.
8686 * @returns {function(string=)} Returns a getter function which if called with:
8688 * - if called with single an argument returns a single header value or null
8689 * - if called with no arguments returns an object containing all headers.
8691 function headersGetter(headers) {
8692 var headersObj = isObject(headers) ? headers : undefined;
8694 return function(name) {
8695 if (!headersObj) headersObj = parseHeaders(headers);
8698 var value = headersObj[lowercase(name)];
8699 if (value === void 0) {
8711 * Chain all given functions
8713 * This function is used for both request and response transforming
8715 * @param {*} data Data to transform.
8716 * @param {function(string=)} headers HTTP headers getter fn.
8717 * @param {number} status HTTP status code of the response.
8718 * @param {(Function|Array.<Function>)} fns Function or an array of functions.
8719 * @returns {*} Transformed data.
8721 function transformData(data, headers, status, fns) {
8722 if (isFunction(fns))
8723 return fns(data, headers, status);
8725 forEach(fns, function(fn) {
8726 data = fn(data, headers, status);
8733 function isSuccess(status) {
8734 return 200 <= status && status < 300;
8740 * @name $httpProvider
8742 * Use `$httpProvider` to change the default behavior of the {@link ng.$http $http} service.
8744 function $HttpProvider() {
8747 * @name $httpProvider#defaults
8750 * Object containing default values for all {@link ng.$http $http} requests.
8752 * - **`defaults.cache`** - {Object} - an object built with {@link ng.$cacheFactory `$cacheFactory`}
8753 * that will provide the cache for all requests who set their `cache` property to `true`.
8754 * If you set the `default.cache = false` then only requests that specify their own custom
8755 * cache object will be cached. See {@link $http#caching $http Caching} for more information.
8757 * - **`defaults.xsrfCookieName`** - {string} - Name of cookie containing the XSRF token.
8758 * Defaults value is `'XSRF-TOKEN'`.
8760 * - **`defaults.xsrfHeaderName`** - {string} - Name of HTTP header to populate with the
8761 * XSRF token. Defaults value is `'X-XSRF-TOKEN'`.
8763 * - **`defaults.headers`** - {Object} - Default headers for all $http requests.
8764 * Refer to {@link ng.$http#setting-http-headers $http} for documentation on
8765 * setting default headers.
8766 * - **`defaults.headers.common`**
8767 * - **`defaults.headers.post`**
8768 * - **`defaults.headers.put`**
8769 * - **`defaults.headers.patch`**
8772 var defaults = this.defaults = {
8773 // transform incoming response data
8774 transformResponse: [defaultHttpResponseTransform],
8776 // transform outgoing request data
8777 transformRequest: [function(d) {
8778 return isObject(d) && !isFile(d) && !isBlob(d) && !isFormData(d) ? toJson(d) : d;
8784 'Accept': 'application/json, text/plain, */*'
8786 post: shallowCopy(CONTENT_TYPE_APPLICATION_JSON),
8787 put: shallowCopy(CONTENT_TYPE_APPLICATION_JSON),
8788 patch: shallowCopy(CONTENT_TYPE_APPLICATION_JSON)
8791 xsrfCookieName: 'XSRF-TOKEN',
8792 xsrfHeaderName: 'X-XSRF-TOKEN'
8795 var useApplyAsync = false;
8798 * @name $httpProvider#useApplyAsync
8801 * Configure $http service to combine processing of multiple http responses received at around
8802 * the same time via {@link ng.$rootScope.Scope#$applyAsync $rootScope.$applyAsync}. This can result in
8803 * significant performance improvement for bigger applications that make many HTTP requests
8804 * concurrently (common during application bootstrap).
8806 * Defaults to false. If no value is specifed, returns the current configured value.
8808 * @param {boolean=} value If true, when requests are loaded, they will schedule a deferred
8809 * "apply" on the next tick, giving time for subsequent requests in a roughly ~10ms window
8810 * to load and share the same digest cycle.
8812 * @returns {boolean|Object} If a value is specified, returns the $httpProvider for chaining.
8813 * otherwise, returns the current configured value.
8815 this.useApplyAsync = function(value) {
8816 if (isDefined(value)) {
8817 useApplyAsync = !!value;
8820 return useApplyAsync;
8825 * @name $httpProvider#interceptors
8828 * Array containing service factories for all synchronous or asynchronous {@link ng.$http $http}
8829 * pre-processing of request or postprocessing of responses.
8831 * These service factories are ordered by request, i.e. they are applied in the same order as the
8832 * array, on request, but reverse order, on response.
8834 * {@link ng.$http#interceptors Interceptors detailed info}
8836 var interceptorFactories = this.interceptors = [];
8838 this.$get = ['$httpBackend', '$browser', '$cacheFactory', '$rootScope', '$q', '$injector',
8839 function($httpBackend, $browser, $cacheFactory, $rootScope, $q, $injector) {
8841 var defaultCache = $cacheFactory('$http');
8844 * Interceptors stored in reverse order. Inner interceptors before outer interceptors.
8845 * The reversal is needed so that we can build up the interception chain around the
8848 var reversedInterceptors = [];
8850 forEach(interceptorFactories, function(interceptorFactory) {
8851 reversedInterceptors.unshift(isString(interceptorFactory)
8852 ? $injector.get(interceptorFactory) : $injector.invoke(interceptorFactory));
8859 * @requires ng.$httpBackend
8860 * @requires $cacheFactory
8861 * @requires $rootScope
8863 * @requires $injector
8866 * The `$http` service is a core Angular service that facilitates communication with the remote
8867 * HTTP servers via the browser's [XMLHttpRequest](https://developer.mozilla.org/en/xmlhttprequest)
8868 * object or via [JSONP](http://en.wikipedia.org/wiki/JSONP).
8870 * For unit testing applications that use `$http` service, see
8871 * {@link ngMock.$httpBackend $httpBackend mock}.
8873 * For a higher level of abstraction, please check out the {@link ngResource.$resource
8874 * $resource} service.
8876 * The $http API is based on the {@link ng.$q deferred/promise APIs} exposed by
8877 * the $q service. While for simple usage patterns this doesn't matter much, for advanced usage
8878 * it is important to familiarize yourself with these APIs and the guarantees they provide.
8882 * The `$http` service is a function which takes a single argument — a configuration object —
8883 * that is used to generate an HTTP request and returns a {@link ng.$q promise}
8884 * with two $http specific methods: `success` and `error`.
8887 * // Simple GET request example :
8888 * $http.get('/someUrl').
8889 * success(function(data, status, headers, config) {
8890 * // this callback will be called asynchronously
8891 * // when the response is available
8893 * error(function(data, status, headers, config) {
8894 * // called asynchronously if an error occurs
8895 * // or server returns response with an error status.
8900 * // Simple POST request example (passing data) :
8901 * $http.post('/someUrl', {msg:'hello word!'}).
8902 * success(function(data, status, headers, config) {
8903 * // this callback will be called asynchronously
8904 * // when the response is available
8906 * error(function(data, status, headers, config) {
8907 * // called asynchronously if an error occurs
8908 * // or server returns response with an error status.
8913 * Since the returned value of calling the $http function is a `promise`, you can also use
8914 * the `then` method to register callbacks, and these callbacks will receive a single argument –
8915 * an object representing the response. See the API signature and type info below for more
8918 * A response status code between 200 and 299 is considered a success status and
8919 * will result in the success callback being called. Note that if the response is a redirect,
8920 * XMLHttpRequest will transparently follow it, meaning that the error callback will not be
8921 * called for such responses.
8923 * ## Writing Unit Tests that use $http
8924 * When unit testing (using {@link ngMock ngMock}), it is necessary to call
8925 * {@link ngMock.$httpBackend#flush $httpBackend.flush()} to flush each pending
8926 * request using trained responses.
8929 * $httpBackend.expectGET(...);
8931 * $httpBackend.flush();
8934 * ## Shortcut methods
8936 * Shortcut methods are also available. All shortcut methods require passing in the URL, and
8937 * request data must be passed in for POST/PUT requests.
8940 * $http.get('/someUrl').success(successCallback);
8941 * $http.post('/someUrl', data).success(successCallback);
8944 * Complete list of shortcut methods:
8946 * - {@link ng.$http#get $http.get}
8947 * - {@link ng.$http#head $http.head}
8948 * - {@link ng.$http#post $http.post}
8949 * - {@link ng.$http#put $http.put}
8950 * - {@link ng.$http#delete $http.delete}
8951 * - {@link ng.$http#jsonp $http.jsonp}
8952 * - {@link ng.$http#patch $http.patch}
8955 * ## Setting HTTP Headers
8957 * The $http service will automatically add certain HTTP headers to all requests. These defaults
8958 * can be fully configured by accessing the `$httpProvider.defaults.headers` configuration
8959 * object, which currently contains this default configuration:
8961 * - `$httpProvider.defaults.headers.common` (headers that are common for all requests):
8962 * - `Accept: application/json, text/plain, * / *`
8963 * - `$httpProvider.defaults.headers.post`: (header defaults for POST requests)
8964 * - `Content-Type: application/json`
8965 * - `$httpProvider.defaults.headers.put` (header defaults for PUT requests)
8966 * - `Content-Type: application/json`
8968 * To add or overwrite these defaults, simply add or remove a property from these configuration
8969 * objects. To add headers for an HTTP method other than POST or PUT, simply add a new object
8970 * with the lowercased HTTP method name as the key, e.g.
8971 * `$httpProvider.defaults.headers.get = { 'My-Header' : 'value' }.
8973 * The defaults can also be set at runtime via the `$http.defaults` object in the same
8974 * fashion. For example:
8977 * module.run(function($http) {
8978 * $http.defaults.headers.common.Authorization = 'Basic YmVlcDpib29w'
8982 * In addition, you can supply a `headers` property in the config object passed when
8983 * calling `$http(config)`, which overrides the defaults without changing them globally.
8985 * To explicitly remove a header automatically added via $httpProvider.defaults.headers on a per request basis,
8986 * Use the `headers` property, setting the desired header to `undefined`. For example:
8991 * url: 'http://example.com',
8993 * 'Content-Type': undefined
8995 * data: { test: 'test' }
8998 * $http(req).success(function(){...}).error(function(){...});
9001 * ## Transforming Requests and Responses
9003 * Both requests and responses can be transformed using transformation functions: `transformRequest`
9004 * and `transformResponse`. These properties can be a single function that returns
9005 * the transformed value (`function(data, headersGetter, status)`) or an array of such transformation functions,
9006 * which allows you to `push` or `unshift` a new transformation function into the transformation chain.
9008 * ### Default Transformations
9010 * The `$httpProvider` provider and `$http` service expose `defaults.transformRequest` and
9011 * `defaults.transformResponse` properties. If a request does not provide its own transformations
9012 * then these will be applied.
9014 * You can augment or replace the default transformations by modifying these properties by adding to or
9015 * replacing the array.
9017 * Angular provides the following default transformations:
9019 * Request transformations (`$httpProvider.defaults.transformRequest` and `$http.defaults.transformRequest`):
9021 * - If the `data` property of the request configuration object contains an object, serialize it
9024 * Response transformations (`$httpProvider.defaults.transformResponse` and `$http.defaults.transformResponse`):
9026 * - If XSRF prefix is detected, strip it (see Security Considerations section below).
9027 * - If JSON response is detected, deserialize it using a JSON parser.
9030 * ### Overriding the Default Transformations Per Request
9032 * If you wish override the request/response transformations only for a single request then provide
9033 * `transformRequest` and/or `transformResponse` properties on the configuration object passed
9036 * Note that if you provide these properties on the config object the default transformations will be
9037 * overwritten. If you wish to augment the default transformations then you must include them in your
9038 * local transformation array.
9040 * The following code demonstrates adding a new response transformation to be run after the default response
9041 * transformations have been run.
9044 * function appendTransform(defaults, transform) {
9046 * // We can't guarantee that the default transformation is an array
9047 * defaults = angular.isArray(defaults) ? defaults : [defaults];
9049 * // Append the new transformation to the defaults
9050 * return defaults.concat(transform);
9056 * transformResponse: appendTransform($http.defaults.transformResponse, function(value) {
9057 * return doTransform(value);
9065 * To enable caching, set the request configuration `cache` property to `true` (to use default
9066 * cache) or to a custom cache object (built with {@link ng.$cacheFactory `$cacheFactory`}).
9067 * When the cache is enabled, `$http` stores the response from the server in the specified
9068 * cache. The next time the same request is made, the response is served from the cache without
9069 * sending a request to the server.
9071 * Note that even if the response is served from cache, delivery of the data is asynchronous in
9072 * the same way that real requests are.
9074 * If there are multiple GET requests for the same URL that should be cached using the same
9075 * cache, but the cache is not populated yet, only one request to the server will be made and
9076 * the remaining requests will be fulfilled using the response from the first request.
9078 * You can change the default cache to a new object (built with
9079 * {@link ng.$cacheFactory `$cacheFactory`}) by updating the
9080 * {@link ng.$http#defaults `$http.defaults.cache`} property. All requests who set
9081 * their `cache` property to `true` will now use this cache object.
9083 * If you set the default cache to `false` then only requests that specify their own custom
9084 * cache object will be cached.
9088 * Before you start creating interceptors, be sure to understand the
9089 * {@link ng.$q $q and deferred/promise APIs}.
9091 * For purposes of global error handling, authentication, or any kind of synchronous or
9092 * asynchronous pre-processing of request or postprocessing of responses, it is desirable to be
9093 * able to intercept requests before they are handed to the server and
9094 * responses before they are handed over to the application code that
9095 * initiated these requests. The interceptors leverage the {@link ng.$q
9096 * promise APIs} to fulfill this need for both synchronous and asynchronous pre-processing.
9098 * The interceptors are service factories that are registered with the `$httpProvider` by
9099 * adding them to the `$httpProvider.interceptors` array. The factory is called and
9100 * injected with dependencies (if specified) and returns the interceptor.
9102 * There are two kinds of interceptors (and two kinds of rejection interceptors):
9104 * * `request`: interceptors get called with a http `config` object. The function is free to
9105 * modify the `config` object or create a new one. The function needs to return the `config`
9106 * object directly, or a promise containing the `config` or a new `config` object.
9107 * * `requestError`: interceptor gets called when a previous interceptor threw an error or
9108 * resolved with a rejection.
9109 * * `response`: interceptors get called with http `response` object. The function is free to
9110 * modify the `response` object or create a new one. The function needs to return the `response`
9111 * object directly, or as a promise containing the `response` or a new `response` object.
9112 * * `responseError`: interceptor gets called when a previous interceptor threw an error or
9113 * resolved with a rejection.
9117 * // register the interceptor as a service
9118 * $provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) {
9120 * // optional method
9121 * 'request': function(config) {
9122 * // do something on success
9126 * // optional method
9127 * 'requestError': function(rejection) {
9128 * // do something on error
9129 * if (canRecover(rejection)) {
9130 * return responseOrNewPromise
9132 * return $q.reject(rejection);
9137 * // optional method
9138 * 'response': function(response) {
9139 * // do something on success
9143 * // optional method
9144 * 'responseError': function(rejection) {
9145 * // do something on error
9146 * if (canRecover(rejection)) {
9147 * return responseOrNewPromise
9149 * return $q.reject(rejection);
9154 * $httpProvider.interceptors.push('myHttpInterceptor');
9157 * // alternatively, register the interceptor via an anonymous factory
9158 * $httpProvider.interceptors.push(function($q, dependency1, dependency2) {
9160 * 'request': function(config) {
9164 * 'response': function(response) {
9171 * ## Security Considerations
9173 * When designing web applications, consider security threats from:
9175 * - [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx)
9176 * - [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery)
9178 * Both server and the client must cooperate in order to eliminate these threats. Angular comes
9179 * pre-configured with strategies that address these issues, but for this to work backend server
9180 * cooperation is required.
9182 * ### JSON Vulnerability Protection
9184 * A [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx)
9185 * allows third party website to turn your JSON resource URL into
9186 * [JSONP](http://en.wikipedia.org/wiki/JSONP) request under some conditions. To
9187 * counter this your server can prefix all JSON requests with following string `")]}',\n"`.
9188 * Angular will automatically strip the prefix before processing it as JSON.
9190 * For example if your server needs to return:
9195 * which is vulnerable to attack, your server can return:
9201 * Angular will strip the prefix, before processing the JSON.
9204 * ### Cross Site Request Forgery (XSRF) Protection
9206 * [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery) is a technique by which
9207 * an unauthorized site can gain your user's private data. Angular provides a mechanism
9208 * to counter XSRF. When performing XHR requests, the $http service reads a token from a cookie
9209 * (by default, `XSRF-TOKEN`) and sets it as an HTTP header (`X-XSRF-TOKEN`). Since only
9210 * JavaScript that runs on your domain could read the cookie, your server can be assured that
9211 * the XHR came from JavaScript running on your domain. The header will not be set for
9212 * cross-domain requests.
9214 * To take advantage of this, your server needs to set a token in a JavaScript readable session
9215 * cookie called `XSRF-TOKEN` on the first HTTP GET request. On subsequent XHR requests the
9216 * server can verify that the cookie matches `X-XSRF-TOKEN` HTTP header, and therefore be sure
9217 * that only JavaScript running on your domain could have sent the request. The token must be
9218 * unique for each user and must be verifiable by the server (to prevent the JavaScript from
9219 * making up its own tokens). We recommend that the token is a digest of your site's
9220 * authentication cookie with a [salt](https://en.wikipedia.org/wiki/Salt_(cryptography))
9221 * for added security.
9223 * The name of the headers can be specified using the xsrfHeaderName and xsrfCookieName
9224 * properties of either $httpProvider.defaults at config-time, $http.defaults at run-time,
9225 * or the per-request config object.
9228 * @param {object} config Object describing the request to be made and how it should be
9229 * processed. The object has following properties:
9231 * - **method** – `{string}` – HTTP method (e.g. 'GET', 'POST', etc)
9232 * - **url** – `{string}` – Absolute or relative URL of the resource that is being requested.
9233 * - **params** – `{Object.<string|Object>}` – Map of strings or objects which will be turned
9234 * to `?key1=value1&key2=value2` after the url. If the value is not a string, it will be
9236 * - **data** – `{string|Object}` – Data to be sent as the request message data.
9237 * - **headers** – `{Object}` – Map of strings or functions which return strings representing
9238 * HTTP headers to send to the server. If the return value of a function is null, the
9239 * header will not be sent.
9240 * - **xsrfHeaderName** – `{string}` – Name of HTTP header to populate with the XSRF token.
9241 * - **xsrfCookieName** – `{string}` – Name of cookie containing the XSRF token.
9242 * - **transformRequest** –
9243 * `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` –
9244 * transform function or an array of such functions. The transform function takes the http
9245 * request body and headers and returns its transformed (typically serialized) version.
9246 * See {@link ng.$http#overriding-the-default-transformations-per-request
9247 * Overriding the Default Transformations}
9248 * - **transformResponse** –
9249 * `{function(data, headersGetter, status)|Array.<function(data, headersGetter, status)>}` –
9250 * transform function or an array of such functions. The transform function takes the http
9251 * response body, headers and status and returns its transformed (typically deserialized) version.
9252 * See {@link ng.$http#overriding-the-default-transformations-per-request
9253 * Overriding the Default Transformations}
9254 * - **cache** – `{boolean|Cache}` – If true, a default $http cache will be used to cache the
9255 * GET request, otherwise if a cache instance built with
9256 * {@link ng.$cacheFactory $cacheFactory}, this cache will be used for
9258 * - **timeout** – `{number|Promise}` – timeout in milliseconds, or {@link ng.$q promise}
9259 * that should abort the request when resolved.
9260 * - **withCredentials** - `{boolean}` - whether to set the `withCredentials` flag on the
9261 * XHR object. See [requests with credentials](https://developer.mozilla.org/docs/Web/HTTP/Access_control_CORS#Requests_with_credentials)
9262 * for more information.
9263 * - **responseType** - `{string}` - see
9264 * [requestType](https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest#responseType).
9266 * @returns {HttpPromise} Returns a {@link ng.$q promise} object with the
9267 * standard `then` method and two http specific methods: `success` and `error`. The `then`
9268 * method takes two arguments a success and an error callback which will be called with a
9269 * response object. The `success` and `error` methods take a single argument - a function that
9270 * will be called when the request succeeds or fails respectively. The arguments passed into
9271 * these functions are destructured representation of the response object passed into the
9272 * `then` method. The response object has these properties:
9274 * - **data** – `{string|Object}` – The response body transformed with the transform
9276 * - **status** – `{number}` – HTTP status code of the response.
9277 * - **headers** – `{function([headerName])}` – Header getter function.
9278 * - **config** – `{Object}` – The configuration object that was used to generate the request.
9279 * - **statusText** – `{string}` – HTTP status text of the response.
9281 * @property {Array.<Object>} pendingRequests Array of config objects for currently pending
9282 * requests. This is primarily meant to be used for debugging purposes.
9286 <example module="httpExample">
9287 <file name="index.html">
9288 <div ng-controller="FetchController">
9289 <select ng-model="method">
9290 <option>GET</option>
9291 <option>JSONP</option>
9293 <input type="text" ng-model="url" size="80"/>
9294 <button id="fetchbtn" ng-click="fetch()">fetch</button><br>
9295 <button id="samplegetbtn" ng-click="updateModel('GET', 'http-hello.html')">Sample GET</button>
9296 <button id="samplejsonpbtn"
9297 ng-click="updateModel('JSONP',
9298 'https://angularjs.org/greet.php?callback=JSON_CALLBACK&name=Super%20Hero')">
9301 <button id="invalidjsonpbtn"
9302 ng-click="updateModel('JSONP', 'https://angularjs.org/doesntexist&callback=JSON_CALLBACK')">
9305 <pre>http status code: {{status}}</pre>
9306 <pre>http response data: {{data}}</pre>
9309 <file name="script.js">
9310 angular.module('httpExample', [])
9311 .controller('FetchController', ['$scope', '$http', '$templateCache',
9312 function($scope, $http, $templateCache) {
9313 $scope.method = 'GET';
9314 $scope.url = 'http-hello.html';
9316 $scope.fetch = function() {
9318 $scope.response = null;
9320 $http({method: $scope.method, url: $scope.url, cache: $templateCache}).
9321 success(function(data, status) {
9322 $scope.status = status;
9325 error(function(data, status) {
9326 $scope.data = data || "Request failed";
9327 $scope.status = status;
9331 $scope.updateModel = function(method, url) {
9332 $scope.method = method;
9337 <file name="http-hello.html">
9340 <file name="protractor.js" type="protractor">
9341 var status = element(by.binding('status'));
9342 var data = element(by.binding('data'));
9343 var fetchBtn = element(by.id('fetchbtn'));
9344 var sampleGetBtn = element(by.id('samplegetbtn'));
9345 var sampleJsonpBtn = element(by.id('samplejsonpbtn'));
9346 var invalidJsonpBtn = element(by.id('invalidjsonpbtn'));
9348 it('should make an xhr GET request', function() {
9349 sampleGetBtn.click();
9351 expect(status.getText()).toMatch('200');
9352 expect(data.getText()).toMatch(/Hello, \$http!/);
9355 // Commented out due to flakes. See https://github.com/angular/angular.js/issues/9185
9356 // it('should make a JSONP request to angularjs.org', function() {
9357 // sampleJsonpBtn.click();
9358 // fetchBtn.click();
9359 // expect(status.getText()).toMatch('200');
9360 // expect(data.getText()).toMatch(/Super Hero!/);
9363 it('should make JSONP request to invalid URL and invoke the error handler',
9365 invalidJsonpBtn.click();
9367 expect(status.getText()).toMatch('0');
9368 expect(data.getText()).toMatch('Request failed');
9373 function $http(requestConfig) {
9375 if (!angular.isObject(requestConfig)) {
9376 throw minErr('$http')('badreq', 'Http request configuration must be an object. Received: {0}', requestConfig);
9379 var config = extend({
9381 transformRequest: defaults.transformRequest,
9382 transformResponse: defaults.transformResponse
9385 config.headers = mergeHeaders(requestConfig);
9386 config.method = uppercase(config.method);
9388 var serverRequest = function(config) {
9389 var headers = config.headers;
9390 var reqData = transformData(config.data, headersGetter(headers), undefined, config.transformRequest);
9392 // strip content-type if data is undefined
9393 if (isUndefined(reqData)) {
9394 forEach(headers, function(value, header) {
9395 if (lowercase(header) === 'content-type') {
9396 delete headers[header];
9401 if (isUndefined(config.withCredentials) && !isUndefined(defaults.withCredentials)) {
9402 config.withCredentials = defaults.withCredentials;
9406 return sendReq(config, reqData).then(transformResponse, transformResponse);
9409 var chain = [serverRequest, undefined];
9410 var promise = $q.when(config);
9412 // apply interceptors
9413 forEach(reversedInterceptors, function(interceptor) {
9414 if (interceptor.request || interceptor.requestError) {
9415 chain.unshift(interceptor.request, interceptor.requestError);
9417 if (interceptor.response || interceptor.responseError) {
9418 chain.push(interceptor.response, interceptor.responseError);
9422 while (chain.length) {
9423 var thenFn = chain.shift();
9424 var rejectFn = chain.shift();
9426 promise = promise.then(thenFn, rejectFn);
9429 promise.success = function(fn) {
9430 assertArgFn(fn, 'fn');
9432 promise.then(function(response) {
9433 fn(response.data, response.status, response.headers, config);
9438 promise.error = function(fn) {
9439 assertArgFn(fn, 'fn');
9441 promise.then(null, function(response) {
9442 fn(response.data, response.status, response.headers, config);
9449 function transformResponse(response) {
9450 // make a copy since the response must be cacheable
9451 var resp = extend({}, response);
9452 if (!response.data) {
9453 resp.data = response.data;
9455 resp.data = transformData(response.data, response.headers, response.status, config.transformResponse);
9457 return (isSuccess(response.status))
9462 function executeHeaderFns(headers) {
9463 var headerContent, processedHeaders = {};
9465 forEach(headers, function(headerFn, header) {
9466 if (isFunction(headerFn)) {
9467 headerContent = headerFn();
9468 if (headerContent != null) {
9469 processedHeaders[header] = headerContent;
9472 processedHeaders[header] = headerFn;
9476 return processedHeaders;
9479 function mergeHeaders(config) {
9480 var defHeaders = defaults.headers,
9481 reqHeaders = extend({}, config.headers),
9482 defHeaderName, lowercaseDefHeaderName, reqHeaderName;
9484 defHeaders = extend({}, defHeaders.common, defHeaders[lowercase(config.method)]);
9486 // using for-in instead of forEach to avoid unecessary iteration after header has been found
9487 defaultHeadersIteration:
9488 for (defHeaderName in defHeaders) {
9489 lowercaseDefHeaderName = lowercase(defHeaderName);
9491 for (reqHeaderName in reqHeaders) {
9492 if (lowercase(reqHeaderName) === lowercaseDefHeaderName) {
9493 continue defaultHeadersIteration;
9497 reqHeaders[defHeaderName] = defHeaders[defHeaderName];
9500 // execute if header value is a function for merged headers
9501 return executeHeaderFns(reqHeaders);
9505 $http.pendingRequests = [];
9512 * Shortcut method to perform `GET` request.
9514 * @param {string} url Relative or absolute URL specifying the destination of the request
9515 * @param {Object=} config Optional configuration object
9516 * @returns {HttpPromise} Future object
9521 * @name $http#delete
9524 * Shortcut method to perform `DELETE` request.
9526 * @param {string} url Relative or absolute URL specifying the destination of the request
9527 * @param {Object=} config Optional configuration object
9528 * @returns {HttpPromise} Future object
9536 * Shortcut method to perform `HEAD` request.
9538 * @param {string} url Relative or absolute URL specifying the destination of the request
9539 * @param {Object=} config Optional configuration object
9540 * @returns {HttpPromise} Future object
9548 * Shortcut method to perform `JSONP` request.
9550 * @param {string} url Relative or absolute URL specifying the destination of the request.
9551 * The name of the callback should be the string `JSON_CALLBACK`.
9552 * @param {Object=} config Optional configuration object
9553 * @returns {HttpPromise} Future object
9555 createShortMethods('get', 'delete', 'head', 'jsonp');
9562 * Shortcut method to perform `POST` request.
9564 * @param {string} url Relative or absolute URL specifying the destination of the request
9565 * @param {*} data Request content
9566 * @param {Object=} config Optional configuration object
9567 * @returns {HttpPromise} Future object
9575 * Shortcut method to perform `PUT` request.
9577 * @param {string} url Relative or absolute URL specifying the destination of the request
9578 * @param {*} data Request content
9579 * @param {Object=} config Optional configuration object
9580 * @returns {HttpPromise} Future object
9588 * Shortcut method to perform `PATCH` request.
9590 * @param {string} url Relative or absolute URL specifying the destination of the request
9591 * @param {*} data Request content
9592 * @param {Object=} config Optional configuration object
9593 * @returns {HttpPromise} Future object
9595 createShortMethodsWithData('post', 'put', 'patch');
9599 * @name $http#defaults
9602 * Runtime equivalent of the `$httpProvider.defaults` property. Allows configuration of
9603 * default headers, withCredentials as well as request and response transformations.
9605 * See "Setting HTTP Headers" and "Transforming Requests and Responses" sections above.
9607 $http.defaults = defaults;
9613 function createShortMethods(names) {
9614 forEach(arguments, function(name) {
9615 $http[name] = function(url, config) {
9616 return $http(extend(config || {}, {
9625 function createShortMethodsWithData(name) {
9626 forEach(arguments, function(name) {
9627 $http[name] = function(url, data, config) {
9628 return $http(extend(config || {}, {
9639 * Makes the request.
9641 * !!! ACCESSES CLOSURE VARS:
9642 * $httpBackend, defaults, $log, $rootScope, defaultCache, $http.pendingRequests
9644 function sendReq(config, reqData) {
9645 var deferred = $q.defer(),
9646 promise = deferred.promise,
9649 reqHeaders = config.headers,
9650 url = buildUrl(config.url, config.params);
9652 $http.pendingRequests.push(config);
9653 promise.then(removePendingReq, removePendingReq);
9656 if ((config.cache || defaults.cache) && config.cache !== false &&
9657 (config.method === 'GET' || config.method === 'JSONP')) {
9658 cache = isObject(config.cache) ? config.cache
9659 : isObject(defaults.cache) ? defaults.cache
9664 cachedResp = cache.get(url);
9665 if (isDefined(cachedResp)) {
9666 if (isPromiseLike(cachedResp)) {
9667 // cached request has already been sent, but there is no response yet
9668 cachedResp.then(resolvePromiseWithResult, resolvePromiseWithResult);
9670 // serving from cache
9671 if (isArray(cachedResp)) {
9672 resolvePromise(cachedResp[1], cachedResp[0], shallowCopy(cachedResp[2]), cachedResp[3]);
9674 resolvePromise(cachedResp, 200, {}, 'OK');
9678 // put the promise for the non-transformed response into cache as a placeholder
9679 cache.put(url, promise);
9684 // if we won't have the response in cache, set the xsrf headers and
9685 // send the request to the backend
9686 if (isUndefined(cachedResp)) {
9687 var xsrfValue = urlIsSameOrigin(config.url)
9688 ? $browser.cookies()[config.xsrfCookieName || defaults.xsrfCookieName]
9691 reqHeaders[(config.xsrfHeaderName || defaults.xsrfHeaderName)] = xsrfValue;
9694 $httpBackend(config.method, url, reqData, done, reqHeaders, config.timeout,
9695 config.withCredentials, config.responseType);
9702 * Callback registered to $httpBackend():
9703 * - caches the response if desired
9704 * - resolves the raw $http promise
9707 function done(status, response, headersString, statusText) {
9709 if (isSuccess(status)) {
9710 cache.put(url, [status, response, parseHeaders(headersString), statusText]);
9712 // remove promise from the cache
9717 function resolveHttpPromise() {
9718 resolvePromise(response, status, headersString, statusText);
9721 if (useApplyAsync) {
9722 $rootScope.$applyAsync(resolveHttpPromise);
9724 resolveHttpPromise();
9725 if (!$rootScope.$$phase) $rootScope.$apply();
9731 * Resolves the raw $http promise.
9733 function resolvePromise(response, status, headers, statusText) {
9734 //status: HTTP response status code, 0, -1 (aborted by timeout / promise)
9735 status = status >= -1 ? status : 0;
9737 (isSuccess(status) ? deferred.resolve : deferred.reject)({
9740 headers: headersGetter(headers),
9742 statusText: statusText
9746 function resolvePromiseWithResult(result) {
9747 resolvePromise(result.data, result.status, shallowCopy(result.headers()), result.statusText);
9750 function removePendingReq() {
9751 var idx = $http.pendingRequests.indexOf(config);
9752 if (idx !== -1) $http.pendingRequests.splice(idx, 1);
9757 function buildUrl(url, params) {
9758 if (!params) return url;
9760 forEachSorted(params, function(value, key) {
9761 if (value === null || isUndefined(value)) return;
9762 if (!isArray(value)) value = [value];
9764 forEach(value, function(v) {
9767 v = v.toISOString();
9772 parts.push(encodeUriQuery(key) + '=' +
9776 if (parts.length > 0) {
9777 url += ((url.indexOf('?') == -1) ? '?' : '&') + parts.join('&');
9784 function createXhr() {
9785 return new window.XMLHttpRequest();
9790 * @name $httpBackend
9792 * @requires $document
9795 * HTTP backend used by the {@link ng.$http service} that delegates to
9796 * XMLHttpRequest object or JSONP and deals with browser incompatibilities.
9798 * You should never need to use this service directly, instead use the higher-level abstractions:
9799 * {@link ng.$http $http} or {@link ngResource.$resource $resource}.
9801 * During testing this implementation is swapped with {@link ngMock.$httpBackend mock
9802 * $httpBackend} which can be trained with responses.
9804 function $HttpBackendProvider() {
9805 this.$get = ['$browser', '$window', '$document', function($browser, $window, $document) {
9806 return createHttpBackend($browser, createXhr, $browser.defer, $window.angular.callbacks, $document[0]);
9810 function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDocument) {
9811 // TODO(vojta): fix the signature
9812 return function(method, url, post, callback, headers, timeout, withCredentials, responseType) {
9813 $browser.$$incOutstandingRequestCount();
9814 url = url || $browser.url();
9816 if (lowercase(method) == 'jsonp') {
9817 var callbackId = '_' + (callbacks.counter++).toString(36);
9818 callbacks[callbackId] = function(data) {
9819 callbacks[callbackId].data = data;
9820 callbacks[callbackId].called = true;
9823 var jsonpDone = jsonpReq(url.replace('JSON_CALLBACK', 'angular.callbacks.' + callbackId),
9824 callbackId, function(status, text) {
9825 completeRequest(callback, status, callbacks[callbackId].data, "", text);
9826 callbacks[callbackId] = noop;
9830 var xhr = createXhr();
9832 xhr.open(method, url, true);
9833 forEach(headers, function(value, key) {
9834 if (isDefined(value)) {
9835 xhr.setRequestHeader(key, value);
9839 xhr.onload = function requestLoaded() {
9840 var statusText = xhr.statusText || '';
9842 // responseText is the old-school way of retrieving response (supported by IE9)
9843 // response/responseType properties were introduced in XHR Level2 spec (supported by IE10)
9844 var response = ('response' in xhr) ? xhr.response : xhr.responseText;
9846 // normalize IE9 bug (http://bugs.jquery.com/ticket/1450)
9847 var status = xhr.status === 1223 ? 204 : xhr.status;
9849 // fix status code when it is 0 (0 status is undocumented).
9850 // Occurs when accessing file resources or on Android 4.1 stock browser
9851 // while retrieving files from application cache.
9853 status = response ? 200 : urlResolve(url).protocol == 'file' ? 404 : 0;
9856 completeRequest(callback,
9859 xhr.getAllResponseHeaders(),
9863 var requestError = function() {
9864 // The response is always empty
9865 // See https://xhr.spec.whatwg.org/#request-error-steps and https://fetch.spec.whatwg.org/#concept-network-error
9866 completeRequest(callback, -1, null, null, '');
9869 xhr.onerror = requestError;
9870 xhr.onabort = requestError;
9872 if (withCredentials) {
9873 xhr.withCredentials = true;
9878 xhr.responseType = responseType;
9880 // WebKit added support for the json responseType value on 09/03/2013
9881 // https://bugs.webkit.org/show_bug.cgi?id=73648. Versions of Safari prior to 7 are
9882 // known to throw when setting the value "json" as the response type. Other older
9883 // browsers implementing the responseType
9885 // The json response type can be ignored if not supported, because JSON payloads are
9886 // parsed on the client-side regardless.
9887 if (responseType !== 'json') {
9893 xhr.send(post || null);
9897 var timeoutId = $browserDefer(timeoutRequest, timeout);
9898 } else if (isPromiseLike(timeout)) {
9899 timeout.then(timeoutRequest);
9903 function timeoutRequest() {
9904 jsonpDone && jsonpDone();
9908 function completeRequest(callback, status, response, headersString, statusText) {
9909 // cancel timeout and subsequent timeout promise resolution
9910 if (timeoutId !== undefined) {
9911 $browserDefer.cancel(timeoutId);
9913 jsonpDone = xhr = null;
9915 callback(status, response, headersString, statusText);
9916 $browser.$$completeOutstandingRequest(noop);
9920 function jsonpReq(url, callbackId, done) {
9921 // we can't use jQuery/jqLite here because jQuery does crazy stuff with script elements, e.g.:
9922 // - fetches local scripts via XHR and evals them
9923 // - adds and immediately removes script elements from the document
9924 var script = rawDocument.createElement('script'), callback = null;
9925 script.type = "text/javascript";
9927 script.async = true;
9929 callback = function(event) {
9930 removeEventListenerFn(script, "load", callback);
9931 removeEventListenerFn(script, "error", callback);
9932 rawDocument.body.removeChild(script);
9935 var text = "unknown";
9938 if (event.type === "load" && !callbacks[callbackId].called) {
9939 event = { type: "error" };
9942 status = event.type === "error" ? 404 : 200;
9950 addEventListenerFn(script, "load", callback);
9951 addEventListenerFn(script, "error", callback);
9952 rawDocument.body.appendChild(script);
9957 var $interpolateMinErr = minErr('$interpolate');
9961 * @name $interpolateProvider
9965 * Used for configuring the interpolation markup. Defaults to `{{` and `}}`.
9968 <example module="customInterpolationApp">
9969 <file name="index.html">
9971 var customInterpolationApp = angular.module('customInterpolationApp', []);
9973 customInterpolationApp.config(function($interpolateProvider) {
9974 $interpolateProvider.startSymbol('//');
9975 $interpolateProvider.endSymbol('//');
9979 customInterpolationApp.controller('DemoController', function() {
9980 this.label = "This binding is brought you by // interpolation symbols.";
9983 <div ng-app="App" ng-controller="DemoController as demo">
9987 <file name="protractor.js" type="protractor">
9988 it('should interpolate binding with custom symbols', function() {
9989 expect(element(by.binding('demo.label')).getText()).toBe('This binding is brought you by // interpolation symbols.');
9994 function $InterpolateProvider() {
9995 var startSymbol = '{{';
9996 var endSymbol = '}}';
10000 * @name $interpolateProvider#startSymbol
10002 * Symbol to denote start of expression in the interpolated string. Defaults to `{{`.
10004 * @param {string=} value new value to set the starting symbol to.
10005 * @returns {string|self} Returns the symbol when used as getter and self if used as setter.
10007 this.startSymbol = function(value) {
10009 startSymbol = value;
10012 return startSymbol;
10018 * @name $interpolateProvider#endSymbol
10020 * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
10022 * @param {string=} value new value to set the ending symbol to.
10023 * @returns {string|self} Returns the symbol when used as getter and self if used as setter.
10025 this.endSymbol = function(value) {
10035 this.$get = ['$parse', '$exceptionHandler', '$sce', function($parse, $exceptionHandler, $sce) {
10036 var startSymbolLength = startSymbol.length,
10037 endSymbolLength = endSymbol.length,
10038 escapedStartRegexp = new RegExp(startSymbol.replace(/./g, escape), 'g'),
10039 escapedEndRegexp = new RegExp(endSymbol.replace(/./g, escape), 'g');
10041 function escape(ch) {
10042 return '\\\\\\' + ch;
10047 * @name $interpolate
10055 * Compiles a string with markup into an interpolation function. This service is used by the
10056 * HTML {@link ng.$compile $compile} service for data binding. See
10057 * {@link ng.$interpolateProvider $interpolateProvider} for configuring the
10058 * interpolation markup.
10062 * var $interpolate = ...; // injected
10063 * var exp = $interpolate('Hello {{name | uppercase}}!');
10064 * expect(exp({name:'Angular'}).toEqual('Hello ANGULAR!');
10067 * `$interpolate` takes an optional fourth argument, `allOrNothing`. If `allOrNothing` is
10068 * `true`, the interpolation function will return `undefined` unless all embedded expressions
10069 * evaluate to a value other than `undefined`.
10072 * var $interpolate = ...; // injected
10073 * var context = {greeting: 'Hello', name: undefined };
10075 * // default "forgiving" mode
10076 * var exp = $interpolate('{{greeting}} {{name}}!');
10077 * expect(exp(context)).toEqual('Hello !');
10079 * // "allOrNothing" mode
10080 * exp = $interpolate('{{greeting}} {{name}}!', false, null, true);
10081 * expect(exp(context)).toBeUndefined();
10082 * context.name = 'Angular';
10083 * expect(exp(context)).toEqual('Hello Angular!');
10086 * `allOrNothing` is useful for interpolating URLs. `ngSrc` and `ngSrcset` use this behavior.
10088 * ####Escaped Interpolation
10089 * $interpolate provides a mechanism for escaping interpolation markers. Start and end markers
10090 * can be escaped by preceding each of their characters with a REVERSE SOLIDUS U+005C (backslash).
10091 * It will be rendered as a regular start/end marker, and will not be interpreted as an expression
10094 * This enables web-servers to prevent script injection attacks and defacing attacks, to some
10095 * degree, while also enabling code examples to work without relying on the
10096 * {@link ng.directive:ngNonBindable ngNonBindable} directive.
10098 * **For security purposes, it is strongly encouraged that web servers escape user-supplied data,
10099 * replacing angle brackets (<, >) with &lt; and &gt; respectively, and replacing all
10100 * interpolation start/end markers with their escaped counterparts.**
10102 * Escaped interpolation markers are only replaced with the actual interpolation markers in rendered
10103 * output when the $interpolate service processes the text. So, for HTML elements interpolated
10104 * by {@link ng.$compile $compile}, or otherwise interpolated with the `mustHaveExpression` parameter
10105 * set to `true`, the interpolated text must contain an unescaped interpolation expression. As such,
10106 * this is typically useful only when user-data is used in rendering a template from the server, or
10107 * when otherwise untrusted data is used by a directive.
10110 * <file name="index.html">
10111 * <div ng-init="username='A user'">
10112 * <p ng-init="apptitle='Escaping demo'">{{apptitle}}: \{\{ username = "defaced value"; \}\}
10114 * <p><strong>{{username}}</strong> attempts to inject code which will deface the
10115 * application, but fails to accomplish their task, because the server has correctly
10116 * escaped the interpolation start/end markers with REVERSE SOLIDUS U+005C (backslash)
10118 * <p>Instead, the result of the attempted script injection is visible, and can be removed
10119 * from the database by an administrator.</p>
10124 * @param {string} text The text with markup to interpolate.
10125 * @param {boolean=} mustHaveExpression if set to true then the interpolation string must have
10126 * embedded expression in order to return an interpolation function. Strings with no
10127 * embedded expression will return null for the interpolation function.
10128 * @param {string=} trustedContext when provided, the returned function passes the interpolated
10129 * result through {@link ng.$sce#getTrusted $sce.getTrusted(interpolatedResult,
10130 * trustedContext)} before returning it. Refer to the {@link ng.$sce $sce} service that
10131 * provides Strict Contextual Escaping for details.
10132 * @param {boolean=} allOrNothing if `true`, then the returned function returns undefined
10133 * unless all embedded expressions evaluate to a value other than `undefined`.
10134 * @returns {function(context)} an interpolation function which is used to compute the
10135 * interpolated string. The function has these parameters:
10137 * - `context`: evaluation context for all expressions embedded in the interpolated text
10139 function $interpolate(text, mustHaveExpression, trustedContext, allOrNothing) {
10140 allOrNothing = !!allOrNothing;
10146 textLength = text.length,
10149 expressionPositions = [];
10151 while (index < textLength) {
10152 if (((startIndex = text.indexOf(startSymbol, index)) != -1) &&
10153 ((endIndex = text.indexOf(endSymbol, startIndex + startSymbolLength)) != -1)) {
10154 if (index !== startIndex) {
10155 concat.push(unescapeText(text.substring(index, startIndex)));
10157 exp = text.substring(startIndex + startSymbolLength, endIndex);
10158 expressions.push(exp);
10159 parseFns.push($parse(exp, parseStringifyInterceptor));
10160 index = endIndex + endSymbolLength;
10161 expressionPositions.push(concat.length);
10164 // we did not find an interpolation, so we have to add the remainder to the separators array
10165 if (index !== textLength) {
10166 concat.push(unescapeText(text.substring(index)));
10172 // Concatenating expressions makes it hard to reason about whether some combination of
10173 // concatenated values are unsafe to use and could easily lead to XSS. By requiring that a
10174 // single expression be used for iframe[src], object[src], etc., we ensure that the value
10175 // that's used is assigned or constructed by some JS code somewhere that is more testable or
10176 // make it obvious that you bound the value to some user controlled value. This helps reduce
10177 // the load when auditing for XSS issues.
10178 if (trustedContext && concat.length > 1) {
10179 throw $interpolateMinErr('noconcat',
10180 "Error while interpolating: {0}\nStrict Contextual Escaping disallows " +
10181 "interpolations that concatenate multiple expressions when a trusted value is " +
10182 "required. See http://docs.angularjs.org/api/ng.$sce", text);
10185 if (!mustHaveExpression || expressions.length) {
10186 var compute = function(values) {
10187 for (var i = 0, ii = expressions.length; i < ii; i++) {
10188 if (allOrNothing && isUndefined(values[i])) return;
10189 concat[expressionPositions[i]] = values[i];
10191 return concat.join('');
10194 var getValue = function(value) {
10195 return trustedContext ?
10196 $sce.getTrusted(trustedContext, value) :
10197 $sce.valueOf(value);
10200 var stringify = function(value) {
10201 if (value == null) { // null || undefined
10204 switch (typeof value) {
10208 value = '' + value;
10211 value = toJson(value);
10217 return extend(function interpolationFn(context) {
10219 var ii = expressions.length;
10220 var values = new Array(ii);
10223 for (; i < ii; i++) {
10224 values[i] = parseFns[i](context);
10227 return compute(values);
10229 var newErr = $interpolateMinErr('interr', "Can't interpolate: {0}\n{1}", text,
10231 $exceptionHandler(newErr);
10235 // all of these properties are undocumented for now
10236 exp: text, //just for compatibility with regular watchers created via $watch
10237 expressions: expressions,
10238 $$watchDelegate: function(scope, listener, objectEquality) {
10240 return scope.$watchGroup(parseFns, function interpolateFnWatcher(values, oldValues) {
10241 var currValue = compute(values);
10242 if (isFunction(listener)) {
10243 listener.call(this, currValue, values !== oldValues ? lastValue : currValue, scope);
10245 lastValue = currValue;
10246 }, objectEquality);
10251 function unescapeText(text) {
10252 return text.replace(escapedStartRegexp, startSymbol).
10253 replace(escapedEndRegexp, endSymbol);
10256 function parseStringifyInterceptor(value) {
10258 value = getValue(value);
10259 return allOrNothing && !isDefined(value) ? value : stringify(value);
10261 var newErr = $interpolateMinErr('interr', "Can't interpolate: {0}\n{1}", text,
10263 $exceptionHandler(newErr);
10271 * @name $interpolate#startSymbol
10273 * Symbol to denote the start of expression in the interpolated string. Defaults to `{{`.
10275 * Use {@link ng.$interpolateProvider#startSymbol `$interpolateProvider.startSymbol`} to change
10278 * @returns {string} start symbol.
10280 $interpolate.startSymbol = function() {
10281 return startSymbol;
10287 * @name $interpolate#endSymbol
10289 * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
10291 * Use {@link ng.$interpolateProvider#endSymbol `$interpolateProvider.endSymbol`} to change
10294 * @returns {string} end symbol.
10296 $interpolate.endSymbol = function() {
10300 return $interpolate;
10304 function $IntervalProvider() {
10305 this.$get = ['$rootScope', '$window', '$q', '$$q',
10306 function($rootScope, $window, $q, $$q) {
10307 var intervals = {};
10315 * Angular's wrapper for `window.setInterval`. The `fn` function is executed every `delay`
10318 * The return value of registering an interval function is a promise. This promise will be
10319 * notified upon each tick of the interval, and will be resolved after `count` iterations, or
10320 * run indefinitely if `count` is not defined. The value of the notification will be the
10321 * number of iterations that have run.
10322 * To cancel an interval, call `$interval.cancel(promise)`.
10324 * In tests you can use {@link ngMock.$interval#flush `$interval.flush(millis)`} to
10325 * move forward by `millis` milliseconds and trigger any functions scheduled to run in that
10328 * <div class="alert alert-warning">
10329 * **Note**: Intervals created by this service must be explicitly destroyed when you are finished
10330 * with them. In particular they are not automatically destroyed when a controller's scope or a
10331 * directive's element are destroyed.
10332 * You should take this into consideration and make sure to always cancel the interval at the
10333 * appropriate moment. See the example below for more details on how and when to do this.
10336 * @param {function()} fn A function that should be called repeatedly.
10337 * @param {number} delay Number of milliseconds between each function call.
10338 * @param {number=} [count=0] Number of times to repeat. If not set, or 0, will repeat
10340 * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
10341 * will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
10342 * @returns {promise} A promise which will be notified on each iteration.
10345 * <example module="intervalExample">
10346 * <file name="index.html">
10348 * angular.module('intervalExample', [])
10349 * .controller('ExampleController', ['$scope', '$interval',
10350 * function($scope, $interval) {
10351 * $scope.format = 'M/d/yy h:mm:ss a';
10352 * $scope.blood_1 = 100;
10353 * $scope.blood_2 = 120;
10356 * $scope.fight = function() {
10357 * // Don't start a new fight if we are already fighting
10358 * if ( angular.isDefined(stop) ) return;
10360 * stop = $interval(function() {
10361 * if ($scope.blood_1 > 0 && $scope.blood_2 > 0) {
10362 * $scope.blood_1 = $scope.blood_1 - 3;
10363 * $scope.blood_2 = $scope.blood_2 - 4;
10365 * $scope.stopFight();
10370 * $scope.stopFight = function() {
10371 * if (angular.isDefined(stop)) {
10372 * $interval.cancel(stop);
10373 * stop = undefined;
10377 * $scope.resetFight = function() {
10378 * $scope.blood_1 = 100;
10379 * $scope.blood_2 = 120;
10382 * $scope.$on('$destroy', function() {
10383 * // Make sure that the interval is destroyed too
10384 * $scope.stopFight();
10387 * // Register the 'myCurrentTime' directive factory method.
10388 * // We inject $interval and dateFilter service since the factory method is DI.
10389 * .directive('myCurrentTime', ['$interval', 'dateFilter',
10390 * function($interval, dateFilter) {
10391 * // return the directive link function. (compile function not needed)
10392 * return function(scope, element, attrs) {
10393 * var format, // date format
10394 * stopTime; // so that we can cancel the time updates
10396 * // used to update the UI
10397 * function updateTime() {
10398 * element.text(dateFilter(new Date(), format));
10401 * // watch the expression, and update the UI on change.
10402 * scope.$watch(attrs.myCurrentTime, function(value) {
10407 * stopTime = $interval(updateTime, 1000);
10409 * // listen on DOM destroy (removal) event, and cancel the next UI update
10410 * // to prevent updating time after the DOM element was removed.
10411 * element.on('$destroy', function() {
10412 * $interval.cancel(stopTime);
10419 * <div ng-controller="ExampleController">
10420 * Date format: <input ng-model="format"> <hr/>
10421 * Current time is: <span my-current-time="format"></span>
10423 * Blood 1 : <font color='red'>{{blood_1}}</font>
10424 * Blood 2 : <font color='red'>{{blood_2}}</font>
10425 * <button type="button" data-ng-click="fight()">Fight</button>
10426 * <button type="button" data-ng-click="stopFight()">StopFight</button>
10427 * <button type="button" data-ng-click="resetFight()">resetFight</button>
10434 function interval(fn, delay, count, invokeApply) {
10435 var setInterval = $window.setInterval,
10436 clearInterval = $window.clearInterval,
10438 skipApply = (isDefined(invokeApply) && !invokeApply),
10439 deferred = (skipApply ? $$q : $q).defer(),
10440 promise = deferred.promise;
10442 count = isDefined(count) ? count : 0;
10444 promise.then(null, null, fn);
10446 promise.$$intervalId = setInterval(function tick() {
10447 deferred.notify(iteration++);
10449 if (count > 0 && iteration >= count) {
10450 deferred.resolve(iteration);
10451 clearInterval(promise.$$intervalId);
10452 delete intervals[promise.$$intervalId];
10455 if (!skipApply) $rootScope.$apply();
10459 intervals[promise.$$intervalId] = deferred;
10467 * @name $interval#cancel
10470 * Cancels a task associated with the `promise`.
10472 * @param {promise} promise returned by the `$interval` function.
10473 * @returns {boolean} Returns `true` if the task was successfully canceled.
10475 interval.cancel = function(promise) {
10476 if (promise && promise.$$intervalId in intervals) {
10477 intervals[promise.$$intervalId].reject('canceled');
10478 $window.clearInterval(promise.$$intervalId);
10479 delete intervals[promise.$$intervalId];
10494 * $locale service provides localization rules for various Angular components. As of right now the
10495 * only public api is:
10497 * * `id` – `{string}` – locale id formatted as `languageId-countryId` (e.g. `en-us`)
10499 function $LocaleProvider() {
10500 this.$get = function() {
10508 { // Decimal Pattern
10518 },{ //Currency Pattern
10533 DATETIME_FORMATS: {
10535 'January,February,March,April,May,June,July,August,September,October,November,December'
10537 SHORTMONTH: 'Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec'.split(','),
10538 DAY: 'Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday'.split(','),
10539 SHORTDAY: 'Sun,Mon,Tue,Wed,Thu,Fri,Sat'.split(','),
10540 AMPMS: ['AM','PM'],
10541 medium: 'MMM d, y h:mm:ss a',
10542 'short': 'M/d/yy h:mm a',
10543 fullDate: 'EEEE, MMMM d, y',
10544 longDate: 'MMMM d, y',
10545 mediumDate: 'MMM d, y',
10546 shortDate: 'M/d/yy',
10547 mediumTime: 'h:mm:ss a',
10548 shortTime: 'h:mm a',
10559 pluralCat: function(num) {
10569 var PATH_MATCH = /^([^\?#]*)(\?([^#]*))?(#(.*))?$/,
10570 DEFAULT_PORTS = {'http': 80, 'https': 443, 'ftp': 21};
10571 var $locationMinErr = minErr('$location');
10575 * Encode path using encodeUriSegment, ignoring forward slashes
10577 * @param {string} path Path to encode
10578 * @returns {string}
10580 function encodePath(path) {
10581 var segments = path.split('/'),
10582 i = segments.length;
10585 segments[i] = encodeUriSegment(segments[i]);
10588 return segments.join('/');
10591 function parseAbsoluteUrl(absoluteUrl, locationObj) {
10592 var parsedUrl = urlResolve(absoluteUrl);
10594 locationObj.$$protocol = parsedUrl.protocol;
10595 locationObj.$$host = parsedUrl.hostname;
10596 locationObj.$$port = int(parsedUrl.port) || DEFAULT_PORTS[parsedUrl.protocol] || null;
10600 function parseAppUrl(relativeUrl, locationObj) {
10601 var prefixed = (relativeUrl.charAt(0) !== '/');
10603 relativeUrl = '/' + relativeUrl;
10605 var match = urlResolve(relativeUrl);
10606 locationObj.$$path = decodeURIComponent(prefixed && match.pathname.charAt(0) === '/' ?
10607 match.pathname.substring(1) : match.pathname);
10608 locationObj.$$search = parseKeyValue(match.search);
10609 locationObj.$$hash = decodeURIComponent(match.hash);
10611 // make sure path starts with '/';
10612 if (locationObj.$$path && locationObj.$$path.charAt(0) != '/') {
10613 locationObj.$$path = '/' + locationObj.$$path;
10620 * @param {string} begin
10621 * @param {string} whole
10622 * @returns {string} returns text from whole after begin or undefined if it does not begin with
10625 function beginsWith(begin, whole) {
10626 if (whole.indexOf(begin) === 0) {
10627 return whole.substr(begin.length);
10632 function stripHash(url) {
10633 var index = url.indexOf('#');
10634 return index == -1 ? url : url.substr(0, index);
10637 function trimEmptyHash(url) {
10638 return url.replace(/(#.+)|#$/, '$1');
10642 function stripFile(url) {
10643 return url.substr(0, stripHash(url).lastIndexOf('/') + 1);
10646 /* return the server only (scheme://host:port) */
10647 function serverBase(url) {
10648 return url.substring(0, url.indexOf('/', url.indexOf('//') + 2));
10653 * LocationHtml5Url represents an url
10654 * This object is exposed as $location service when HTML5 mode is enabled and supported
10657 * @param {string} appBase application base URL
10658 * @param {string} appBaseNoFile application base URL stripped of any filename
10659 * @param {string} basePrefix url path prefix
10661 function LocationHtml5Url(appBase, appBaseNoFile, basePrefix) {
10662 this.$$html5 = true;
10663 basePrefix = basePrefix || '';
10664 parseAbsoluteUrl(appBase, this);
10668 * Parse given html5 (regular) url string into properties
10669 * @param {string} url HTML5 url
10672 this.$$parse = function(url) {
10673 var pathUrl = beginsWith(appBaseNoFile, url);
10674 if (!isString(pathUrl)) {
10675 throw $locationMinErr('ipthprfx', 'Invalid url "{0}", missing path prefix "{1}".', url,
10679 parseAppUrl(pathUrl, this);
10681 if (!this.$$path) {
10689 * Compose url and update `absUrl` property
10692 this.$$compose = function() {
10693 var search = toKeyValue(this.$$search),
10694 hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
10696 this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
10697 this.$$absUrl = appBaseNoFile + this.$$url.substr(1); // first char is always '/'
10700 this.$$parseLinkUrl = function(url, relHref) {
10701 if (relHref && relHref[0] === '#') {
10702 // special case for links to hash fragments:
10703 // keep the old url and only replace the hash fragment
10704 this.hash(relHref.slice(1));
10707 var appUrl, prevAppUrl;
10710 if ((appUrl = beginsWith(appBase, url)) !== undefined) {
10711 prevAppUrl = appUrl;
10712 if ((appUrl = beginsWith(basePrefix, appUrl)) !== undefined) {
10713 rewrittenUrl = appBaseNoFile + (beginsWith('/', appUrl) || appUrl);
10715 rewrittenUrl = appBase + prevAppUrl;
10717 } else if ((appUrl = beginsWith(appBaseNoFile, url)) !== undefined) {
10718 rewrittenUrl = appBaseNoFile + appUrl;
10719 } else if (appBaseNoFile == url + '/') {
10720 rewrittenUrl = appBaseNoFile;
10722 if (rewrittenUrl) {
10723 this.$$parse(rewrittenUrl);
10725 return !!rewrittenUrl;
10731 * LocationHashbangUrl represents url
10732 * This object is exposed as $location service when developer doesn't opt into html5 mode.
10733 * It also serves as the base class for html5 mode fallback on legacy browsers.
10736 * @param {string} appBase application base URL
10737 * @param {string} appBaseNoFile application base URL stripped of any filename
10738 * @param {string} hashPrefix hashbang prefix
10740 function LocationHashbangUrl(appBase, appBaseNoFile, hashPrefix) {
10742 parseAbsoluteUrl(appBase, this);
10746 * Parse given hashbang url into properties
10747 * @param {string} url Hashbang url
10750 this.$$parse = function(url) {
10751 var withoutBaseUrl = beginsWith(appBase, url) || beginsWith(appBaseNoFile, url);
10752 var withoutHashUrl;
10754 if (!isUndefined(withoutBaseUrl) && withoutBaseUrl.charAt(0) === '#') {
10756 // The rest of the url starts with a hash so we have
10757 // got either a hashbang path or a plain hash fragment
10758 withoutHashUrl = beginsWith(hashPrefix, withoutBaseUrl);
10759 if (isUndefined(withoutHashUrl)) {
10760 // There was no hashbang prefix so we just have a hash fragment
10761 withoutHashUrl = withoutBaseUrl;
10765 // There was no hashbang path nor hash fragment:
10766 // If we are in HTML5 mode we use what is left as the path;
10767 // Otherwise we ignore what is left
10768 if (this.$$html5) {
10769 withoutHashUrl = withoutBaseUrl;
10771 withoutHashUrl = '';
10772 if (isUndefined(withoutBaseUrl)) {
10779 parseAppUrl(withoutHashUrl, this);
10781 this.$$path = removeWindowsDriveName(this.$$path, withoutHashUrl, appBase);
10786 * In Windows, on an anchor node on documents loaded from
10787 * the filesystem, the browser will return a pathname
10788 * prefixed with the drive name ('/C:/path') when a
10789 * pathname without a drive is set:
10790 * * a.setAttribute('href', '/foo')
10791 * * a.pathname === '/C:/foo' //true
10793 * Inside of Angular, we're always using pathnames that
10794 * do not include drive names for routing.
10796 function removeWindowsDriveName(path, url, base) {
10798 Matches paths for file protocol on windows,
10799 such as /C:/foo/bar, and captures only /foo/bar.
10801 var windowsFilePathExp = /^\/[A-Z]:(\/.*)/;
10803 var firstPathSegmentMatch;
10805 //Get the relative path from the input URL.
10806 if (url.indexOf(base) === 0) {
10807 url = url.replace(base, '');
10810 // The input URL intentionally contains a first path segment that ends with a colon.
10811 if (windowsFilePathExp.exec(url)) {
10815 firstPathSegmentMatch = windowsFilePathExp.exec(path);
10816 return firstPathSegmentMatch ? firstPathSegmentMatch[1] : path;
10821 * Compose hashbang url and update `absUrl` property
10824 this.$$compose = function() {
10825 var search = toKeyValue(this.$$search),
10826 hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
10828 this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
10829 this.$$absUrl = appBase + (this.$$url ? hashPrefix + this.$$url : '');
10832 this.$$parseLinkUrl = function(url, relHref) {
10833 if (stripHash(appBase) == stripHash(url)) {
10843 * LocationHashbangUrl represents url
10844 * This object is exposed as $location service when html5 history api is enabled but the browser
10845 * does not support it.
10848 * @param {string} appBase application base URL
10849 * @param {string} appBaseNoFile application base URL stripped of any filename
10850 * @param {string} hashPrefix hashbang prefix
10852 function LocationHashbangInHtml5Url(appBase, appBaseNoFile, hashPrefix) {
10853 this.$$html5 = true;
10854 LocationHashbangUrl.apply(this, arguments);
10856 this.$$parseLinkUrl = function(url, relHref) {
10857 if (relHref && relHref[0] === '#') {
10858 // special case for links to hash fragments:
10859 // keep the old url and only replace the hash fragment
10860 this.hash(relHref.slice(1));
10867 if (appBase == stripHash(url)) {
10868 rewrittenUrl = url;
10869 } else if ((appUrl = beginsWith(appBaseNoFile, url))) {
10870 rewrittenUrl = appBase + hashPrefix + appUrl;
10871 } else if (appBaseNoFile === url + '/') {
10872 rewrittenUrl = appBaseNoFile;
10874 if (rewrittenUrl) {
10875 this.$$parse(rewrittenUrl);
10877 return !!rewrittenUrl;
10880 this.$$compose = function() {
10881 var search = toKeyValue(this.$$search),
10882 hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
10884 this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
10885 // include hashPrefix in $$absUrl when $$url is empty so IE9 does not reload page because of removal of '#'
10886 this.$$absUrl = appBase + hashPrefix + this.$$url;
10892 var locationPrototype = {
10895 * Are we in html5 mode?
10901 * Has any change been replacing?
10908 * @name $location#absUrl
10911 * This method is getter only.
10913 * Return full url representation with all segments encoded according to rules specified in
10914 * [RFC 3986](http://www.ietf.org/rfc/rfc3986.txt).
10918 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
10919 * var absUrl = $location.absUrl();
10920 * // => "http://example.com/#/some/path?foo=bar&baz=xoxo"
10923 * @return {string} full url
10925 absUrl: locationGetter('$$absUrl'),
10929 * @name $location#url
10932 * This method is getter / setter.
10934 * Return url (e.g. `/path?a=b#hash`) when called without any parameter.
10936 * Change path, search and hash, when called with parameter and return `$location`.
10940 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
10941 * var url = $location.url();
10942 * // => "/some/path?foo=bar&baz=xoxo"
10945 * @param {string=} url New url without base prefix (e.g. `/path?a=b#hash`)
10946 * @return {string} url
10948 url: function(url) {
10949 if (isUndefined(url))
10952 var match = PATH_MATCH.exec(url);
10953 if (match[1] || url === '') this.path(decodeURIComponent(match[1]));
10954 if (match[2] || match[1] || url === '') this.search(match[3] || '');
10955 this.hash(match[5] || '');
10962 * @name $location#protocol
10965 * This method is getter only.
10967 * Return protocol of current url.
10971 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
10972 * var protocol = $location.protocol();
10976 * @return {string} protocol of current url
10978 protocol: locationGetter('$$protocol'),
10982 * @name $location#host
10985 * This method is getter only.
10987 * Return host of current url.
10989 * Note: compared to the non-angular version `location.host` which returns `hostname:port`, this returns the `hostname` portion only.
10993 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
10994 * var host = $location.host();
10995 * // => "example.com"
10997 * // given url http://user:password@example.com:8080/#/some/path?foo=bar&baz=xoxo
10998 * host = $location.host();
10999 * // => "example.com"
11000 * host = location.host;
11001 * // => "example.com:8080"
11004 * @return {string} host of current url.
11006 host: locationGetter('$$host'),
11010 * @name $location#port
11013 * This method is getter only.
11015 * Return port of current url.
11019 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
11020 * var port = $location.port();
11024 * @return {Number} port
11026 port: locationGetter('$$port'),
11030 * @name $location#path
11033 * This method is getter / setter.
11035 * Return path of current url when called without any parameter.
11037 * Change path when called with parameter and return `$location`.
11039 * Note: Path should always begin with forward slash (/), this method will add the forward slash
11040 * if it is missing.
11044 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
11045 * var path = $location.path();
11046 * // => "/some/path"
11049 * @param {(string|number)=} path New path
11050 * @return {string} path
11052 path: locationGetterSetter('$$path', function(path) {
11053 path = path !== null ? path.toString() : '';
11054 return path.charAt(0) == '/' ? path : '/' + path;
11059 * @name $location#search
11062 * This method is getter / setter.
11064 * Return search part (as object) of current url when called without any parameter.
11066 * Change search part when called with parameter and return `$location`.
11070 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
11071 * var searchObject = $location.search();
11072 * // => {foo: 'bar', baz: 'xoxo'}
11074 * // set foo to 'yipee'
11075 * $location.search('foo', 'yipee');
11076 * // $location.search() => {foo: 'yipee', baz: 'xoxo'}
11079 * @param {string|Object.<string>|Object.<Array.<string>>} search New search params - string or
11082 * When called with a single argument the method acts as a setter, setting the `search` component
11083 * of `$location` to the specified value.
11085 * If the argument is a hash object containing an array of values, these values will be encoded
11086 * as duplicate search parameters in the url.
11088 * @param {(string|Number|Array<string>|boolean)=} paramValue If `search` is a string or number, then `paramValue`
11089 * will override only a single search property.
11091 * If `paramValue` is an array, it will override the property of the `search` component of
11092 * `$location` specified via the first argument.
11094 * If `paramValue` is `null`, the property specified via the first argument will be deleted.
11096 * If `paramValue` is `true`, the property specified via the first argument will be added with no
11097 * value nor trailing equal sign.
11099 * @return {Object} If called with no arguments returns the parsed `search` object. If called with
11100 * one or more arguments returns `$location` object itself.
11102 search: function(search, paramValue) {
11103 switch (arguments.length) {
11105 return this.$$search;
11107 if (isString(search) || isNumber(search)) {
11108 search = search.toString();
11109 this.$$search = parseKeyValue(search);
11110 } else if (isObject(search)) {
11111 search = copy(search, {});
11112 // remove object undefined or null properties
11113 forEach(search, function(value, key) {
11114 if (value == null) delete search[key];
11117 this.$$search = search;
11119 throw $locationMinErr('isrcharg',
11120 'The first argument of the `$location#search()` call must be a string or an object.');
11124 if (isUndefined(paramValue) || paramValue === null) {
11125 delete this.$$search[search];
11127 this.$$search[search] = paramValue;
11137 * @name $location#hash
11140 * This method is getter / setter.
11142 * Return hash fragment when called without any parameter.
11144 * Change hash fragment when called with parameter and return `$location`.
11148 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo#hashValue
11149 * var hash = $location.hash();
11150 * // => "hashValue"
11153 * @param {(string|number)=} hash New hash fragment
11154 * @return {string} hash
11156 hash: locationGetterSetter('$$hash', function(hash) {
11157 return hash !== null ? hash.toString() : '';
11162 * @name $location#replace
11165 * If called, all changes to $location during current `$digest` will be replacing current history
11166 * record, instead of adding new one.
11168 replace: function() {
11169 this.$$replace = true;
11174 forEach([LocationHashbangInHtml5Url, LocationHashbangUrl, LocationHtml5Url], function(Location) {
11175 Location.prototype = Object.create(locationPrototype);
11179 * @name $location#state
11182 * This method is getter / setter.
11184 * Return the history state object when called without any parameter.
11186 * Change the history state object when called with one parameter and return `$location`.
11187 * The state object is later passed to `pushState` or `replaceState`.
11189 * NOTE: This method is supported only in HTML5 mode and only in browsers supporting
11190 * the HTML5 History API (i.e. methods `pushState` and `replaceState`). If you need to support
11191 * older browsers (like IE9 or Android < 4.0), don't use this method.
11193 * @param {object=} state State object for pushState or replaceState
11194 * @return {object} state
11196 Location.prototype.state = function(state) {
11197 if (!arguments.length)
11198 return this.$$state;
11200 if (Location !== LocationHtml5Url || !this.$$html5) {
11201 throw $locationMinErr('nostate', 'History API state support is available only ' +
11202 'in HTML5 mode and only in browsers supporting HTML5 History API');
11204 // The user might modify `stateObject` after invoking `$location.state(stateObject)`
11205 // but we're changing the $$state reference to $browser.state() during the $digest
11206 // so the modification window is narrow.
11207 this.$$state = isUndefined(state) ? null : state;
11214 function locationGetter(property) {
11215 return function() {
11216 return this[property];
11221 function locationGetterSetter(property, preprocess) {
11222 return function(value) {
11223 if (isUndefined(value))
11224 return this[property];
11226 this[property] = preprocess(value);
11238 * @requires $rootElement
11241 * The $location service parses the URL in the browser address bar (based on the
11242 * [window.location](https://developer.mozilla.org/en/window.location)) and makes the URL
11243 * available to your application. Changes to the URL in the address bar are reflected into
11244 * $location service and changes to $location are reflected into the browser address bar.
11246 * **The $location service:**
11248 * - Exposes the current URL in the browser address bar, so you can
11249 * - Watch and observe the URL.
11250 * - Change the URL.
11251 * - Synchronizes the URL with the browser when the user
11252 * - Changes the address bar.
11253 * - Clicks the back or forward button (or clicks a History link).
11254 * - Clicks on a link.
11255 * - Represents the URL object as a set of methods (protocol, host, port, path, search, hash).
11257 * For more information see {@link guide/$location Developer Guide: Using $location}
11262 * @name $locationProvider
11264 * Use the `$locationProvider` to configure how the application deep linking paths are stored.
11266 function $LocationProvider() {
11267 var hashPrefix = '',
11276 * @name $locationProvider#hashPrefix
11278 * @param {string=} prefix Prefix for hash part (containing path and search)
11279 * @returns {*} current value if used as getter or itself (chaining) if used as setter
11281 this.hashPrefix = function(prefix) {
11282 if (isDefined(prefix)) {
11283 hashPrefix = prefix;
11292 * @name $locationProvider#html5Mode
11294 * @param {(boolean|Object)=} mode If boolean, sets `html5Mode.enabled` to value.
11295 * If object, sets `enabled`, `requireBase` and `rewriteLinks` to respective values. Supported
11297 * - **enabled** – `{boolean}` – (default: false) If true, will rely on `history.pushState` to
11298 * change urls where supported. Will fall back to hash-prefixed paths in browsers that do not
11299 * support `pushState`.
11300 * - **requireBase** - `{boolean}` - (default: `true`) When html5Mode is enabled, specifies
11301 * whether or not a <base> tag is required to be present. If `enabled` and `requireBase` are
11302 * true, and a base tag is not present, an error will be thrown when `$location` is injected.
11303 * See the {@link guide/$location $location guide for more information}
11304 * - **rewriteLinks** - `{boolean}` - (default: `true`) When html5Mode is enabled,
11305 * enables/disables url rewriting for relative links.
11307 * @returns {Object} html5Mode object if used as getter or itself (chaining) if used as setter
11309 this.html5Mode = function(mode) {
11310 if (isBoolean(mode)) {
11311 html5Mode.enabled = mode;
11313 } else if (isObject(mode)) {
11315 if (isBoolean(mode.enabled)) {
11316 html5Mode.enabled = mode.enabled;
11319 if (isBoolean(mode.requireBase)) {
11320 html5Mode.requireBase = mode.requireBase;
11323 if (isBoolean(mode.rewriteLinks)) {
11324 html5Mode.rewriteLinks = mode.rewriteLinks;
11335 * @name $location#$locationChangeStart
11336 * @eventType broadcast on root scope
11338 * Broadcasted before a URL will change.
11340 * This change can be prevented by calling
11341 * `preventDefault` method of the event. See {@link ng.$rootScope.Scope#$on} for more
11342 * details about event object. Upon successful change
11343 * {@link ng.$location#$locationChangeSuccess $locationChangeSuccess} is fired.
11345 * The `newState` and `oldState` parameters may be defined only in HTML5 mode and when
11346 * the browser supports the HTML5 History API.
11348 * @param {Object} angularEvent Synthetic event object.
11349 * @param {string} newUrl New URL
11350 * @param {string=} oldUrl URL that was before it was changed.
11351 * @param {string=} newState New history state object
11352 * @param {string=} oldState History state object that was before it was changed.
11357 * @name $location#$locationChangeSuccess
11358 * @eventType broadcast on root scope
11360 * Broadcasted after a URL was changed.
11362 * The `newState` and `oldState` parameters may be defined only in HTML5 mode and when
11363 * the browser supports the HTML5 History API.
11365 * @param {Object} angularEvent Synthetic event object.
11366 * @param {string} newUrl New URL
11367 * @param {string=} oldUrl URL that was before it was changed.
11368 * @param {string=} newState New history state object
11369 * @param {string=} oldState History state object that was before it was changed.
11372 this.$get = ['$rootScope', '$browser', '$sniffer', '$rootElement', '$window',
11373 function($rootScope, $browser, $sniffer, $rootElement, $window) {
11376 baseHref = $browser.baseHref(), // if base[href] is undefined, it defaults to ''
11377 initialUrl = $browser.url(),
11380 if (html5Mode.enabled) {
11381 if (!baseHref && html5Mode.requireBase) {
11382 throw $locationMinErr('nobase',
11383 "$location in HTML5 mode requires a <base> tag to be present!");
11385 appBase = serverBase(initialUrl) + (baseHref || '/');
11386 LocationMode = $sniffer.history ? LocationHtml5Url : LocationHashbangInHtml5Url;
11388 appBase = stripHash(initialUrl);
11389 LocationMode = LocationHashbangUrl;
11391 var appBaseNoFile = stripFile(appBase);
11393 $location = new LocationMode(appBase, appBaseNoFile, '#' + hashPrefix);
11394 $location.$$parseLinkUrl(initialUrl, initialUrl);
11396 $location.$$state = $browser.state();
11398 var IGNORE_URI_REGEXP = /^\s*(javascript|mailto):/i;
11400 function setBrowserUrlWithFallback(url, replace, state) {
11401 var oldUrl = $location.url();
11402 var oldState = $location.$$state;
11404 $browser.url(url, replace, state);
11406 // Make sure $location.state() returns referentially identical (not just deeply equal)
11407 // state object; this makes possible quick checking if the state changed in the digest
11408 // loop. Checking deep equality would be too expensive.
11409 $location.$$state = $browser.state();
11411 // Restore old values if pushState fails
11412 $location.url(oldUrl);
11413 $location.$$state = oldState;
11419 $rootElement.on('click', function(event) {
11420 // TODO(vojta): rewrite link when opening in new tab/window (in legacy browser)
11421 // currently we open nice url link and redirect then
11423 if (!html5Mode.rewriteLinks || event.ctrlKey || event.metaKey || event.shiftKey || event.which == 2 || event.button == 2) return;
11425 var elm = jqLite(event.target);
11427 // traverse the DOM up to find first A tag
11428 while (nodeName_(elm[0]) !== 'a') {
11429 // ignore rewriting if no A tag (reached root element, or no parent - removed from document)
11430 if (elm[0] === $rootElement[0] || !(elm = elm.parent())[0]) return;
11433 var absHref = elm.prop('href');
11434 // get the actual href attribute - see
11435 // http://msdn.microsoft.com/en-us/library/ie/dd347148(v=vs.85).aspx
11436 var relHref = elm.attr('href') || elm.attr('xlink:href');
11438 if (isObject(absHref) && absHref.toString() === '[object SVGAnimatedString]') {
11439 // SVGAnimatedString.animVal should be identical to SVGAnimatedString.baseVal, unless during
11441 absHref = urlResolve(absHref.animVal).href;
11444 // Ignore when url is started with javascript: or mailto:
11445 if (IGNORE_URI_REGEXP.test(absHref)) return;
11447 if (absHref && !elm.attr('target') && !event.isDefaultPrevented()) {
11448 if ($location.$$parseLinkUrl(absHref, relHref)) {
11449 // We do a preventDefault for all urls that are part of the angular application,
11450 // in html5mode and also without, so that we are able to abort navigation without
11451 // getting double entries in the location history.
11452 event.preventDefault();
11453 // update location manually
11454 if ($location.absUrl() != $browser.url()) {
11455 $rootScope.$apply();
11456 // hack to work around FF6 bug 684208 when scenario runner clicks on links
11457 $window.angular['ff-684208-preventDefault'] = true;
11464 // rewrite hashbang url <> html5 url
11465 if (trimEmptyHash($location.absUrl()) != trimEmptyHash(initialUrl)) {
11466 $browser.url($location.absUrl(), true);
11469 var initializing = true;
11471 // update $location when $browser url changes
11472 $browser.onUrlChange(function(newUrl, newState) {
11474 if (isUndefined(beginsWith(appBaseNoFile, newUrl))) {
11475 // If we are navigating outside of the app then force a reload
11476 $window.location.href = newUrl;
11480 $rootScope.$evalAsync(function() {
11481 var oldUrl = $location.absUrl();
11482 var oldState = $location.$$state;
11483 var defaultPrevented;
11485 $location.$$parse(newUrl);
11486 $location.$$state = newState;
11488 defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
11489 newState, oldState).defaultPrevented;
11491 // if the location was changed by a `$locationChangeStart` handler then stop
11492 // processing this location change
11493 if ($location.absUrl() !== newUrl) return;
11495 if (defaultPrevented) {
11496 $location.$$parse(oldUrl);
11497 $location.$$state = oldState;
11498 setBrowserUrlWithFallback(oldUrl, false, oldState);
11500 initializing = false;
11501 afterLocationChange(oldUrl, oldState);
11504 if (!$rootScope.$$phase) $rootScope.$digest();
11508 $rootScope.$watch(function $locationWatch() {
11509 var oldUrl = trimEmptyHash($browser.url());
11510 var newUrl = trimEmptyHash($location.absUrl());
11511 var oldState = $browser.state();
11512 var currentReplace = $location.$$replace;
11513 var urlOrStateChanged = oldUrl !== newUrl ||
11514 ($location.$$html5 && $sniffer.history && oldState !== $location.$$state);
11516 if (initializing || urlOrStateChanged) {
11517 initializing = false;
11519 $rootScope.$evalAsync(function() {
11520 var newUrl = $location.absUrl();
11521 var defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
11522 $location.$$state, oldState).defaultPrevented;
11524 // if the location was changed by a `$locationChangeStart` handler then stop
11525 // processing this location change
11526 if ($location.absUrl() !== newUrl) return;
11528 if (defaultPrevented) {
11529 $location.$$parse(oldUrl);
11530 $location.$$state = oldState;
11532 if (urlOrStateChanged) {
11533 setBrowserUrlWithFallback(newUrl, currentReplace,
11534 oldState === $location.$$state ? null : $location.$$state);
11536 afterLocationChange(oldUrl, oldState);
11541 $location.$$replace = false;
11543 // we don't need to return anything because $evalAsync will make the digest loop dirty when
11544 // there is a change
11549 function afterLocationChange(oldUrl, oldState) {
11550 $rootScope.$broadcast('$locationChangeSuccess', $location.absUrl(), oldUrl,
11551 $location.$$state, oldState);
11559 * @requires $window
11562 * Simple service for logging. Default implementation safely writes the message
11563 * into the browser's console (if present).
11565 * The main purpose of this service is to simplify debugging and troubleshooting.
11567 * The default is to log `debug` messages. You can use
11568 * {@link ng.$logProvider ng.$logProvider#debugEnabled} to change this.
11571 <example module="logExample">
11572 <file name="script.js">
11573 angular.module('logExample', [])
11574 .controller('LogController', ['$scope', '$log', function($scope, $log) {
11575 $scope.$log = $log;
11576 $scope.message = 'Hello World!';
11579 <file name="index.html">
11580 <div ng-controller="LogController">
11581 <p>Reload this page with open console, enter text and hit the log button...</p>
11583 <input type="text" ng-model="message"/>
11584 <button ng-click="$log.log(message)">log</button>
11585 <button ng-click="$log.warn(message)">warn</button>
11586 <button ng-click="$log.info(message)">info</button>
11587 <button ng-click="$log.error(message)">error</button>
11588 <button ng-click="$log.debug(message)">debug</button>
11596 * @name $logProvider
11598 * Use the `$logProvider` to configure how the application logs messages
11600 function $LogProvider() {
11606 * @name $logProvider#debugEnabled
11608 * @param {boolean=} flag enable or disable debug level messages
11609 * @returns {*} current value if used as getter or itself (chaining) if used as setter
11611 this.debugEnabled = function(flag) {
11612 if (isDefined(flag)) {
11620 this.$get = ['$window', function($window) {
11627 * Write a log message
11629 log: consoleLog('log'),
11636 * Write an information message
11638 info: consoleLog('info'),
11645 * Write a warning message
11647 warn: consoleLog('warn'),
11654 * Write an error message
11656 error: consoleLog('error'),
11663 * Write a debug message
11665 debug: (function() {
11666 var fn = consoleLog('debug');
11668 return function() {
11670 fn.apply(self, arguments);
11676 function formatError(arg) {
11677 if (arg instanceof Error) {
11679 arg = (arg.message && arg.stack.indexOf(arg.message) === -1)
11680 ? 'Error: ' + arg.message + '\n' + arg.stack
11682 } else if (arg.sourceURL) {
11683 arg = arg.message + '\n' + arg.sourceURL + ':' + arg.line;
11689 function consoleLog(type) {
11690 var console = $window.console || {},
11691 logFn = console[type] || console.log || noop,
11694 // Note: reading logFn.apply throws an error in IE11 in IE8 document mode.
11695 // The reason behind this is that console.log has type "object" in IE8...
11697 hasApply = !!logFn.apply;
11701 return function() {
11703 forEach(arguments, function(arg) {
11704 args.push(formatError(arg));
11706 return logFn.apply(console, args);
11710 // we are IE which either doesn't have window.console => this is noop and we do nothing,
11711 // or we are IE where console.log doesn't have apply so we log at least first 2 args
11712 return function(arg1, arg2) {
11713 logFn(arg1, arg2 == null ? '' : arg2);
11719 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
11720 * Any commits to this file should be reviewed with security in mind. *
11721 * Changes to this file can potentially create security vulnerabilities. *
11722 * An approval from 2 Core members with history of modifying *
11723 * this file is required. *
11725 * Does the change somehow allow for arbitrary javascript to be executed? *
11726 * Or allows for someone to change the prototype of built-in objects? *
11727 * Or gives undesired access to variables likes document or window? *
11728 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
11730 var $parseMinErr = minErr('$parse');
11732 // Sandboxing Angular Expressions
11733 // ------------------------------
11734 // Angular expressions are generally considered safe because these expressions only have direct
11735 // access to `$scope` and locals. However, one can obtain the ability to execute arbitrary JS code by
11736 // obtaining a reference to native JS functions such as the Function constructor.
11738 // As an example, consider the following Angular expression:
11740 // {}.toString.constructor('alert("evil JS code")')
11742 // This sandboxing technique is not perfect and doesn't aim to be. The goal is to prevent exploits
11743 // against the expression language, but not to prevent exploits that were enabled by exposing
11744 // sensitive JavaScript or browser APIs on Scope. Exposing such objects on a Scope is never a good
11745 // practice and therefore we are not even trying to protect against interaction with an object
11746 // explicitly exposed in this way.
11748 // In general, it is not possible to access a Window object from an angular expression unless a
11749 // window or some DOM object that has a reference to window is published onto a Scope.
11750 // Similarly we prevent invocations of function known to be dangerous, as well as assignments to
11753 // See https://docs.angularjs.org/guide/security
11756 function ensureSafeMemberName(name, fullExpression) {
11757 if (name === "__defineGetter__" || name === "__defineSetter__"
11758 || name === "__lookupGetter__" || name === "__lookupSetter__"
11759 || name === "__proto__") {
11760 throw $parseMinErr('isecfld',
11761 'Attempting to access a disallowed field in Angular expressions! '
11762 + 'Expression: {0}', fullExpression);
11767 function getStringValue(name, fullExpression) {
11768 // From the JavaScript docs:
11769 // Property names must be strings. This means that non-string objects cannot be used
11770 // as keys in an object. Any non-string object, including a number, is typecasted
11771 // into a string via the toString method.
11773 // So, to ensure that we are checking the same `name` that JavaScript would use,
11774 // we cast it to a string, if possible.
11775 // Doing `name + ''` can cause a repl error if the result to `toString` is not a string,
11776 // this is, this will handle objects that misbehave.
11778 if (!isString(name)) {
11779 throw $parseMinErr('iseccst',
11780 'Cannot convert object to primitive value! '
11781 + 'Expression: {0}', fullExpression);
11786 function ensureSafeObject(obj, fullExpression) {
11787 // nifty check if obj is Function that is fast and works across iframes and other contexts
11789 if (obj.constructor === obj) {
11790 throw $parseMinErr('isecfn',
11791 'Referencing Function in Angular expressions is disallowed! Expression: {0}',
11793 } else if (// isWindow(obj)
11794 obj.window === obj) {
11795 throw $parseMinErr('isecwindow',
11796 'Referencing the Window in Angular expressions is disallowed! Expression: {0}',
11798 } else if (// isElement(obj)
11799 obj.children && (obj.nodeName || (obj.prop && obj.attr && obj.find))) {
11800 throw $parseMinErr('isecdom',
11801 'Referencing DOM nodes in Angular expressions is disallowed! Expression: {0}',
11803 } else if (// block Object so that we can't get hold of dangerous Object.* methods
11805 throw $parseMinErr('isecobj',
11806 'Referencing Object in Angular expressions is disallowed! Expression: {0}',
11813 var CALL = Function.prototype.call;
11814 var APPLY = Function.prototype.apply;
11815 var BIND = Function.prototype.bind;
11817 function ensureSafeFunction(obj, fullExpression) {
11819 if (obj.constructor === obj) {
11820 throw $parseMinErr('isecfn',
11821 'Referencing Function in Angular expressions is disallowed! Expression: {0}',
11823 } else if (obj === CALL || obj === APPLY || obj === BIND) {
11824 throw $parseMinErr('isecff',
11825 'Referencing call, apply or bind in Angular expressions is disallowed! Expression: {0}',
11831 //Keyword constants
11832 var CONSTANTS = createMap();
11834 'null': function() { return null; },
11835 'true': function() { return true; },
11836 'false': function() { return false; },
11837 'undefined': function() {}
11838 }, function(constantGetter, name) {
11839 constantGetter.constant = constantGetter.literal = constantGetter.sharedGetter = true;
11840 CONSTANTS[name] = constantGetter;
11843 //Not quite a constant, but can be lex/parsed the same
11844 CONSTANTS['this'] = function(self) { return self; };
11845 CONSTANTS['this'].sharedGetter = true;
11848 //Operators - will be wrapped by binaryFn/unaryFn/assignment/filter
11849 var OPERATORS = extend(createMap(), {
11850 '+':function(self, locals, a, b) {
11851 a=a(self, locals); b=b(self, locals);
11852 if (isDefined(a)) {
11853 if (isDefined(b)) {
11858 return isDefined(b) ? b : undefined;},
11859 '-':function(self, locals, a, b) {
11860 a=a(self, locals); b=b(self, locals);
11861 return (isDefined(a) ? a : 0) - (isDefined(b) ? b : 0);
11863 '*':function(self, locals, a, b) {return a(self, locals) * b(self, locals);},
11864 '/':function(self, locals, a, b) {return a(self, locals) / b(self, locals);},
11865 '%':function(self, locals, a, b) {return a(self, locals) % b(self, locals);},
11866 '===':function(self, locals, a, b) {return a(self, locals) === b(self, locals);},
11867 '!==':function(self, locals, a, b) {return a(self, locals) !== b(self, locals);},
11868 '==':function(self, locals, a, b) {return a(self, locals) == b(self, locals);},
11869 '!=':function(self, locals, a, b) {return a(self, locals) != b(self, locals);},
11870 '<':function(self, locals, a, b) {return a(self, locals) < b(self, locals);},
11871 '>':function(self, locals, a, b) {return a(self, locals) > b(self, locals);},
11872 '<=':function(self, locals, a, b) {return a(self, locals) <= b(self, locals);},
11873 '>=':function(self, locals, a, b) {return a(self, locals) >= b(self, locals);},
11874 '&&':function(self, locals, a, b) {return a(self, locals) && b(self, locals);},
11875 '||':function(self, locals, a, b) {return a(self, locals) || b(self, locals);},
11876 '!':function(self, locals, a) {return !a(self, locals);},
11878 //Tokenized as operators but parsed as assignment/filters
11882 var ESCAPE = {"n":"\n", "f":"\f", "r":"\r", "t":"\t", "v":"\v", "'":"'", '"':'"'};
11885 /////////////////////////////////////////
11891 var Lexer = function(options) {
11892 this.options = options;
11895 Lexer.prototype = {
11896 constructor: Lexer,
11898 lex: function(text) {
11903 while (this.index < this.text.length) {
11904 var ch = this.text.charAt(this.index);
11905 if (ch === '"' || ch === "'") {
11906 this.readString(ch);
11907 } else if (this.isNumber(ch) || ch === '.' && this.isNumber(this.peek())) {
11909 } else if (this.isIdent(ch)) {
11911 } else if (this.is(ch, '(){}[].,;:?')) {
11912 this.tokens.push({index: this.index, text: ch});
11914 } else if (this.isWhitespace(ch)) {
11917 var ch2 = ch + this.peek();
11918 var ch3 = ch2 + this.peek(2);
11919 var op1 = OPERATORS[ch];
11920 var op2 = OPERATORS[ch2];
11921 var op3 = OPERATORS[ch3];
11922 if (op1 || op2 || op3) {
11923 var token = op3 ? ch3 : (op2 ? ch2 : ch);
11924 this.tokens.push({index: this.index, text: token, operator: true});
11925 this.index += token.length;
11927 this.throwError('Unexpected next character ', this.index, this.index + 1);
11931 return this.tokens;
11934 is: function(ch, chars) {
11935 return chars.indexOf(ch) !== -1;
11938 peek: function(i) {
11940 return (this.index + num < this.text.length) ? this.text.charAt(this.index + num) : false;
11943 isNumber: function(ch) {
11944 return ('0' <= ch && ch <= '9') && typeof ch === "string";
11947 isWhitespace: function(ch) {
11948 // IE treats non-breaking space as \u00A0
11949 return (ch === ' ' || ch === '\r' || ch === '\t' ||
11950 ch === '\n' || ch === '\v' || ch === '\u00A0');
11953 isIdent: function(ch) {
11954 return ('a' <= ch && ch <= 'z' ||
11955 'A' <= ch && ch <= 'Z' ||
11956 '_' === ch || ch === '$');
11959 isExpOperator: function(ch) {
11960 return (ch === '-' || ch === '+' || this.isNumber(ch));
11963 throwError: function(error, start, end) {
11964 end = end || this.index;
11965 var colStr = (isDefined(start)
11966 ? 's ' + start + '-' + this.index + ' [' + this.text.substring(start, end) + ']'
11968 throw $parseMinErr('lexerr', 'Lexer Error: {0} at column{1} in expression [{2}].',
11969 error, colStr, this.text);
11972 readNumber: function() {
11974 var start = this.index;
11975 while (this.index < this.text.length) {
11976 var ch = lowercase(this.text.charAt(this.index));
11977 if (ch == '.' || this.isNumber(ch)) {
11980 var peekCh = this.peek();
11981 if (ch == 'e' && this.isExpOperator(peekCh)) {
11983 } else if (this.isExpOperator(ch) &&
11984 peekCh && this.isNumber(peekCh) &&
11985 number.charAt(number.length - 1) == 'e') {
11987 } else if (this.isExpOperator(ch) &&
11988 (!peekCh || !this.isNumber(peekCh)) &&
11989 number.charAt(number.length - 1) == 'e') {
11990 this.throwError('Invalid exponent');
12001 value: Number(number)
12005 readIdent: function() {
12006 var start = this.index;
12007 while (this.index < this.text.length) {
12008 var ch = this.text.charAt(this.index);
12009 if (!(this.isIdent(ch) || this.isNumber(ch))) {
12016 text: this.text.slice(start, this.index),
12021 readString: function(quote) {
12022 var start = this.index;
12025 var rawString = quote;
12026 var escape = false;
12027 while (this.index < this.text.length) {
12028 var ch = this.text.charAt(this.index);
12032 var hex = this.text.substring(this.index + 1, this.index + 5);
12033 if (!hex.match(/[\da-f]{4}/i))
12034 this.throwError('Invalid unicode escape [\\u' + hex + ']');
12036 string += String.fromCharCode(parseInt(hex, 16));
12038 var rep = ESCAPE[ch];
12039 string = string + (rep || ch);
12042 } else if (ch === '\\') {
12044 } else if (ch === quote) {
12058 this.throwError('Unterminated quote', start);
12063 function isConstant(exp) {
12064 return exp.constant;
12070 var Parser = function(lexer, $filter, options) {
12071 this.lexer = lexer;
12072 this.$filter = $filter;
12073 this.options = options;
12076 Parser.ZERO = extend(function() {
12079 sharedGetter: true,
12083 Parser.prototype = {
12084 constructor: Parser,
12086 parse: function(text) {
12088 this.tokens = this.lexer.lex(text);
12090 var value = this.statements();
12092 if (this.tokens.length !== 0) {
12093 this.throwError('is an unexpected token', this.tokens[0]);
12096 value.literal = !!value.literal;
12097 value.constant = !!value.constant;
12102 primary: function() {
12104 if (this.expect('(')) {
12105 primary = this.filterChain();
12107 } else if (this.expect('[')) {
12108 primary = this.arrayDeclaration();
12109 } else if (this.expect('{')) {
12110 primary = this.object();
12111 } else if (this.peek().identifier && this.peek().text in CONSTANTS) {
12112 primary = CONSTANTS[this.consume().text];
12113 } else if (this.peek().identifier) {
12114 primary = this.identifier();
12115 } else if (this.peek().constant) {
12116 primary = this.constant();
12118 this.throwError('not a primary expression', this.peek());
12122 while ((next = this.expect('(', '[', '.'))) {
12123 if (next.text === '(') {
12124 primary = this.functionCall(primary, context);
12126 } else if (next.text === '[') {
12128 primary = this.objectIndex(primary);
12129 } else if (next.text === '.') {
12131 primary = this.fieldAccess(primary);
12133 this.throwError('IMPOSSIBLE');
12139 throwError: function(msg, token) {
12140 throw $parseMinErr('syntax',
12141 'Syntax Error: Token \'{0}\' {1} at column {2} of the expression [{3}] starting at [{4}].',
12142 token.text, msg, (token.index + 1), this.text, this.text.substring(token.index));
12145 peekToken: function() {
12146 if (this.tokens.length === 0)
12147 throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text);
12148 return this.tokens[0];
12151 peek: function(e1, e2, e3, e4) {
12152 return this.peekAhead(0, e1, e2, e3, e4);
12154 peekAhead: function(i, e1, e2, e3, e4) {
12155 if (this.tokens.length > i) {
12156 var token = this.tokens[i];
12157 var t = token.text;
12158 if (t === e1 || t === e2 || t === e3 || t === e4 ||
12159 (!e1 && !e2 && !e3 && !e4)) {
12166 expect: function(e1, e2, e3, e4) {
12167 var token = this.peek(e1, e2, e3, e4);
12169 this.tokens.shift();
12175 consume: function(e1) {
12176 if (this.tokens.length === 0) {
12177 throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text);
12180 var token = this.expect(e1);
12182 this.throwError('is unexpected, expecting [' + e1 + ']', this.peek());
12187 unaryFn: function(op, right) {
12188 var fn = OPERATORS[op];
12189 return extend(function $parseUnaryFn(self, locals) {
12190 return fn(self, locals, right);
12192 constant:right.constant,
12197 binaryFn: function(left, op, right, isBranching) {
12198 var fn = OPERATORS[op];
12199 return extend(function $parseBinaryFn(self, locals) {
12200 return fn(self, locals, left, right);
12202 constant: left.constant && right.constant,
12203 inputs: !isBranching && [left, right]
12207 identifier: function() {
12208 var id = this.consume().text;
12210 //Continue reading each `.identifier` unless it is a method invocation
12211 while (this.peek('.') && this.peekAhead(1).identifier && !this.peekAhead(2, '(')) {
12212 id += this.consume().text + this.consume().text;
12215 return getterFn(id, this.options, this.text);
12218 constant: function() {
12219 var value = this.consume().value;
12221 return extend(function $parseConstant() {
12229 statements: function() {
12230 var statements = [];
12232 if (this.tokens.length > 0 && !this.peek('}', ')', ';', ']'))
12233 statements.push(this.filterChain());
12234 if (!this.expect(';')) {
12235 // optimize for the common case where there is only one statement.
12236 // TODO(size): maybe we should not support multiple statements?
12237 return (statements.length === 1)
12239 : function $parseStatements(self, locals) {
12241 for (var i = 0, ii = statements.length; i < ii; i++) {
12242 value = statements[i](self, locals);
12250 filterChain: function() {
12251 var left = this.expression();
12253 while ((token = this.expect('|'))) {
12254 left = this.filter(left);
12259 filter: function(inputFn) {
12260 var fn = this.$filter(this.consume().text);
12264 if (this.peek(':')) {
12266 args = []; // we can safely reuse the array
12267 while (this.expect(':')) {
12268 argsFn.push(this.expression());
12272 var inputs = [inputFn].concat(argsFn || []);
12274 return extend(function $parseFilter(self, locals) {
12275 var input = inputFn(self, locals);
12279 var i = argsFn.length;
12281 args[i + 1] = argsFn[i](self, locals);
12284 return fn.apply(undefined, args);
12289 constant: !fn.$stateful && inputs.every(isConstant),
12290 inputs: !fn.$stateful && inputs
12294 expression: function() {
12295 return this.assignment();
12298 assignment: function() {
12299 var left = this.ternary();
12302 if ((token = this.expect('='))) {
12303 if (!left.assign) {
12304 this.throwError('implies assignment but [' +
12305 this.text.substring(0, token.index) + '] can not be assigned to', token);
12307 right = this.ternary();
12308 return extend(function $parseAssignment(scope, locals) {
12309 return left.assign(scope, right(scope, locals), locals);
12311 inputs: [left, right]
12317 ternary: function() {
12318 var left = this.logicalOR();
12321 if ((token = this.expect('?'))) {
12322 middle = this.assignment();
12323 if (this.consume(':')) {
12324 var right = this.assignment();
12326 return extend(function $parseTernary(self, locals) {
12327 return left(self, locals) ? middle(self, locals) : right(self, locals);
12329 constant: left.constant && middle.constant && right.constant
12337 logicalOR: function() {
12338 var left = this.logicalAND();
12340 while ((token = this.expect('||'))) {
12341 left = this.binaryFn(left, token.text, this.logicalAND(), true);
12346 logicalAND: function() {
12347 var left = this.equality();
12349 while ((token = this.expect('&&'))) {
12350 left = this.binaryFn(left, token.text, this.equality(), true);
12355 equality: function() {
12356 var left = this.relational();
12358 while ((token = this.expect('==','!=','===','!=='))) {
12359 left = this.binaryFn(left, token.text, this.relational());
12364 relational: function() {
12365 var left = this.additive();
12367 while ((token = this.expect('<', '>', '<=', '>='))) {
12368 left = this.binaryFn(left, token.text, this.additive());
12373 additive: function() {
12374 var left = this.multiplicative();
12376 while ((token = this.expect('+','-'))) {
12377 left = this.binaryFn(left, token.text, this.multiplicative());
12382 multiplicative: function() {
12383 var left = this.unary();
12385 while ((token = this.expect('*','/','%'))) {
12386 left = this.binaryFn(left, token.text, this.unary());
12391 unary: function() {
12393 if (this.expect('+')) {
12394 return this.primary();
12395 } else if ((token = this.expect('-'))) {
12396 return this.binaryFn(Parser.ZERO, token.text, this.unary());
12397 } else if ((token = this.expect('!'))) {
12398 return this.unaryFn(token.text, this.unary());
12400 return this.primary();
12404 fieldAccess: function(object) {
12405 var getter = this.identifier();
12407 return extend(function $parseFieldAccess(scope, locals, self) {
12408 var o = self || object(scope, locals);
12409 return (o == null) ? undefined : getter(o);
12411 assign: function(scope, value, locals) {
12412 var o = object(scope, locals);
12413 if (!o) object.assign(scope, o = {}, locals);
12414 return getter.assign(o, value);
12419 objectIndex: function(obj) {
12420 var expression = this.text;
12422 var indexFn = this.expression();
12425 return extend(function $parseObjectIndex(self, locals) {
12426 var o = obj(self, locals),
12427 i = getStringValue(indexFn(self, locals), expression),
12430 ensureSafeMemberName(i, expression);
12431 if (!o) return undefined;
12432 v = ensureSafeObject(o[i], expression);
12435 assign: function(self, value, locals) {
12436 var key = ensureSafeMemberName(getStringValue(indexFn(self, locals), expression), expression);
12437 // prevent overwriting of Function.constructor which would break ensureSafeObject check
12438 var o = ensureSafeObject(obj(self, locals), expression);
12439 if (!o) obj.assign(self, o = {}, locals);
12440 return o[key] = value;
12445 functionCall: function(fnGetter, contextGetter) {
12447 if (this.peekToken().text !== ')') {
12449 argsFn.push(this.expression());
12450 } while (this.expect(','));
12454 var expressionText = this.text;
12455 // we can safely reuse the array across invocations
12456 var args = argsFn.length ? [] : null;
12458 return function $parseFunctionCall(scope, locals) {
12459 var context = contextGetter ? contextGetter(scope, locals) : isDefined(contextGetter) ? undefined : scope;
12460 var fn = fnGetter(scope, locals, context) || noop;
12463 var i = argsFn.length;
12465 args[i] = ensureSafeObject(argsFn[i](scope, locals), expressionText);
12469 ensureSafeObject(context, expressionText);
12470 ensureSafeFunction(fn, expressionText);
12472 // IE doesn't have apply for some native functions
12474 ? fn.apply(context, args)
12475 : fn(args[0], args[1], args[2], args[3], args[4]);
12478 // Free-up the memory (arguments of the last function call).
12482 return ensureSafeObject(v, expressionText);
12486 // This is used with json array declaration
12487 arrayDeclaration: function() {
12488 var elementFns = [];
12489 if (this.peekToken().text !== ']') {
12491 if (this.peek(']')) {
12492 // Support trailing commas per ES5.1.
12495 elementFns.push(this.expression());
12496 } while (this.expect(','));
12500 return extend(function $parseArrayLiteral(self, locals) {
12502 for (var i = 0, ii = elementFns.length; i < ii; i++) {
12503 array.push(elementFns[i](self, locals));
12508 constant: elementFns.every(isConstant),
12513 object: function() {
12514 var keys = [], valueFns = [];
12515 if (this.peekToken().text !== '}') {
12517 if (this.peek('}')) {
12518 // Support trailing commas per ES5.1.
12521 var token = this.consume();
12522 if (token.constant) {
12523 keys.push(token.value);
12524 } else if (token.identifier) {
12525 keys.push(token.text);
12527 this.throwError("invalid key", token);
12530 valueFns.push(this.expression());
12531 } while (this.expect(','));
12535 return extend(function $parseObjectLiteral(self, locals) {
12537 for (var i = 0, ii = valueFns.length; i < ii; i++) {
12538 object[keys[i]] = valueFns[i](self, locals);
12543 constant: valueFns.every(isConstant),
12550 //////////////////////////////////////////////////
12551 // Parser helper functions
12552 //////////////////////////////////////////////////
12554 function setter(obj, locals, path, setValue, fullExp) {
12555 ensureSafeObject(obj, fullExp);
12556 ensureSafeObject(locals, fullExp);
12558 var element = path.split('.'), key;
12559 for (var i = 0; element.length > 1; i++) {
12560 key = ensureSafeMemberName(element.shift(), fullExp);
12561 var propertyObj = (i === 0 && locals && locals[key]) || obj[key];
12562 if (!propertyObj) {
12564 obj[key] = propertyObj;
12566 obj = ensureSafeObject(propertyObj, fullExp);
12568 key = ensureSafeMemberName(element.shift(), fullExp);
12569 ensureSafeObject(obj[key], fullExp);
12570 obj[key] = setValue;
12574 var getterFnCacheDefault = createMap();
12575 var getterFnCacheExpensive = createMap();
12577 function isPossiblyDangerousMemberName(name) {
12578 return name == 'constructor';
12582 * Implementation of the "Black Hole" variant from:
12583 * - http://jsperf.com/angularjs-parse-getter/4
12584 * - http://jsperf.com/path-evaluation-simplified/7
12586 function cspSafeGetterFn(key0, key1, key2, key3, key4, fullExp, expensiveChecks) {
12587 ensureSafeMemberName(key0, fullExp);
12588 ensureSafeMemberName(key1, fullExp);
12589 ensureSafeMemberName(key2, fullExp);
12590 ensureSafeMemberName(key3, fullExp);
12591 ensureSafeMemberName(key4, fullExp);
12592 var eso = function(o) {
12593 return ensureSafeObject(o, fullExp);
12595 var eso0 = (expensiveChecks || isPossiblyDangerousMemberName(key0)) ? eso : identity;
12596 var eso1 = (expensiveChecks || isPossiblyDangerousMemberName(key1)) ? eso : identity;
12597 var eso2 = (expensiveChecks || isPossiblyDangerousMemberName(key2)) ? eso : identity;
12598 var eso3 = (expensiveChecks || isPossiblyDangerousMemberName(key3)) ? eso : identity;
12599 var eso4 = (expensiveChecks || isPossiblyDangerousMemberName(key4)) ? eso : identity;
12601 return function cspSafeGetter(scope, locals) {
12602 var pathVal = (locals && locals.hasOwnProperty(key0)) ? locals : scope;
12604 if (pathVal == null) return pathVal;
12605 pathVal = eso0(pathVal[key0]);
12607 if (!key1) return pathVal;
12608 if (pathVal == null) return undefined;
12609 pathVal = eso1(pathVal[key1]);
12611 if (!key2) return pathVal;
12612 if (pathVal == null) return undefined;
12613 pathVal = eso2(pathVal[key2]);
12615 if (!key3) return pathVal;
12616 if (pathVal == null) return undefined;
12617 pathVal = eso3(pathVal[key3]);
12619 if (!key4) return pathVal;
12620 if (pathVal == null) return undefined;
12621 pathVal = eso4(pathVal[key4]);
12627 function getterFnWithEnsureSafeObject(fn, fullExpression) {
12628 return function(s, l) {
12629 return fn(s, l, ensureSafeObject, fullExpression);
12633 function getterFn(path, options, fullExp) {
12634 var expensiveChecks = options.expensiveChecks;
12635 var getterFnCache = (expensiveChecks ? getterFnCacheExpensive : getterFnCacheDefault);
12636 var fn = getterFnCache[path];
12640 var pathKeys = path.split('.'),
12641 pathKeysLength = pathKeys.length;
12643 // http://jsperf.com/angularjs-parse-getter/6
12645 if (pathKeysLength < 6) {
12646 fn = cspSafeGetterFn(pathKeys[0], pathKeys[1], pathKeys[2], pathKeys[3], pathKeys[4], fullExp, expensiveChecks);
12648 fn = function cspSafeGetter(scope, locals) {
12651 val = cspSafeGetterFn(pathKeys[i++], pathKeys[i++], pathKeys[i++], pathKeys[i++],
12652 pathKeys[i++], fullExp, expensiveChecks)(scope, locals);
12654 locals = undefined; // clear after first iteration
12656 } while (i < pathKeysLength);
12662 if (expensiveChecks) {
12663 code += 's = eso(s, fe);\nl = eso(l, fe);\n';
12665 var needsEnsureSafeObject = expensiveChecks;
12666 forEach(pathKeys, function(key, index) {
12667 ensureSafeMemberName(key, fullExp);
12668 var lookupJs = (index
12669 // we simply dereference 's' on any .dot notation
12671 // but if we are first then we check locals first, and if so read it first
12672 : '((l&&l.hasOwnProperty("' + key + '"))?l:s)') + '.' + key;
12673 if (expensiveChecks || isPossiblyDangerousMemberName(key)) {
12674 lookupJs = 'eso(' + lookupJs + ', fe)';
12675 needsEnsureSafeObject = true;
12677 code += 'if(s == null) return undefined;\n' +
12678 's=' + lookupJs + ';\n';
12680 code += 'return s;';
12683 var evaledFnGetter = new Function('s', 'l', 'eso', 'fe', code); // s=scope, l=locals, eso=ensureSafeObject
12685 evaledFnGetter.toString = valueFn(code);
12686 if (needsEnsureSafeObject) {
12687 evaledFnGetter = getterFnWithEnsureSafeObject(evaledFnGetter, fullExp);
12689 fn = evaledFnGetter;
12692 fn.sharedGetter = true;
12693 fn.assign = function(self, value, locals) {
12694 return setter(self, locals, path, value, path);
12696 getterFnCache[path] = fn;
12700 var objectValueOf = Object.prototype.valueOf;
12702 function getValueOf(value) {
12703 return isFunction(value.valueOf) ? value.valueOf() : objectValueOf.call(value);
12706 ///////////////////////////////////
12715 * Converts Angular {@link guide/expression expression} into a function.
12718 * var getter = $parse('user.name');
12719 * var setter = getter.assign;
12720 * var context = {user:{name:'angular'}};
12721 * var locals = {user:{name:'local'}};
12723 * expect(getter(context)).toEqual('angular');
12724 * setter(context, 'newValue');
12725 * expect(context.user.name).toEqual('newValue');
12726 * expect(getter(context, locals)).toEqual('local');
12730 * @param {string} expression String expression to compile.
12731 * @returns {function(context, locals)} a function which represents the compiled expression:
12733 * * `context` – `{object}` – an object against which any expressions embedded in the strings
12734 * are evaluated against (typically a scope object).
12735 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
12738 * The returned function also has the following properties:
12739 * * `literal` – `{boolean}` – whether the expression's top-level node is a JavaScript
12741 * * `constant` – `{boolean}` – whether the expression is made entirely of JavaScript
12742 * constant literals.
12743 * * `assign` – `{?function(context, value)}` – if the expression is assignable, this will be
12744 * set to a function to change its value on the given context.
12751 * @name $parseProvider
12754 * `$parseProvider` can be used for configuring the default behavior of the {@link ng.$parse $parse}
12757 function $ParseProvider() {
12758 var cacheDefault = createMap();
12759 var cacheExpensive = createMap();
12763 this.$get = ['$filter', '$sniffer', function($filter, $sniffer) {
12764 var $parseOptions = {
12766 expensiveChecks: false
12768 $parseOptionsExpensive = {
12770 expensiveChecks: true
12773 function wrapSharedExpression(exp) {
12776 if (exp.sharedGetter) {
12777 wrapped = function $parseWrapper(self, locals) {
12778 return exp(self, locals);
12780 wrapped.literal = exp.literal;
12781 wrapped.constant = exp.constant;
12782 wrapped.assign = exp.assign;
12788 return function $parse(exp, interceptorFn, expensiveChecks) {
12789 var parsedExpression, oneTime, cacheKey;
12791 switch (typeof exp) {
12793 cacheKey = exp = exp.trim();
12795 var cache = (expensiveChecks ? cacheExpensive : cacheDefault);
12796 parsedExpression = cache[cacheKey];
12798 if (!parsedExpression) {
12799 if (exp.charAt(0) === ':' && exp.charAt(1) === ':') {
12801 exp = exp.substring(2);
12804 var parseOptions = expensiveChecks ? $parseOptionsExpensive : $parseOptions;
12805 var lexer = new Lexer(parseOptions);
12806 var parser = new Parser(lexer, $filter, parseOptions);
12807 parsedExpression = parser.parse(exp);
12809 if (parsedExpression.constant) {
12810 parsedExpression.$$watchDelegate = constantWatchDelegate;
12811 } else if (oneTime) {
12812 //oneTime is not part of the exp passed to the Parser so we may have to
12813 //wrap the parsedExpression before adding a $$watchDelegate
12814 parsedExpression = wrapSharedExpression(parsedExpression);
12815 parsedExpression.$$watchDelegate = parsedExpression.literal ?
12816 oneTimeLiteralWatchDelegate : oneTimeWatchDelegate;
12817 } else if (parsedExpression.inputs) {
12818 parsedExpression.$$watchDelegate = inputsWatchDelegate;
12821 cache[cacheKey] = parsedExpression;
12823 return addInterceptor(parsedExpression, interceptorFn);
12826 return addInterceptor(exp, interceptorFn);
12829 return addInterceptor(noop, interceptorFn);
12833 function collectExpressionInputs(inputs, list) {
12834 for (var i = 0, ii = inputs.length; i < ii; i++) {
12835 var input = inputs[i];
12836 if (!input.constant) {
12837 if (input.inputs) {
12838 collectExpressionInputs(input.inputs, list);
12839 } else if (list.indexOf(input) === -1) { // TODO(perf) can we do better?
12848 function expressionInputDirtyCheck(newValue, oldValueOfValue) {
12850 if (newValue == null || oldValueOfValue == null) { // null/undefined
12851 return newValue === oldValueOfValue;
12854 if (typeof newValue === 'object') {
12856 // attempt to convert the value to a primitive type
12857 // TODO(docs): add a note to docs that by implementing valueOf even objects and arrays can
12858 // be cheaply dirty-checked
12859 newValue = getValueOf(newValue);
12861 if (typeof newValue === 'object') {
12862 // objects/arrays are not supported - deep-watching them would be too expensive
12866 // fall-through to the primitive equality check
12870 return newValue === oldValueOfValue || (newValue !== newValue && oldValueOfValue !== oldValueOfValue);
12873 function inputsWatchDelegate(scope, listener, objectEquality, parsedExpression) {
12874 var inputExpressions = parsedExpression.$$inputs ||
12875 (parsedExpression.$$inputs = collectExpressionInputs(parsedExpression.inputs, []));
12879 if (inputExpressions.length === 1) {
12880 var oldInputValue = expressionInputDirtyCheck; // init to something unique so that equals check fails
12881 inputExpressions = inputExpressions[0];
12882 return scope.$watch(function expressionInputWatch(scope) {
12883 var newInputValue = inputExpressions(scope);
12884 if (!expressionInputDirtyCheck(newInputValue, oldInputValue)) {
12885 lastResult = parsedExpression(scope);
12886 oldInputValue = newInputValue && getValueOf(newInputValue);
12889 }, listener, objectEquality);
12892 var oldInputValueOfValues = [];
12893 for (var i = 0, ii = inputExpressions.length; i < ii; i++) {
12894 oldInputValueOfValues[i] = expressionInputDirtyCheck; // init to something unique so that equals check fails
12897 return scope.$watch(function expressionInputsWatch(scope) {
12898 var changed = false;
12900 for (var i = 0, ii = inputExpressions.length; i < ii; i++) {
12901 var newInputValue = inputExpressions[i](scope);
12902 if (changed || (changed = !expressionInputDirtyCheck(newInputValue, oldInputValueOfValues[i]))) {
12903 oldInputValueOfValues[i] = newInputValue && getValueOf(newInputValue);
12908 lastResult = parsedExpression(scope);
12912 }, listener, objectEquality);
12915 function oneTimeWatchDelegate(scope, listener, objectEquality, parsedExpression) {
12916 var unwatch, lastValue;
12917 return unwatch = scope.$watch(function oneTimeWatch(scope) {
12918 return parsedExpression(scope);
12919 }, function oneTimeListener(value, old, scope) {
12921 if (isFunction(listener)) {
12922 listener.apply(this, arguments);
12924 if (isDefined(value)) {
12925 scope.$$postDigest(function() {
12926 if (isDefined(lastValue)) {
12931 }, objectEquality);
12934 function oneTimeLiteralWatchDelegate(scope, listener, objectEquality, parsedExpression) {
12935 var unwatch, lastValue;
12936 return unwatch = scope.$watch(function oneTimeWatch(scope) {
12937 return parsedExpression(scope);
12938 }, function oneTimeListener(value, old, scope) {
12940 if (isFunction(listener)) {
12941 listener.call(this, value, old, scope);
12943 if (isAllDefined(value)) {
12944 scope.$$postDigest(function() {
12945 if (isAllDefined(lastValue)) unwatch();
12948 }, objectEquality);
12950 function isAllDefined(value) {
12951 var allDefined = true;
12952 forEach(value, function(val) {
12953 if (!isDefined(val)) allDefined = false;
12959 function constantWatchDelegate(scope, listener, objectEquality, parsedExpression) {
12961 return unwatch = scope.$watch(function constantWatch(scope) {
12962 return parsedExpression(scope);
12963 }, function constantListener(value, old, scope) {
12964 if (isFunction(listener)) {
12965 listener.apply(this, arguments);
12968 }, objectEquality);
12971 function addInterceptor(parsedExpression, interceptorFn) {
12972 if (!interceptorFn) return parsedExpression;
12973 var watchDelegate = parsedExpression.$$watchDelegate;
12976 watchDelegate !== oneTimeLiteralWatchDelegate &&
12977 watchDelegate !== oneTimeWatchDelegate;
12979 var fn = regularWatch ? function regularInterceptedExpression(scope, locals) {
12980 var value = parsedExpression(scope, locals);
12981 return interceptorFn(value, scope, locals);
12982 } : function oneTimeInterceptedExpression(scope, locals) {
12983 var value = parsedExpression(scope, locals);
12984 var result = interceptorFn(value, scope, locals);
12985 // we only return the interceptor's result if the
12986 // initial value is defined (for bind-once)
12987 return isDefined(value) ? result : value;
12990 // Propagate $$watchDelegates other then inputsWatchDelegate
12991 if (parsedExpression.$$watchDelegate &&
12992 parsedExpression.$$watchDelegate !== inputsWatchDelegate) {
12993 fn.$$watchDelegate = parsedExpression.$$watchDelegate;
12994 } else if (!interceptorFn.$stateful) {
12995 // If there is an interceptor, but no watchDelegate then treat the interceptor like
12996 // we treat filters - it is assumed to be a pure function unless flagged with $stateful
12997 fn.$$watchDelegate = inputsWatchDelegate;
12998 fn.inputs = [parsedExpression];
13009 * @requires $rootScope
13012 * A service that helps you run functions asynchronously, and use their return values (or exceptions)
13013 * when they are done processing.
13015 * This is an implementation of promises/deferred objects inspired by
13016 * [Kris Kowal's Q](https://github.com/kriskowal/q).
13018 * $q can be used in two fashions --- one which is more similar to Kris Kowal's Q or jQuery's Deferred
13019 * implementations, and the other which resembles ES6 promises to some degree.
13023 * The streamlined ES6 style promise is essentially just using $q as a constructor which takes a `resolver`
13024 * function as the first argument. This is similar to the native Promise implementation from ES6 Harmony,
13025 * see [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise).
13027 * While the constructor-style use is supported, not all of the supporting methods from ES6 Harmony promises are
13030 * It can be used like so:
13033 * // for the purpose of this example let's assume that variables `$q` and `okToGreet`
13034 * // are available in the current lexical scope (they could have been injected or passed in).
13036 * function asyncGreet(name) {
13037 * // perform some asynchronous operation, resolve or reject the promise when appropriate.
13038 * return $q(function(resolve, reject) {
13039 * setTimeout(function() {
13040 * if (okToGreet(name)) {
13041 * resolve('Hello, ' + name + '!');
13043 * reject('Greeting ' + name + ' is not allowed.');
13049 * var promise = asyncGreet('Robin Hood');
13050 * promise.then(function(greeting) {
13051 * alert('Success: ' + greeting);
13052 * }, function(reason) {
13053 * alert('Failed: ' + reason);
13057 * Note: progress/notify callbacks are not currently supported via the ES6-style interface.
13059 * However, the more traditional CommonJS-style usage is still available, and documented below.
13061 * [The CommonJS Promise proposal](http://wiki.commonjs.org/wiki/Promises) describes a promise as an
13062 * interface for interacting with an object that represents the result of an action that is
13063 * performed asynchronously, and may or may not be finished at any given point in time.
13065 * From the perspective of dealing with error handling, deferred and promise APIs are to
13066 * asynchronous programming what `try`, `catch` and `throw` keywords are to synchronous programming.
13069 * // for the purpose of this example let's assume that variables `$q` and `okToGreet`
13070 * // are available in the current lexical scope (they could have been injected or passed in).
13072 * function asyncGreet(name) {
13073 * var deferred = $q.defer();
13075 * setTimeout(function() {
13076 * deferred.notify('About to greet ' + name + '.');
13078 * if (okToGreet(name)) {
13079 * deferred.resolve('Hello, ' + name + '!');
13081 * deferred.reject('Greeting ' + name + ' is not allowed.');
13085 * return deferred.promise;
13088 * var promise = asyncGreet('Robin Hood');
13089 * promise.then(function(greeting) {
13090 * alert('Success: ' + greeting);
13091 * }, function(reason) {
13092 * alert('Failed: ' + reason);
13093 * }, function(update) {
13094 * alert('Got notification: ' + update);
13098 * At first it might not be obvious why this extra complexity is worth the trouble. The payoff
13099 * comes in the way of guarantees that promise and deferred APIs make, see
13100 * https://github.com/kriskowal/uncommonjs/blob/master/promises/specification.md.
13102 * Additionally the promise api allows for composition that is very hard to do with the
13103 * traditional callback ([CPS](http://en.wikipedia.org/wiki/Continuation-passing_style)) approach.
13104 * For more on this please see the [Q documentation](https://github.com/kriskowal/q) especially the
13105 * section on serial or parallel joining of promises.
13107 * # The Deferred API
13109 * A new instance of deferred is constructed by calling `$q.defer()`.
13111 * The purpose of the deferred object is to expose the associated Promise instance as well as APIs
13112 * that can be used for signaling the successful or unsuccessful completion, as well as the status
13117 * - `resolve(value)` – resolves the derived promise with the `value`. If the value is a rejection
13118 * constructed via `$q.reject`, the promise will be rejected instead.
13119 * - `reject(reason)` – rejects the derived promise with the `reason`. This is equivalent to
13120 * resolving it with a rejection constructed via `$q.reject`.
13121 * - `notify(value)` - provides updates on the status of the promise's execution. This may be called
13122 * multiple times before the promise is either resolved or rejected.
13126 * - promise – `{Promise}` – promise object associated with this deferred.
13129 * # The Promise API
13131 * A new promise instance is created when a deferred instance is created and can be retrieved by
13132 * calling `deferred.promise`.
13134 * The purpose of the promise object is to allow for interested parties to get access to the result
13135 * of the deferred task when it completes.
13139 * - `then(successCallback, errorCallback, notifyCallback)` – regardless of when the promise was or
13140 * will be resolved or rejected, `then` calls one of the success or error callbacks asynchronously
13141 * as soon as the result is available. The callbacks are called with a single argument: the result
13142 * or rejection reason. Additionally, the notify callback may be called zero or more times to
13143 * provide a progress indication, before the promise is resolved or rejected.
13145 * This method *returns a new promise* which is resolved or rejected via the return value of the
13146 * `successCallback`, `errorCallback`. It also notifies via the return value of the
13147 * `notifyCallback` method. The promise cannot be resolved or rejected from the notifyCallback
13150 * - `catch(errorCallback)` – shorthand for `promise.then(null, errorCallback)`
13152 * - `finally(callback, notifyCallback)` – allows you to observe either the fulfillment or rejection of a promise,
13153 * but to do so without modifying the final value. This is useful to release resources or do some
13154 * clean-up that needs to be done whether the promise was rejected or resolved. See the [full
13155 * specification](https://github.com/kriskowal/q/wiki/API-Reference#promisefinallycallback) for
13156 * more information.
13158 * # Chaining promises
13160 * Because calling the `then` method of a promise returns a new derived promise, it is easily
13161 * possible to create a chain of promises:
13164 * promiseB = promiseA.then(function(result) {
13165 * return result + 1;
13168 * // promiseB will be resolved immediately after promiseA is resolved and its value
13169 * // will be the result of promiseA incremented by 1
13172 * It is possible to create chains of any length and since a promise can be resolved with another
13173 * promise (which will defer its resolution further), it is possible to pause/defer resolution of
13174 * the promises at any point in the chain. This makes it possible to implement powerful APIs like
13175 * $http's response interceptors.
13178 * # Differences between Kris Kowal's Q and $q
13180 * There are two main differences:
13182 * - $q is integrated with the {@link ng.$rootScope.Scope} Scope model observation
13183 * mechanism in angular, which means faster propagation of resolution or rejection into your
13184 * models and avoiding unnecessary browser repaints, which would result in flickering UI.
13185 * - Q has many more features than $q, but that comes at a cost of bytes. $q is tiny, but contains
13186 * all the important functionality needed for common async tasks.
13191 * it('should simulate promise', inject(function($q, $rootScope) {
13192 * var deferred = $q.defer();
13193 * var promise = deferred.promise;
13194 * var resolvedValue;
13196 * promise.then(function(value) { resolvedValue = value; });
13197 * expect(resolvedValue).toBeUndefined();
13199 * // Simulate resolving of promise
13200 * deferred.resolve(123);
13201 * // Note that the 'then' function does not get called synchronously.
13202 * // This is because we want the promise API to always be async, whether or not
13203 * // it got called synchronously or asynchronously.
13204 * expect(resolvedValue).toBeUndefined();
13206 * // Propagate promise resolution to 'then' functions using $apply().
13207 * $rootScope.$apply();
13208 * expect(resolvedValue).toEqual(123);
13212 * @param {function(function, function)} resolver Function which is responsible for resolving or
13213 * rejecting the newly created promise. The first parameter is a function which resolves the
13214 * promise, the second parameter is a function which rejects the promise.
13216 * @returns {Promise} The newly created promise.
13218 function $QProvider() {
13220 this.$get = ['$rootScope', '$exceptionHandler', function($rootScope, $exceptionHandler) {
13221 return qFactory(function(callback) {
13222 $rootScope.$evalAsync(callback);
13223 }, $exceptionHandler);
13227 function $$QProvider() {
13228 this.$get = ['$browser', '$exceptionHandler', function($browser, $exceptionHandler) {
13229 return qFactory(function(callback) {
13230 $browser.defer(callback);
13231 }, $exceptionHandler);
13236 * Constructs a promise manager.
13238 * @param {function(function)} nextTick Function for executing functions in the next turn.
13239 * @param {function(...*)} exceptionHandler Function into which unexpected exceptions are passed for
13240 * debugging purposes.
13241 * @returns {object} Promise manager.
13243 function qFactory(nextTick, exceptionHandler) {
13244 var $qMinErr = minErr('$q', TypeError);
13245 function callOnce(self, resolveFn, rejectFn) {
13246 var called = false;
13247 function wrap(fn) {
13248 return function(value) {
13249 if (called) return;
13251 fn.call(self, value);
13255 return [wrap(resolveFn), wrap(rejectFn)];
13260 * @name ng.$q#defer
13264 * Creates a `Deferred` object which represents a task which will finish in the future.
13266 * @returns {Deferred} Returns a new instance of deferred.
13268 var defer = function() {
13269 return new Deferred();
13272 function Promise() {
13273 this.$$state = { status: 0 };
13276 Promise.prototype = {
13277 then: function(onFulfilled, onRejected, progressBack) {
13278 var result = new Deferred();
13280 this.$$state.pending = this.$$state.pending || [];
13281 this.$$state.pending.push([result, onFulfilled, onRejected, progressBack]);
13282 if (this.$$state.status > 0) scheduleProcessQueue(this.$$state);
13284 return result.promise;
13287 "catch": function(callback) {
13288 return this.then(null, callback);
13291 "finally": function(callback, progressBack) {
13292 return this.then(function(value) {
13293 return handleCallback(value, true, callback);
13294 }, function(error) {
13295 return handleCallback(error, false, callback);
13300 //Faster, more basic than angular.bind http://jsperf.com/angular-bind-vs-custom-vs-native
13301 function simpleBind(context, fn) {
13302 return function(value) {
13303 fn.call(context, value);
13307 function processQueue(state) {
13308 var fn, promise, pending;
13310 pending = state.pending;
13311 state.processScheduled = false;
13312 state.pending = undefined;
13313 for (var i = 0, ii = pending.length; i < ii; ++i) {
13314 promise = pending[i][0];
13315 fn = pending[i][state.status];
13317 if (isFunction(fn)) {
13318 promise.resolve(fn(state.value));
13319 } else if (state.status === 1) {
13320 promise.resolve(state.value);
13322 promise.reject(state.value);
13326 exceptionHandler(e);
13331 function scheduleProcessQueue(state) {
13332 if (state.processScheduled || !state.pending) return;
13333 state.processScheduled = true;
13334 nextTick(function() { processQueue(state); });
13337 function Deferred() {
13338 this.promise = new Promise();
13339 //Necessary to support unbound execution :/
13340 this.resolve = simpleBind(this, this.resolve);
13341 this.reject = simpleBind(this, this.reject);
13342 this.notify = simpleBind(this, this.notify);
13345 Deferred.prototype = {
13346 resolve: function(val) {
13347 if (this.promise.$$state.status) return;
13348 if (val === this.promise) {
13349 this.$$reject($qMinErr(
13351 "Expected promise to be resolved with value other than itself '{0}'",
13354 this.$$resolve(val);
13359 $$resolve: function(val) {
13362 fns = callOnce(this, this.$$resolve, this.$$reject);
13364 if ((isObject(val) || isFunction(val))) then = val && val.then;
13365 if (isFunction(then)) {
13366 this.promise.$$state.status = -1;
13367 then.call(val, fns[0], fns[1], this.notify);
13369 this.promise.$$state.value = val;
13370 this.promise.$$state.status = 1;
13371 scheduleProcessQueue(this.promise.$$state);
13375 exceptionHandler(e);
13379 reject: function(reason) {
13380 if (this.promise.$$state.status) return;
13381 this.$$reject(reason);
13384 $$reject: function(reason) {
13385 this.promise.$$state.value = reason;
13386 this.promise.$$state.status = 2;
13387 scheduleProcessQueue(this.promise.$$state);
13390 notify: function(progress) {
13391 var callbacks = this.promise.$$state.pending;
13393 if ((this.promise.$$state.status <= 0) && callbacks && callbacks.length) {
13394 nextTick(function() {
13395 var callback, result;
13396 for (var i = 0, ii = callbacks.length; i < ii; i++) {
13397 result = callbacks[i][0];
13398 callback = callbacks[i][3];
13400 result.notify(isFunction(callback) ? callback(progress) : progress);
13402 exceptionHandler(e);
13416 * Creates a promise that is resolved as rejected with the specified `reason`. This api should be
13417 * used to forward rejection in a chain of promises. If you are dealing with the last promise in
13418 * a promise chain, you don't need to worry about it.
13420 * When comparing deferreds/promises to the familiar behavior of try/catch/throw, think of
13421 * `reject` as the `throw` keyword in JavaScript. This also means that if you "catch" an error via
13422 * a promise error callback and you want to forward the error to the promise derived from the
13423 * current promise, you have to "rethrow" the error by returning a rejection constructed via
13427 * promiseB = promiseA.then(function(result) {
13428 * // success: do something and resolve promiseB
13429 * // with the old or a new result
13431 * }, function(reason) {
13432 * // error: handle the error if possible and
13433 * // resolve promiseB with newPromiseOrValue,
13434 * // otherwise forward the rejection to promiseB
13435 * if (canHandle(reason)) {
13436 * // handle the error and recover
13437 * return newPromiseOrValue;
13439 * return $q.reject(reason);
13443 * @param {*} reason Constant, message, exception or an object representing the rejection reason.
13444 * @returns {Promise} Returns a promise that was already resolved as rejected with the `reason`.
13446 var reject = function(reason) {
13447 var result = new Deferred();
13448 result.reject(reason);
13449 return result.promise;
13452 var makePromise = function makePromise(value, resolved) {
13453 var result = new Deferred();
13455 result.resolve(value);
13457 result.reject(value);
13459 return result.promise;
13462 var handleCallback = function handleCallback(value, isResolved, callback) {
13463 var callbackOutput = null;
13465 if (isFunction(callback)) callbackOutput = callback();
13467 return makePromise(e, false);
13469 if (isPromiseLike(callbackOutput)) {
13470 return callbackOutput.then(function() {
13471 return makePromise(value, isResolved);
13472 }, function(error) {
13473 return makePromise(error, false);
13476 return makePromise(value, isResolved);
13486 * Wraps an object that might be a value or a (3rd party) then-able promise into a $q promise.
13487 * This is useful when you are dealing with an object that might or might not be a promise, or if
13488 * the promise comes from a source that can't be trusted.
13490 * @param {*} value Value or a promise
13491 * @returns {Promise} Returns a promise of the passed value or promise
13495 var when = function(value, callback, errback, progressBack) {
13496 var result = new Deferred();
13497 result.resolve(value);
13498 return result.promise.then(callback, errback, progressBack);
13507 * Combines multiple promises into a single promise that is resolved when all of the input
13508 * promises are resolved.
13510 * @param {Array.<Promise>|Object.<Promise>} promises An array or hash of promises.
13511 * @returns {Promise} Returns a single promise that will be resolved with an array/hash of values,
13512 * each value corresponding to the promise at the same index/key in the `promises` array/hash.
13513 * If any of the promises is resolved with a rejection, this resulting promise will be rejected
13514 * with the same rejection value.
13517 function all(promises) {
13518 var deferred = new Deferred(),
13520 results = isArray(promises) ? [] : {};
13522 forEach(promises, function(promise, key) {
13524 when(promise).then(function(value) {
13525 if (results.hasOwnProperty(key)) return;
13526 results[key] = value;
13527 if (!(--counter)) deferred.resolve(results);
13528 }, function(reason) {
13529 if (results.hasOwnProperty(key)) return;
13530 deferred.reject(reason);
13534 if (counter === 0) {
13535 deferred.resolve(results);
13538 return deferred.promise;
13541 var $Q = function Q(resolver) {
13542 if (!isFunction(resolver)) {
13543 throw $qMinErr('norslvr', "Expected resolverFn, got '{0}'", resolver);
13546 if (!(this instanceof Q)) {
13547 // More useful when $Q is the Promise itself.
13548 return new Q(resolver);
13551 var deferred = new Deferred();
13553 function resolveFn(value) {
13554 deferred.resolve(value);
13557 function rejectFn(reason) {
13558 deferred.reject(reason);
13561 resolver(resolveFn, rejectFn);
13563 return deferred.promise;
13567 $Q.reject = reject;
13574 function $$RAFProvider() { //rAF
13575 this.$get = ['$window', '$timeout', function($window, $timeout) {
13576 var requestAnimationFrame = $window.requestAnimationFrame ||
13577 $window.webkitRequestAnimationFrame;
13579 var cancelAnimationFrame = $window.cancelAnimationFrame ||
13580 $window.webkitCancelAnimationFrame ||
13581 $window.webkitCancelRequestAnimationFrame;
13583 var rafSupported = !!requestAnimationFrame;
13584 var rafFn = rafSupported
13586 var id = requestAnimationFrame(fn);
13587 return function() {
13588 cancelAnimationFrame(id);
13592 var timer = $timeout(fn, 16.66, false); // 1000 / 60 = 16.666
13593 return function() {
13594 $timeout.cancel(timer);
13598 queueFn.supported = rafSupported;
13602 var taskQueue = [];
13606 for (var i = 0; i < taskQueue.length; i++) {
13607 var task = taskQueue[i];
13609 taskQueue[i] = null;
13613 taskCount = taskQueue.length = 0;
13616 function queueFn(asyncFn) {
13617 var index = taskQueue.length;
13620 taskQueue.push(asyncFn);
13623 cancelLastRAF = rafFn(flush);
13626 return function cancelQueueFn() {
13628 taskQueue[index] = null;
13631 if (--taskCount === 0 && cancelLastRAF) {
13633 cancelLastRAF = null;
13634 taskQueue.length = 0;
13645 * The design decisions behind the scope are heavily favored for speed and memory consumption.
13647 * The typical use of scope is to watch the expressions, which most of the time return the same
13648 * value as last time so we optimize the operation.
13650 * Closures construction is expensive in terms of speed as well as memory:
13651 * - No closures, instead use prototypical inheritance for API
13652 * - Internal state needs to be stored on scope directly, which means that private state is
13653 * exposed as $$____ properties
13655 * Loop operations are optimized by using while(count--) { ... }
13656 * - this means that in order to keep the same order of execution as addition we have to add
13657 * items to the array at the beginning (unshift) instead of at the end (push)
13659 * Child scopes are created and removed often
13660 * - Using an array would be slow since inserts in middle are expensive so we use linked list
13662 * There are few watches then a lot of observers. This is why you don't want the observer to be
13663 * implemented in the same way as watch. Watch requires return of initialization function which
13664 * are expensive to construct.
13670 * @name $rootScopeProvider
13673 * Provider for the $rootScope service.
13678 * @name $rootScopeProvider#digestTtl
13681 * Sets the number of `$digest` iterations the scope should attempt to execute before giving up and
13682 * assuming that the model is unstable.
13684 * The current default is 10 iterations.
13686 * In complex applications it's possible that the dependencies between `$watch`s will result in
13687 * several digest iterations. However if an application needs more than the default 10 digest
13688 * iterations for its model to stabilize then you should investigate what is causing the model to
13689 * continuously change during the digest.
13691 * Increasing the TTL could have performance implications, so you should not change it without
13692 * proper justification.
13694 * @param {number} limit The number of digest iterations.
13703 * Every application has a single root {@link ng.$rootScope.Scope scope}.
13704 * All other scopes are descendant scopes of the root scope. Scopes provide separation
13705 * between the model and the view, via a mechanism for watching the model for changes.
13706 * They also provide an event emission/broadcast and subscription facility. See the
13707 * {@link guide/scope developer guide on scopes}.
13709 function $RootScopeProvider() {
13711 var $rootScopeMinErr = minErr('$rootScope');
13712 var lastDirtyWatch = null;
13713 var applyAsyncId = null;
13715 this.digestTtl = function(value) {
13716 if (arguments.length) {
13722 function createChildScopeClass(parent) {
13723 function ChildScope() {
13724 this.$$watchers = this.$$nextSibling =
13725 this.$$childHead = this.$$childTail = null;
13726 this.$$listeners = {};
13727 this.$$listenerCount = {};
13728 this.$id = nextUid();
13729 this.$$ChildScope = null;
13731 ChildScope.prototype = parent;
13735 this.$get = ['$injector', '$exceptionHandler', '$parse', '$browser',
13736 function($injector, $exceptionHandler, $parse, $browser) {
13738 function destroyChildScope($event) {
13739 $event.currentScope.$$destroyed = true;
13744 * @name $rootScope.Scope
13747 * A root scope can be retrieved using the {@link ng.$rootScope $rootScope} key from the
13748 * {@link auto.$injector $injector}. Child scopes are created using the
13749 * {@link ng.$rootScope.Scope#$new $new()} method. (Most scopes are created automatically when
13750 * compiled HTML template is executed.) See also the {@link guide/scope Scopes guide} for
13751 * an in-depth introduction and usage examples.
13755 * A scope can inherit from a parent scope, as in this example:
13757 var parent = $rootScope;
13758 var child = parent.$new();
13760 parent.salutation = "Hello";
13761 expect(child.salutation).toEqual('Hello');
13763 child.salutation = "Welcome";
13764 expect(child.salutation).toEqual('Welcome');
13765 expect(parent.salutation).toEqual('Hello');
13768 * When interacting with `Scope` in tests, additional helper methods are available on the
13769 * instances of `Scope` type. See {@link ngMock.$rootScope.Scope ngMock Scope} for additional
13773 * @param {Object.<string, function()>=} providers Map of service factory which need to be
13774 * provided for the current scope. Defaults to {@link ng}.
13775 * @param {Object.<string, *>=} instanceCache Provides pre-instantiated services which should
13776 * append/override services provided by `providers`. This is handy
13777 * when unit-testing and having the need to override a default
13779 * @returns {Object} Newly created scope.
13783 this.$id = nextUid();
13784 this.$$phase = this.$parent = this.$$watchers =
13785 this.$$nextSibling = this.$$prevSibling =
13786 this.$$childHead = this.$$childTail = null;
13788 this.$$destroyed = false;
13789 this.$$listeners = {};
13790 this.$$listenerCount = {};
13791 this.$$isolateBindings = null;
13796 * @name $rootScope.Scope#$id
13799 * Unique scope ID (monotonically increasing) useful for debugging.
13804 * @name $rootScope.Scope#$parent
13807 * Reference to the parent scope.
13812 * @name $rootScope.Scope#$root
13815 * Reference to the root scope.
13818 Scope.prototype = {
13819 constructor: Scope,
13822 * @name $rootScope.Scope#$new
13826 * Creates a new child {@link ng.$rootScope.Scope scope}.
13828 * The parent scope will propagate the {@link ng.$rootScope.Scope#$digest $digest()} event.
13829 * The scope can be removed from the scope hierarchy using {@link ng.$rootScope.Scope#$destroy $destroy()}.
13831 * {@link ng.$rootScope.Scope#$destroy $destroy()} must be called on a scope when it is
13832 * desired for the scope and its child scopes to be permanently detached from the parent and
13833 * thus stop participating in model change detection and listener notification by invoking.
13835 * @param {boolean} isolate If true, then the scope does not prototypically inherit from the
13836 * parent scope. The scope is isolated, as it can not see parent scope properties.
13837 * When creating widgets, it is useful for the widget to not accidentally read parent
13840 * @param {Scope} [parent=this] The {@link ng.$rootScope.Scope `Scope`} that will be the `$parent`
13841 * of the newly created scope. Defaults to `this` scope if not provided.
13842 * This is used when creating a transclude scope to correctly place it
13843 * in the scope hierarchy while maintaining the correct prototypical
13846 * @returns {Object} The newly created child scope.
13849 $new: function(isolate, parent) {
13852 parent = parent || this;
13855 child = new Scope();
13856 child.$root = this.$root;
13858 // Only create a child scope class if somebody asks for one,
13859 // but cache it to allow the VM to optimize lookups.
13860 if (!this.$$ChildScope) {
13861 this.$$ChildScope = createChildScopeClass(this);
13863 child = new this.$$ChildScope();
13865 child.$parent = parent;
13866 child.$$prevSibling = parent.$$childTail;
13867 if (parent.$$childHead) {
13868 parent.$$childTail.$$nextSibling = child;
13869 parent.$$childTail = child;
13871 parent.$$childHead = parent.$$childTail = child;
13874 // When the new scope is not isolated or we inherit from `this`, and
13875 // the parent scope is destroyed, the property `$$destroyed` is inherited
13876 // prototypically. In all other cases, this property needs to be set
13877 // when the parent scope is destroyed.
13878 // The listener needs to be added after the parent is set
13879 if (isolate || parent != this) child.$on('$destroy', destroyChildScope);
13886 * @name $rootScope.Scope#$watch
13890 * Registers a `listener` callback to be executed whenever the `watchExpression` changes.
13892 * - The `watchExpression` is called on every call to {@link ng.$rootScope.Scope#$digest
13893 * $digest()} and should return the value that will be watched. (Since
13894 * {@link ng.$rootScope.Scope#$digest $digest()} reruns when it detects changes the
13895 * `watchExpression` can execute multiple times per
13896 * {@link ng.$rootScope.Scope#$digest $digest()} and should be idempotent.)
13897 * - The `listener` is called only when the value from the current `watchExpression` and the
13898 * previous call to `watchExpression` are not equal (with the exception of the initial run,
13899 * see below). Inequality is determined according to reference inequality,
13900 * [strict comparison](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators)
13901 * via the `!==` Javascript operator, unless `objectEquality == true`
13903 * - When `objectEquality == true`, inequality of the `watchExpression` is determined
13904 * according to the {@link angular.equals} function. To save the value of the object for
13905 * later comparison, the {@link angular.copy} function is used. This therefore means that
13906 * watching complex objects will have adverse memory and performance implications.
13907 * - The watch `listener` may change the model, which may trigger other `listener`s to fire.
13908 * This is achieved by rerunning the watchers until no changes are detected. The rerun
13909 * iteration limit is 10 to prevent an infinite loop deadlock.
13912 * If you want to be notified whenever {@link ng.$rootScope.Scope#$digest $digest} is called,
13913 * you can register a `watchExpression` function with no `listener`. (Be prepared for
13914 * multiple calls to your `watchExpression` because it will execute multiple times in a
13915 * single {@link ng.$rootScope.Scope#$digest $digest} cycle if a change is detected.)
13917 * After a watcher is registered with the scope, the `listener` fn is called asynchronously
13918 * (via {@link ng.$rootScope.Scope#$evalAsync $evalAsync}) to initialize the
13919 * watcher. In rare cases, this is undesirable because the listener is called when the result
13920 * of `watchExpression` didn't change. To detect this scenario within the `listener` fn, you
13921 * can compare the `newVal` and `oldVal`. If these two values are identical (`===`) then the
13922 * listener was called due to initialization.
13928 // let's assume that scope was dependency injected as the $rootScope
13929 var scope = $rootScope;
13930 scope.name = 'misko';
13933 expect(scope.counter).toEqual(0);
13934 scope.$watch('name', function(newValue, oldValue) {
13935 scope.counter = scope.counter + 1;
13937 expect(scope.counter).toEqual(0);
13940 // the listener is always called during the first $digest loop after it was registered
13941 expect(scope.counter).toEqual(1);
13944 // but now it will not be called unless the value changes
13945 expect(scope.counter).toEqual(1);
13947 scope.name = 'adam';
13949 expect(scope.counter).toEqual(2);
13953 // Using a function as a watchExpression
13955 scope.foodCounter = 0;
13956 expect(scope.foodCounter).toEqual(0);
13958 // This function returns the value being watched. It is called for each turn of the $digest loop
13959 function() { return food; },
13960 // This is the change listener, called when the value returned from the above function changes
13961 function(newValue, oldValue) {
13962 if ( newValue !== oldValue ) {
13963 // Only increment the counter if the value changed
13964 scope.foodCounter = scope.foodCounter + 1;
13968 // No digest has been run so the counter will be zero
13969 expect(scope.foodCounter).toEqual(0);
13971 // Run the digest but since food has not changed count will still be zero
13973 expect(scope.foodCounter).toEqual(0);
13975 // Update food and run digest. Now the counter will increment
13976 food = 'cheeseburger';
13978 expect(scope.foodCounter).toEqual(1);
13984 * @param {(function()|string)} watchExpression Expression that is evaluated on each
13985 * {@link ng.$rootScope.Scope#$digest $digest} cycle. A change in the return value triggers
13986 * a call to the `listener`.
13988 * - `string`: Evaluated as {@link guide/expression expression}
13989 * - `function(scope)`: called with current `scope` as a parameter.
13990 * @param {function(newVal, oldVal, scope)} listener Callback called whenever the value
13991 * of `watchExpression` changes.
13993 * - `newVal` contains the current value of the `watchExpression`
13994 * - `oldVal` contains the previous value of the `watchExpression`
13995 * - `scope` refers to the current scope
13996 * @param {boolean=} objectEquality Compare for object equality using {@link angular.equals} instead of
13997 * comparing for reference equality.
13998 * @returns {function()} Returns a deregistration function for this listener.
14000 $watch: function(watchExp, listener, objectEquality) {
14001 var get = $parse(watchExp);
14003 if (get.$$watchDelegate) {
14004 return get.$$watchDelegate(this, listener, objectEquality, get);
14007 array = scope.$$watchers,
14010 last: initWatchVal,
14013 eq: !!objectEquality
14016 lastDirtyWatch = null;
14018 if (!isFunction(listener)) {
14023 array = scope.$$watchers = [];
14025 // we use unshift since we use a while loop in $digest for speed.
14026 // the while loop reads in reverse order.
14027 array.unshift(watcher);
14029 return function deregisterWatch() {
14030 arrayRemove(array, watcher);
14031 lastDirtyWatch = null;
14037 * @name $rootScope.Scope#$watchGroup
14041 * A variant of {@link ng.$rootScope.Scope#$watch $watch()} where it watches an array of `watchExpressions`.
14042 * If any one expression in the collection changes the `listener` is executed.
14044 * - The items in the `watchExpressions` array are observed via standard $watch operation and are examined on every
14045 * call to $digest() to see if any items changes.
14046 * - The `listener` is called whenever any expression in the `watchExpressions` array changes.
14048 * @param {Array.<string|Function(scope)>} watchExpressions Array of expressions that will be individually
14049 * watched using {@link ng.$rootScope.Scope#$watch $watch()}
14051 * @param {function(newValues, oldValues, scope)} listener Callback called whenever the return value of any
14052 * expression in `watchExpressions` changes
14053 * The `newValues` array contains the current values of the `watchExpressions`, with the indexes matching
14054 * those of `watchExpression`
14055 * and the `oldValues` array contains the previous values of the `watchExpressions`, with the indexes matching
14056 * those of `watchExpression`
14057 * The `scope` refers to the current scope.
14058 * @returns {function()} Returns a de-registration function for all listeners.
14060 $watchGroup: function(watchExpressions, listener) {
14061 var oldValues = new Array(watchExpressions.length);
14062 var newValues = new Array(watchExpressions.length);
14063 var deregisterFns = [];
14065 var changeReactionScheduled = false;
14066 var firstRun = true;
14068 if (!watchExpressions.length) {
14069 // No expressions means we call the listener ASAP
14070 var shouldCall = true;
14071 self.$evalAsync(function() {
14072 if (shouldCall) listener(newValues, newValues, self);
14074 return function deregisterWatchGroup() {
14075 shouldCall = false;
14079 if (watchExpressions.length === 1) {
14080 // Special case size of one
14081 return this.$watch(watchExpressions[0], function watchGroupAction(value, oldValue, scope) {
14082 newValues[0] = value;
14083 oldValues[0] = oldValue;
14084 listener(newValues, (value === oldValue) ? newValues : oldValues, scope);
14088 forEach(watchExpressions, function(expr, i) {
14089 var unwatchFn = self.$watch(expr, function watchGroupSubAction(value, oldValue) {
14090 newValues[i] = value;
14091 oldValues[i] = oldValue;
14092 if (!changeReactionScheduled) {
14093 changeReactionScheduled = true;
14094 self.$evalAsync(watchGroupAction);
14097 deregisterFns.push(unwatchFn);
14100 function watchGroupAction() {
14101 changeReactionScheduled = false;
14105 listener(newValues, newValues, self);
14107 listener(newValues, oldValues, self);
14111 return function deregisterWatchGroup() {
14112 while (deregisterFns.length) {
14113 deregisterFns.shift()();
14121 * @name $rootScope.Scope#$watchCollection
14125 * Shallow watches the properties of an object and fires whenever any of the properties change
14126 * (for arrays, this implies watching the array items; for object maps, this implies watching
14127 * the properties). If a change is detected, the `listener` callback is fired.
14129 * - The `obj` collection is observed via standard $watch operation and is examined on every
14130 * call to $digest() to see if any items have been added, removed, or moved.
14131 * - The `listener` is called whenever anything within the `obj` has changed. Examples include
14132 * adding, removing, and moving items belonging to an object or array.
14137 $scope.names = ['igor', 'matias', 'misko', 'james'];
14138 $scope.dataCount = 4;
14140 $scope.$watchCollection('names', function(newNames, oldNames) {
14141 $scope.dataCount = newNames.length;
14144 expect($scope.dataCount).toEqual(4);
14147 //still at 4 ... no changes
14148 expect($scope.dataCount).toEqual(4);
14150 $scope.names.pop();
14153 //now there's been a change
14154 expect($scope.dataCount).toEqual(3);
14158 * @param {string|function(scope)} obj Evaluated as {@link guide/expression expression}. The
14159 * expression value should evaluate to an object or an array which is observed on each
14160 * {@link ng.$rootScope.Scope#$digest $digest} cycle. Any shallow change within the
14161 * collection will trigger a call to the `listener`.
14163 * @param {function(newCollection, oldCollection, scope)} listener a callback function called
14164 * when a change is detected.
14165 * - The `newCollection` object is the newly modified data obtained from the `obj` expression
14166 * - The `oldCollection` object is a copy of the former collection data.
14167 * Due to performance considerations, the`oldCollection` value is computed only if the
14168 * `listener` function declares two or more arguments.
14169 * - The `scope` argument refers to the current scope.
14171 * @returns {function()} Returns a de-registration function for this listener. When the
14172 * de-registration function is executed, the internal watch operation is terminated.
14174 $watchCollection: function(obj, listener) {
14175 $watchCollectionInterceptor.$stateful = true;
14178 // the current value, updated on each dirty-check run
14180 // a shallow copy of the newValue from the last dirty-check run,
14181 // updated to match newValue during dirty-check run
14183 // a shallow copy of the newValue from when the last change happened
14185 // only track veryOldValue if the listener is asking for it
14186 var trackVeryOldValue = (listener.length > 1);
14187 var changeDetected = 0;
14188 var changeDetector = $parse(obj, $watchCollectionInterceptor);
14189 var internalArray = [];
14190 var internalObject = {};
14191 var initRun = true;
14194 function $watchCollectionInterceptor(_value) {
14196 var newLength, key, bothNaN, newItem, oldItem;
14198 // If the new value is undefined, then return undefined as the watch may be a one-time watch
14199 if (isUndefined(newValue)) return;
14201 if (!isObject(newValue)) { // if primitive
14202 if (oldValue !== newValue) {
14203 oldValue = newValue;
14206 } else if (isArrayLike(newValue)) {
14207 if (oldValue !== internalArray) {
14208 // we are transitioning from something which was not an array into array.
14209 oldValue = internalArray;
14210 oldLength = oldValue.length = 0;
14214 newLength = newValue.length;
14216 if (oldLength !== newLength) {
14217 // if lengths do not match we need to trigger change notification
14219 oldValue.length = oldLength = newLength;
14221 // copy the items to oldValue and look for changes.
14222 for (var i = 0; i < newLength; i++) {
14223 oldItem = oldValue[i];
14224 newItem = newValue[i];
14226 bothNaN = (oldItem !== oldItem) && (newItem !== newItem);
14227 if (!bothNaN && (oldItem !== newItem)) {
14229 oldValue[i] = newItem;
14233 if (oldValue !== internalObject) {
14234 // we are transitioning from something which was not an object into object.
14235 oldValue = internalObject = {};
14239 // copy the items to oldValue and look for changes.
14241 for (key in newValue) {
14242 if (newValue.hasOwnProperty(key)) {
14244 newItem = newValue[key];
14245 oldItem = oldValue[key];
14247 if (key in oldValue) {
14248 bothNaN = (oldItem !== oldItem) && (newItem !== newItem);
14249 if (!bothNaN && (oldItem !== newItem)) {
14251 oldValue[key] = newItem;
14255 oldValue[key] = newItem;
14260 if (oldLength > newLength) {
14261 // we used to have more keys, need to find them and destroy them.
14263 for (key in oldValue) {
14264 if (!newValue.hasOwnProperty(key)) {
14266 delete oldValue[key];
14271 return changeDetected;
14274 function $watchCollectionAction() {
14277 listener(newValue, newValue, self);
14279 listener(newValue, veryOldValue, self);
14282 // make a copy for the next time a collection is changed
14283 if (trackVeryOldValue) {
14284 if (!isObject(newValue)) {
14286 veryOldValue = newValue;
14287 } else if (isArrayLike(newValue)) {
14288 veryOldValue = new Array(newValue.length);
14289 for (var i = 0; i < newValue.length; i++) {
14290 veryOldValue[i] = newValue[i];
14292 } else { // if object
14294 for (var key in newValue) {
14295 if (hasOwnProperty.call(newValue, key)) {
14296 veryOldValue[key] = newValue[key];
14303 return this.$watch(changeDetector, $watchCollectionAction);
14308 * @name $rootScope.Scope#$digest
14312 * Processes all of the {@link ng.$rootScope.Scope#$watch watchers} of the current scope and
14313 * its children. Because a {@link ng.$rootScope.Scope#$watch watcher}'s listener can change
14314 * the model, the `$digest()` keeps calling the {@link ng.$rootScope.Scope#$watch watchers}
14315 * until no more listeners are firing. This means that it is possible to get into an infinite
14316 * loop. This function will throw `'Maximum iteration limit exceeded.'` if the number of
14317 * iterations exceeds 10.
14319 * Usually, you don't call `$digest()` directly in
14320 * {@link ng.directive:ngController controllers} or in
14321 * {@link ng.$compileProvider#directive directives}.
14322 * Instead, you should call {@link ng.$rootScope.Scope#$apply $apply()} (typically from within
14323 * a {@link ng.$compileProvider#directive directive}), which will force a `$digest()`.
14325 * If you want to be notified whenever `$digest()` is called,
14326 * you can register a `watchExpression` function with
14327 * {@link ng.$rootScope.Scope#$watch $watch()} with no `listener`.
14329 * In unit tests, you may need to call `$digest()` to simulate the scope life cycle.
14334 scope.name = 'misko';
14337 expect(scope.counter).toEqual(0);
14338 scope.$watch('name', function(newValue, oldValue) {
14339 scope.counter = scope.counter + 1;
14341 expect(scope.counter).toEqual(0);
14344 // the listener is always called during the first $digest loop after it was registered
14345 expect(scope.counter).toEqual(1);
14348 // but now it will not be called unless the value changes
14349 expect(scope.counter).toEqual(1);
14351 scope.name = 'adam';
14353 expect(scope.counter).toEqual(2);
14357 $digest: function() {
14358 var watch, value, last,
14362 next, current, target = this,
14364 logIdx, logMsg, asyncTask;
14366 beginPhase('$digest');
14367 // Check for changes to browser url that happened in sync before the call to $digest
14368 $browser.$$checkUrlChange();
14370 if (this === $rootScope && applyAsyncId !== null) {
14371 // If this is the root scope, and $applyAsync has scheduled a deferred $apply(), then
14372 // cancel the scheduled $apply and flush the queue of expressions to be evaluated.
14373 $browser.defer.cancel(applyAsyncId);
14377 lastDirtyWatch = null;
14379 do { // "while dirty" loop
14383 while (asyncQueue.length) {
14385 asyncTask = asyncQueue.shift();
14386 asyncTask.scope.$eval(asyncTask.expression, asyncTask.locals);
14388 $exceptionHandler(e);
14390 lastDirtyWatch = null;
14393 traverseScopesLoop:
14394 do { // "traverse the scopes" loop
14395 if ((watchers = current.$$watchers)) {
14396 // process our watches
14397 length = watchers.length;
14400 watch = watchers[length];
14401 // Most common watches are on primitives, in which case we can short
14402 // circuit it with === operator, only when === fails do we use .equals
14404 if ((value = watch.get(current)) !== (last = watch.last) &&
14406 ? equals(value, last)
14407 : (typeof value === 'number' && typeof last === 'number'
14408 && isNaN(value) && isNaN(last)))) {
14410 lastDirtyWatch = watch;
14411 watch.last = watch.eq ? copy(value, null) : value;
14412 watch.fn(value, ((last === initWatchVal) ? value : last), current);
14415 if (!watchLog[logIdx]) watchLog[logIdx] = [];
14416 watchLog[logIdx].push({
14417 msg: isFunction(watch.exp) ? 'fn: ' + (watch.exp.name || watch.exp.toString()) : watch.exp,
14422 } else if (watch === lastDirtyWatch) {
14423 // If the most recently dirty watcher is now clean, short circuit since the remaining watchers
14424 // have already been tested.
14426 break traverseScopesLoop;
14430 $exceptionHandler(e);
14435 // Insanity Warning: scope depth-first traversal
14436 // yes, this code is a bit crazy, but it works and we have tests to prove it!
14437 // this piece should be kept in sync with the traversal in $broadcast
14438 if (!(next = (current.$$childHead ||
14439 (current !== target && current.$$nextSibling)))) {
14440 while (current !== target && !(next = current.$$nextSibling)) {
14441 current = current.$parent;
14444 } while ((current = next));
14446 // `break traverseScopesLoop;` takes us to here
14448 if ((dirty || asyncQueue.length) && !(ttl--)) {
14450 throw $rootScopeMinErr('infdig',
14451 '{0} $digest() iterations reached. Aborting!\n' +
14452 'Watchers fired in the last 5 iterations: {1}',
14456 } while (dirty || asyncQueue.length);
14460 while (postDigestQueue.length) {
14462 postDigestQueue.shift()();
14464 $exceptionHandler(e);
14472 * @name $rootScope.Scope#$destroy
14473 * @eventType broadcast on scope being destroyed
14476 * Broadcasted when a scope and its children are being destroyed.
14478 * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to
14479 * clean up DOM bindings before an element is removed from the DOM.
14484 * @name $rootScope.Scope#$destroy
14488 * Removes the current scope (and all of its children) from the parent scope. Removal implies
14489 * that calls to {@link ng.$rootScope.Scope#$digest $digest()} will no longer
14490 * propagate to the current scope and its children. Removal also implies that the current
14491 * scope is eligible for garbage collection.
14493 * The `$destroy()` is usually used by directives such as
14494 * {@link ng.directive:ngRepeat ngRepeat} for managing the
14495 * unrolling of the loop.
14497 * Just before a scope is destroyed, a `$destroy` event is broadcasted on this scope.
14498 * Application code can register a `$destroy` event handler that will give it a chance to
14499 * perform any necessary cleanup.
14501 * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to
14502 * clean up DOM bindings before an element is removed from the DOM.
14504 $destroy: function() {
14505 // we can't destroy the root scope or a scope that has been already destroyed
14506 if (this.$$destroyed) return;
14507 var parent = this.$parent;
14509 this.$broadcast('$destroy');
14510 this.$$destroyed = true;
14511 if (this === $rootScope) return;
14513 for (var eventName in this.$$listenerCount) {
14514 decrementListenerCount(this, this.$$listenerCount[eventName], eventName);
14517 // sever all the references to parent scopes (after this cleanup, the current scope should
14518 // not be retained by any of our references and should be eligible for garbage collection)
14519 if (parent.$$childHead == this) parent.$$childHead = this.$$nextSibling;
14520 if (parent.$$childTail == this) parent.$$childTail = this.$$prevSibling;
14521 if (this.$$prevSibling) this.$$prevSibling.$$nextSibling = this.$$nextSibling;
14522 if (this.$$nextSibling) this.$$nextSibling.$$prevSibling = this.$$prevSibling;
14524 // Disable listeners, watchers and apply/digest methods
14525 this.$destroy = this.$digest = this.$apply = this.$evalAsync = this.$applyAsync = noop;
14526 this.$on = this.$watch = this.$watchGroup = function() { return noop; };
14527 this.$$listeners = {};
14529 // All of the code below is bogus code that works around V8's memory leak via optimized code
14530 // and inline caches.
14533 // - https://code.google.com/p/v8/issues/detail?id=2073#c26
14534 // - https://github.com/angular/angular.js/issues/6794#issuecomment-38648909
14535 // - https://github.com/angular/angular.js/issues/1313#issuecomment-10378451
14537 this.$parent = this.$$nextSibling = this.$$prevSibling = this.$$childHead =
14538 this.$$childTail = this.$root = this.$$watchers = null;
14543 * @name $rootScope.Scope#$eval
14547 * Executes the `expression` on the current scope and returns the result. Any exceptions in
14548 * the expression are propagated (uncaught). This is useful when evaluating Angular
14553 var scope = ng.$rootScope.Scope();
14557 expect(scope.$eval('a+b')).toEqual(3);
14558 expect(scope.$eval(function(scope){ return scope.a + scope.b; })).toEqual(3);
14561 * @param {(string|function())=} expression An angular expression to be executed.
14563 * - `string`: execute using the rules as defined in {@link guide/expression expression}.
14564 * - `function(scope)`: execute the function with the current `scope` parameter.
14566 * @param {(object)=} locals Local variables object, useful for overriding values in scope.
14567 * @returns {*} The result of evaluating the expression.
14569 $eval: function(expr, locals) {
14570 return $parse(expr)(this, locals);
14575 * @name $rootScope.Scope#$evalAsync
14579 * Executes the expression on the current scope at a later point in time.
14581 * The `$evalAsync` makes no guarantees as to when the `expression` will be executed, only
14584 * - it will execute after the function that scheduled the evaluation (preferably before DOM
14586 * - at least one {@link ng.$rootScope.Scope#$digest $digest cycle} will be performed after
14587 * `expression` execution.
14589 * Any exceptions from the execution of the expression are forwarded to the
14590 * {@link ng.$exceptionHandler $exceptionHandler} service.
14592 * __Note:__ if this function is called outside of a `$digest` cycle, a new `$digest` cycle
14593 * will be scheduled. However, it is encouraged to always call code that changes the model
14594 * from within an `$apply` call. That includes code evaluated via `$evalAsync`.
14596 * @param {(string|function())=} expression An angular expression to be executed.
14598 * - `string`: execute using the rules as defined in {@link guide/expression expression}.
14599 * - `function(scope)`: execute the function with the current `scope` parameter.
14601 * @param {(object)=} locals Local variables object, useful for overriding values in scope.
14603 $evalAsync: function(expr, locals) {
14604 // if we are outside of an $digest loop and this is the first time we are scheduling async
14605 // task also schedule async auto-flush
14606 if (!$rootScope.$$phase && !asyncQueue.length) {
14607 $browser.defer(function() {
14608 if (asyncQueue.length) {
14609 $rootScope.$digest();
14614 asyncQueue.push({scope: this, expression: expr, locals: locals});
14617 $$postDigest: function(fn) {
14618 postDigestQueue.push(fn);
14623 * @name $rootScope.Scope#$apply
14627 * `$apply()` is used to execute an expression in angular from outside of the angular
14628 * framework. (For example from browser DOM events, setTimeout, XHR or third party libraries).
14629 * Because we are calling into the angular framework we need to perform proper scope life
14630 * cycle of {@link ng.$exceptionHandler exception handling},
14631 * {@link ng.$rootScope.Scope#$digest executing watches}.
14635 * # Pseudo-Code of `$apply()`
14637 function $apply(expr) {
14639 return $eval(expr);
14641 $exceptionHandler(e);
14649 * Scope's `$apply()` method transitions through the following stages:
14651 * 1. The {@link guide/expression expression} is executed using the
14652 * {@link ng.$rootScope.Scope#$eval $eval()} method.
14653 * 2. Any exceptions from the execution of the expression are forwarded to the
14654 * {@link ng.$exceptionHandler $exceptionHandler} service.
14655 * 3. The {@link ng.$rootScope.Scope#$watch watch} listeners are fired immediately after the
14656 * expression was executed using the {@link ng.$rootScope.Scope#$digest $digest()} method.
14659 * @param {(string|function())=} exp An angular expression to be executed.
14661 * - `string`: execute using the rules as defined in {@link guide/expression expression}.
14662 * - `function(scope)`: execute the function with current `scope` parameter.
14664 * @returns {*} The result of evaluating the expression.
14666 $apply: function(expr) {
14668 beginPhase('$apply');
14669 return this.$eval(expr);
14671 $exceptionHandler(e);
14675 $rootScope.$digest();
14677 $exceptionHandler(e);
14685 * @name $rootScope.Scope#$applyAsync
14689 * Schedule the invocation of $apply to occur at a later time. The actual time difference
14690 * varies across browsers, but is typically around ~10 milliseconds.
14692 * This can be used to queue up multiple expressions which need to be evaluated in the same
14695 * @param {(string|function())=} exp An angular expression to be executed.
14697 * - `string`: execute using the rules as defined in {@link guide/expression expression}.
14698 * - `function(scope)`: execute the function with current `scope` parameter.
14700 $applyAsync: function(expr) {
14702 expr && applyAsyncQueue.push($applyAsyncExpression);
14703 scheduleApplyAsync();
14705 function $applyAsyncExpression() {
14712 * @name $rootScope.Scope#$on
14716 * Listens on events of a given type. See {@link ng.$rootScope.Scope#$emit $emit} for
14717 * discussion of event life cycle.
14719 * The event listener function format is: `function(event, args...)`. The `event` object
14720 * passed into the listener has the following attributes:
14722 * - `targetScope` - `{Scope}`: the scope on which the event was `$emit`-ed or
14724 * - `currentScope` - `{Scope}`: the scope that is currently handling the event. Once the
14725 * event propagates through the scope hierarchy, this property is set to null.
14726 * - `name` - `{string}`: name of the event.
14727 * - `stopPropagation` - `{function=}`: calling `stopPropagation` function will cancel
14728 * further event propagation (available only for events that were `$emit`-ed).
14729 * - `preventDefault` - `{function}`: calling `preventDefault` sets `defaultPrevented` flag
14731 * - `defaultPrevented` - `{boolean}`: true if `preventDefault` was called.
14733 * @param {string} name Event name to listen on.
14734 * @param {function(event, ...args)} listener Function to call when the event is emitted.
14735 * @returns {function()} Returns a deregistration function for this listener.
14737 $on: function(name, listener) {
14738 var namedListeners = this.$$listeners[name];
14739 if (!namedListeners) {
14740 this.$$listeners[name] = namedListeners = [];
14742 namedListeners.push(listener);
14744 var current = this;
14746 if (!current.$$listenerCount[name]) {
14747 current.$$listenerCount[name] = 0;
14749 current.$$listenerCount[name]++;
14750 } while ((current = current.$parent));
14753 return function() {
14754 var indexOfListener = namedListeners.indexOf(listener);
14755 if (indexOfListener !== -1) {
14756 namedListeners[indexOfListener] = null;
14757 decrementListenerCount(self, 1, name);
14765 * @name $rootScope.Scope#$emit
14769 * Dispatches an event `name` upwards through the scope hierarchy notifying the
14770 * registered {@link ng.$rootScope.Scope#$on} listeners.
14772 * The event life cycle starts at the scope on which `$emit` was called. All
14773 * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get
14774 * notified. Afterwards, the event traverses upwards toward the root scope and calls all
14775 * registered listeners along the way. The event will stop propagating if one of the listeners
14778 * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
14779 * onto the {@link ng.$exceptionHandler $exceptionHandler} service.
14781 * @param {string} name Event name to emit.
14782 * @param {...*} args Optional one or more arguments which will be passed onto the event listeners.
14783 * @return {Object} Event object (see {@link ng.$rootScope.Scope#$on}).
14785 $emit: function(name, args) {
14789 stopPropagation = false,
14792 targetScope: scope,
14793 stopPropagation: function() {stopPropagation = true;},
14794 preventDefault: function() {
14795 event.defaultPrevented = true;
14797 defaultPrevented: false
14799 listenerArgs = concat([event], arguments, 1),
14803 namedListeners = scope.$$listeners[name] || empty;
14804 event.currentScope = scope;
14805 for (i = 0, length = namedListeners.length; i < length; i++) {
14807 // if listeners were deregistered, defragment the array
14808 if (!namedListeners[i]) {
14809 namedListeners.splice(i, 1);
14815 //allow all listeners attached to the current scope to run
14816 namedListeners[i].apply(null, listenerArgs);
14818 $exceptionHandler(e);
14821 //if any listener on the current scope stops propagation, prevent bubbling
14822 if (stopPropagation) {
14823 event.currentScope = null;
14827 scope = scope.$parent;
14830 event.currentScope = null;
14838 * @name $rootScope.Scope#$broadcast
14842 * Dispatches an event `name` downwards to all child scopes (and their children) notifying the
14843 * registered {@link ng.$rootScope.Scope#$on} listeners.
14845 * The event life cycle starts at the scope on which `$broadcast` was called. All
14846 * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get
14847 * notified. Afterwards, the event propagates to all direct and indirect scopes of the current
14848 * scope and calls all registered listeners along the way. The event cannot be canceled.
14850 * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
14851 * onto the {@link ng.$exceptionHandler $exceptionHandler} service.
14853 * @param {string} name Event name to broadcast.
14854 * @param {...*} args Optional one or more arguments which will be passed onto the event listeners.
14855 * @return {Object} Event object, see {@link ng.$rootScope.Scope#$on}
14857 $broadcast: function(name, args) {
14863 targetScope: target,
14864 preventDefault: function() {
14865 event.defaultPrevented = true;
14867 defaultPrevented: false
14870 if (!target.$$listenerCount[name]) return event;
14872 var listenerArgs = concat([event], arguments, 1),
14873 listeners, i, length;
14875 //down while you can, then up and next sibling or up and next sibling until back at root
14876 while ((current = next)) {
14877 event.currentScope = current;
14878 listeners = current.$$listeners[name] || [];
14879 for (i = 0, length = listeners.length; i < length; i++) {
14880 // if listeners were deregistered, defragment the array
14881 if (!listeners[i]) {
14882 listeners.splice(i, 1);
14889 listeners[i].apply(null, listenerArgs);
14891 $exceptionHandler(e);
14895 // Insanity Warning: scope depth-first traversal
14896 // yes, this code is a bit crazy, but it works and we have tests to prove it!
14897 // this piece should be kept in sync with the traversal in $digest
14898 // (though it differs due to having the extra check for $$listenerCount)
14899 if (!(next = ((current.$$listenerCount[name] && current.$$childHead) ||
14900 (current !== target && current.$$nextSibling)))) {
14901 while (current !== target && !(next = current.$$nextSibling)) {
14902 current = current.$parent;
14907 event.currentScope = null;
14912 var $rootScope = new Scope();
14914 //The internal queues. Expose them on the $rootScope for debugging/testing purposes.
14915 var asyncQueue = $rootScope.$$asyncQueue = [];
14916 var postDigestQueue = $rootScope.$$postDigestQueue = [];
14917 var applyAsyncQueue = $rootScope.$$applyAsyncQueue = [];
14922 function beginPhase(phase) {
14923 if ($rootScope.$$phase) {
14924 throw $rootScopeMinErr('inprog', '{0} already in progress', $rootScope.$$phase);
14927 $rootScope.$$phase = phase;
14930 function clearPhase() {
14931 $rootScope.$$phase = null;
14935 function decrementListenerCount(current, count, name) {
14937 current.$$listenerCount[name] -= count;
14939 if (current.$$listenerCount[name] === 0) {
14940 delete current.$$listenerCount[name];
14942 } while ((current = current.$parent));
14946 * function used as an initial value for watchers.
14947 * because it's unique we can easily tell it apart from other values
14949 function initWatchVal() {}
14951 function flushApplyAsync() {
14952 while (applyAsyncQueue.length) {
14954 applyAsyncQueue.shift()();
14956 $exceptionHandler(e);
14959 applyAsyncId = null;
14962 function scheduleApplyAsync() {
14963 if (applyAsyncId === null) {
14964 applyAsyncId = $browser.defer(function() {
14965 $rootScope.$apply(flushApplyAsync);
14974 * Private service to sanitize uris for links and images. Used by $compile and $sanitize.
14976 function $$SanitizeUriProvider() {
14977 var aHrefSanitizationWhitelist = /^\s*(https?|ftp|mailto|tel|file):/,
14978 imgSrcSanitizationWhitelist = /^\s*((https?|ftp|file|blob):|data:image\/)/;
14982 * Retrieves or overrides the default regular expression that is used for whitelisting of safe
14983 * urls during a[href] sanitization.
14985 * The sanitization is a security measure aimed at prevent XSS attacks via html links.
14987 * Any url about to be assigned to a[href] via data-binding is first normalized and turned into
14988 * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist`
14989 * regular expression. If a match is found, the original url is written into the dom. Otherwise,
14990 * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
14992 * @param {RegExp=} regexp New regexp to whitelist urls with.
14993 * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
14994 * chaining otherwise.
14996 this.aHrefSanitizationWhitelist = function(regexp) {
14997 if (isDefined(regexp)) {
14998 aHrefSanitizationWhitelist = regexp;
15001 return aHrefSanitizationWhitelist;
15007 * Retrieves or overrides the default regular expression that is used for whitelisting of safe
15008 * urls during img[src] sanitization.
15010 * The sanitization is a security measure aimed at prevent XSS attacks via html links.
15012 * Any url about to be assigned to img[src] via data-binding is first normalized and turned into
15013 * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist`
15014 * regular expression. If a match is found, the original url is written into the dom. Otherwise,
15015 * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
15017 * @param {RegExp=} regexp New regexp to whitelist urls with.
15018 * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
15019 * chaining otherwise.
15021 this.imgSrcSanitizationWhitelist = function(regexp) {
15022 if (isDefined(regexp)) {
15023 imgSrcSanitizationWhitelist = regexp;
15026 return imgSrcSanitizationWhitelist;
15029 this.$get = function() {
15030 return function sanitizeUri(uri, isImage) {
15031 var regex = isImage ? imgSrcSanitizationWhitelist : aHrefSanitizationWhitelist;
15033 normalizedVal = urlResolve(uri).href;
15034 if (normalizedVal !== '' && !normalizedVal.match(regex)) {
15035 return 'unsafe:' + normalizedVal;
15042 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
15043 * Any commits to this file should be reviewed with security in mind. *
15044 * Changes to this file can potentially create security vulnerabilities. *
15045 * An approval from 2 Core members with history of modifying *
15046 * this file is required. *
15048 * Does the change somehow allow for arbitrary javascript to be executed? *
15049 * Or allows for someone to change the prototype of built-in objects? *
15050 * Or gives undesired access to variables likes document or window? *
15051 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
15053 var $sceMinErr = minErr('$sce');
15055 var SCE_CONTEXTS = {
15059 // RESOURCE_URL is a subtype of URL used in contexts where a privileged resource is sourced from a
15060 // url. (e.g. ng-include, script src, templateUrl)
15061 RESOURCE_URL: 'resourceUrl',
15065 // Helper functions follow.
15067 function adjustMatcher(matcher) {
15068 if (matcher === 'self') {
15070 } else if (isString(matcher)) {
15071 // Strings match exactly except for 2 wildcards - '*' and '**'.
15072 // '*' matches any character except those from the set ':/.?&'.
15073 // '**' matches any character (like .* in a RegExp).
15074 // More than 2 *'s raises an error as it's ill defined.
15075 if (matcher.indexOf('***') > -1) {
15076 throw $sceMinErr('iwcard',
15077 'Illegal sequence *** in string matcher. String: {0}', matcher);
15079 matcher = escapeForRegexp(matcher).
15080 replace('\\*\\*', '.*').
15081 replace('\\*', '[^:/.?&;]*');
15082 return new RegExp('^' + matcher + '$');
15083 } else if (isRegExp(matcher)) {
15084 // The only other type of matcher allowed is a Regexp.
15085 // Match entire URL / disallow partial matches.
15086 // Flags are reset (i.e. no global, ignoreCase or multiline)
15087 return new RegExp('^' + matcher.source + '$');
15089 throw $sceMinErr('imatcher',
15090 'Matchers may only be "self", string patterns or RegExp objects');
15095 function adjustMatchers(matchers) {
15096 var adjustedMatchers = [];
15097 if (isDefined(matchers)) {
15098 forEach(matchers, function(matcher) {
15099 adjustedMatchers.push(adjustMatcher(matcher));
15102 return adjustedMatchers;
15108 * @name $sceDelegate
15113 * `$sceDelegate` is a service that is used by the `$sce` service to provide {@link ng.$sce Strict
15114 * Contextual Escaping (SCE)} services to AngularJS.
15116 * Typically, you would configure or override the {@link ng.$sceDelegate $sceDelegate} instead of
15117 * the `$sce` service to customize the way Strict Contextual Escaping works in AngularJS. This is
15118 * because, while the `$sce` provides numerous shorthand methods, etc., you really only need to
15119 * override 3 core functions (`trustAs`, `getTrusted` and `valueOf`) to replace the way things
15120 * work because `$sce` delegates to `$sceDelegate` for these operations.
15122 * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} to configure this service.
15124 * The default instance of `$sceDelegate` should work out of the box with little pain. While you
15125 * can override it completely to change the behavior of `$sce`, the common case would
15126 * involve configuring the {@link ng.$sceDelegateProvider $sceDelegateProvider} instead by setting
15127 * your own whitelists and blacklists for trusting URLs used for loading AngularJS resources such as
15128 * templates. Refer {@link ng.$sceDelegateProvider#resourceUrlWhitelist
15129 * $sceDelegateProvider.resourceUrlWhitelist} and {@link
15130 * ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist}
15135 * @name $sceDelegateProvider
15138 * The `$sceDelegateProvider` provider allows developers to configure the {@link ng.$sceDelegate
15139 * $sceDelegate} service. This allows one to get/set the whitelists and blacklists used to ensure
15140 * that the URLs used for sourcing Angular templates are safe. Refer {@link
15141 * ng.$sceDelegateProvider#resourceUrlWhitelist $sceDelegateProvider.resourceUrlWhitelist} and
15142 * {@link ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist}
15144 * For the general details about this service in Angular, read the main page for {@link ng.$sce
15145 * Strict Contextual Escaping (SCE)}.
15147 * **Example**: Consider the following case. <a name="example"></a>
15149 * - your app is hosted at url `http://myapp.example.com/`
15150 * - but some of your templates are hosted on other domains you control such as
15151 * `http://srv01.assets.example.com/`, `http://srv02.assets.example.com/`, etc.
15152 * - and you have an open redirect at `http://myapp.example.com/clickThru?...`.
15154 * Here is what a secure configuration for this scenario might look like:
15157 * angular.module('myApp', []).config(function($sceDelegateProvider) {
15158 * $sceDelegateProvider.resourceUrlWhitelist([
15159 * // Allow same origin resource loads.
15161 * // Allow loading from our assets domain. Notice the difference between * and **.
15162 * 'http://srv*.assets.example.com/**'
15165 * // The blacklist overrides the whitelist so the open redirect here is blocked.
15166 * $sceDelegateProvider.resourceUrlBlacklist([
15167 * 'http://myapp.example.com/clickThru**'
15173 function $SceDelegateProvider() {
15174 this.SCE_CONTEXTS = SCE_CONTEXTS;
15176 // Resource URLs can also be trusted by policy.
15177 var resourceUrlWhitelist = ['self'],
15178 resourceUrlBlacklist = [];
15182 * @name $sceDelegateProvider#resourceUrlWhitelist
15185 * @param {Array=} whitelist When provided, replaces the resourceUrlWhitelist with the value
15186 * provided. This must be an array or null. A snapshot of this array is used so further
15187 * changes to the array are ignored.
15189 * Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
15190 * allowed in this array.
15192 * Note: **an empty whitelist array will block all URLs**!
15194 * @return {Array} the currently set whitelist array.
15196 * The **default value** when no whitelist has been explicitly set is `['self']` allowing only
15197 * same origin resource requests.
15200 * Sets/Gets the whitelist of trusted resource URLs.
15202 this.resourceUrlWhitelist = function(value) {
15203 if (arguments.length) {
15204 resourceUrlWhitelist = adjustMatchers(value);
15206 return resourceUrlWhitelist;
15211 * @name $sceDelegateProvider#resourceUrlBlacklist
15214 * @param {Array=} blacklist When provided, replaces the resourceUrlBlacklist with the value
15215 * provided. This must be an array or null. A snapshot of this array is used so further
15216 * changes to the array are ignored.
15218 * Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
15219 * allowed in this array.
15221 * The typical usage for the blacklist is to **block
15222 * [open redirects](http://cwe.mitre.org/data/definitions/601.html)** served by your domain as
15223 * these would otherwise be trusted but actually return content from the redirected domain.
15225 * Finally, **the blacklist overrides the whitelist** and has the final say.
15227 * @return {Array} the currently set blacklist array.
15229 * The **default value** when no whitelist has been explicitly set is the empty array (i.e. there
15230 * is no blacklist.)
15233 * Sets/Gets the blacklist of trusted resource URLs.
15236 this.resourceUrlBlacklist = function(value) {
15237 if (arguments.length) {
15238 resourceUrlBlacklist = adjustMatchers(value);
15240 return resourceUrlBlacklist;
15243 this.$get = ['$injector', function($injector) {
15245 var htmlSanitizer = function htmlSanitizer(html) {
15246 throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.');
15249 if ($injector.has('$sanitize')) {
15250 htmlSanitizer = $injector.get('$sanitize');
15254 function matchUrl(matcher, parsedUrl) {
15255 if (matcher === 'self') {
15256 return urlIsSameOrigin(parsedUrl);
15258 // definitely a regex. See adjustMatchers()
15259 return !!matcher.exec(parsedUrl.href);
15263 function isResourceUrlAllowedByPolicy(url) {
15264 var parsedUrl = urlResolve(url.toString());
15265 var i, n, allowed = false;
15266 // Ensure that at least one item from the whitelist allows this url.
15267 for (i = 0, n = resourceUrlWhitelist.length; i < n; i++) {
15268 if (matchUrl(resourceUrlWhitelist[i], parsedUrl)) {
15274 // Ensure that no item from the blacklist blocked this url.
15275 for (i = 0, n = resourceUrlBlacklist.length; i < n; i++) {
15276 if (matchUrl(resourceUrlBlacklist[i], parsedUrl)) {
15285 function generateHolderType(Base) {
15286 var holderType = function TrustedValueHolderType(trustedValue) {
15287 this.$$unwrapTrustedValue = function() {
15288 return trustedValue;
15292 holderType.prototype = new Base();
15294 holderType.prototype.valueOf = function sceValueOf() {
15295 return this.$$unwrapTrustedValue();
15297 holderType.prototype.toString = function sceToString() {
15298 return this.$$unwrapTrustedValue().toString();
15303 var trustedValueHolderBase = generateHolderType(),
15306 byType[SCE_CONTEXTS.HTML] = generateHolderType(trustedValueHolderBase);
15307 byType[SCE_CONTEXTS.CSS] = generateHolderType(trustedValueHolderBase);
15308 byType[SCE_CONTEXTS.URL] = generateHolderType(trustedValueHolderBase);
15309 byType[SCE_CONTEXTS.JS] = generateHolderType(trustedValueHolderBase);
15310 byType[SCE_CONTEXTS.RESOURCE_URL] = generateHolderType(byType[SCE_CONTEXTS.URL]);
15314 * @name $sceDelegate#trustAs
15317 * Returns an object that is trusted by angular for use in specified strict
15318 * contextual escaping contexts (such as ng-bind-html, ng-include, any src
15319 * attribute interpolation, any dom event binding attribute interpolation
15320 * such as for onclick, etc.) that uses the provided value.
15321 * See {@link ng.$sce $sce} for enabling strict contextual escaping.
15323 * @param {string} type The kind of context in which this value is safe for use. e.g. url,
15324 * resourceUrl, html, js and css.
15325 * @param {*} value The value that that should be considered trusted/safe.
15326 * @returns {*} A value that can be used to stand in for the provided `value` in places
15327 * where Angular expects a $sce.trustAs() return value.
15329 function trustAs(type, trustedValue) {
15330 var Constructor = (byType.hasOwnProperty(type) ? byType[type] : null);
15331 if (!Constructor) {
15332 throw $sceMinErr('icontext',
15333 'Attempted to trust a value in invalid context. Context: {0}; Value: {1}',
15334 type, trustedValue);
15336 if (trustedValue === null || trustedValue === undefined || trustedValue === '') {
15337 return trustedValue;
15339 // All the current contexts in SCE_CONTEXTS happen to be strings. In order to avoid trusting
15340 // mutable objects, we ensure here that the value passed in is actually a string.
15341 if (typeof trustedValue !== 'string') {
15342 throw $sceMinErr('itype',
15343 'Attempted to trust a non-string value in a content requiring a string: Context: {0}',
15346 return new Constructor(trustedValue);
15351 * @name $sceDelegate#valueOf
15354 * If the passed parameter had been returned by a prior call to {@link ng.$sceDelegate#trustAs
15355 * `$sceDelegate.trustAs`}, returns the value that had been passed to {@link
15356 * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}.
15358 * If the passed parameter is not a value that had been returned by {@link
15359 * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}, returns it as-is.
15361 * @param {*} value The result of a prior {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}
15362 * call or anything else.
15363 * @returns {*} The `value` that was originally provided to {@link ng.$sceDelegate#trustAs
15364 * `$sceDelegate.trustAs`} if `value` is the result of such a call. Otherwise, returns
15365 * `value` unchanged.
15367 function valueOf(maybeTrusted) {
15368 if (maybeTrusted instanceof trustedValueHolderBase) {
15369 return maybeTrusted.$$unwrapTrustedValue();
15371 return maybeTrusted;
15377 * @name $sceDelegate#getTrusted
15380 * Takes the result of a {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`} call and
15381 * returns the originally supplied value if the queried context type is a supertype of the
15382 * created type. If this condition isn't satisfied, throws an exception.
15384 * @param {string} type The kind of context in which this value is to be used.
15385 * @param {*} maybeTrusted The result of a prior {@link ng.$sceDelegate#trustAs
15386 * `$sceDelegate.trustAs`} call.
15387 * @returns {*} The value the was originally provided to {@link ng.$sceDelegate#trustAs
15388 * `$sceDelegate.trustAs`} if valid in this context. Otherwise, throws an exception.
15390 function getTrusted(type, maybeTrusted) {
15391 if (maybeTrusted === null || maybeTrusted === undefined || maybeTrusted === '') {
15392 return maybeTrusted;
15394 var constructor = (byType.hasOwnProperty(type) ? byType[type] : null);
15395 if (constructor && maybeTrusted instanceof constructor) {
15396 return maybeTrusted.$$unwrapTrustedValue();
15398 // If we get here, then we may only take one of two actions.
15399 // 1. sanitize the value for the requested type, or
15400 // 2. throw an exception.
15401 if (type === SCE_CONTEXTS.RESOURCE_URL) {
15402 if (isResourceUrlAllowedByPolicy(maybeTrusted)) {
15403 return maybeTrusted;
15405 throw $sceMinErr('insecurl',
15406 'Blocked loading resource from url not allowed by $sceDelegate policy. URL: {0}',
15407 maybeTrusted.toString());
15409 } else if (type === SCE_CONTEXTS.HTML) {
15410 return htmlSanitizer(maybeTrusted);
15412 throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.');
15415 return { trustAs: trustAs,
15416 getTrusted: getTrusted,
15417 valueOf: valueOf };
15424 * @name $sceProvider
15427 * The $sceProvider provider allows developers to configure the {@link ng.$sce $sce} service.
15428 * - enable/disable Strict Contextual Escaping (SCE) in a module
15429 * - override the default implementation with a custom delegate
15431 * Read more about {@link ng.$sce Strict Contextual Escaping (SCE)}.
15434 /* jshint maxlen: false*/
15443 * `$sce` is a service that provides Strict Contextual Escaping services to AngularJS.
15445 * # Strict Contextual Escaping
15447 * Strict Contextual Escaping (SCE) is a mode in which AngularJS requires bindings in certain
15448 * contexts to result in a value that is marked as safe to use for that context. One example of
15449 * such a context is binding arbitrary html controlled by the user via `ng-bind-html`. We refer
15450 * to these contexts as privileged or SCE contexts.
15452 * As of version 1.2, Angular ships with SCE enabled by default.
15454 * Note: When enabled (the default), IE<11 in quirks mode is not supported. In this mode, IE<11 allow
15455 * one to execute arbitrary javascript by the use of the expression() syntax. Refer
15456 * <http://blogs.msdn.com/b/ie/archive/2008/10/16/ending-expressions.aspx> to learn more about them.
15457 * You can ensure your document is in standards mode and not quirks mode by adding `<!doctype html>`
15458 * to the top of your HTML document.
15460 * SCE assists in writing code in way that (a) is secure by default and (b) makes auditing for
15461 * security vulnerabilities such as XSS, clickjacking, etc. a lot easier.
15463 * Here's an example of a binding in a privileged context:
15466 * <input ng-model="userHtml">
15467 * <div ng-bind-html="userHtml"></div>
15470 * Notice that `ng-bind-html` is bound to `userHtml` controlled by the user. With SCE
15471 * disabled, this application allows the user to render arbitrary HTML into the DIV.
15472 * In a more realistic example, one may be rendering user comments, blog articles, etc. via
15473 * bindings. (HTML is just one example of a context where rendering user controlled input creates
15474 * security vulnerabilities.)
15476 * For the case of HTML, you might use a library, either on the client side, or on the server side,
15477 * to sanitize unsafe HTML before binding to the value and rendering it in the document.
15479 * How would you ensure that every place that used these types of bindings was bound to a value that
15480 * was sanitized by your library (or returned as safe for rendering by your server?) How can you
15481 * ensure that you didn't accidentally delete the line that sanitized the value, or renamed some
15482 * properties/fields and forgot to update the binding to the sanitized value?
15484 * To be secure by default, you want to ensure that any such bindings are disallowed unless you can
15485 * determine that something explicitly says it's safe to use a value for binding in that
15486 * context. You can then audit your code (a simple grep would do) to ensure that this is only done
15487 * for those values that you can easily tell are safe - because they were received from your server,
15488 * sanitized by your library, etc. You can organize your codebase to help with this - perhaps
15489 * allowing only the files in a specific directory to do this. Ensuring that the internal API
15490 * exposed by that code doesn't markup arbitrary values as safe then becomes a more manageable task.
15492 * In the case of AngularJS' SCE service, one uses {@link ng.$sce#trustAs $sce.trustAs}
15493 * (and shorthand methods such as {@link ng.$sce#trustAsHtml $sce.trustAsHtml}, etc.) to
15494 * obtain values that will be accepted by SCE / privileged contexts.
15497 * ## How does it work?
15499 * In privileged contexts, directives and code will bind to the result of {@link ng.$sce#getTrusted
15500 * $sce.getTrusted(context, value)} rather than to the value directly. Directives use {@link
15501 * ng.$sce#parseAs $sce.parseAs} rather than `$parse` to watch attribute bindings, which performs the
15502 * {@link ng.$sce#getTrusted $sce.getTrusted} behind the scenes on non-constant literals.
15504 * As an example, {@link ng.directive:ngBindHtml ngBindHtml} uses {@link
15505 * ng.$sce#parseAsHtml $sce.parseAsHtml(binding expression)}. Here's the actual code (slightly
15509 * var ngBindHtmlDirective = ['$sce', function($sce) {
15510 * return function(scope, element, attr) {
15511 * scope.$watch($sce.parseAsHtml(attr.ngBindHtml), function(value) {
15512 * element.html(value || '');
15518 * ## Impact on loading templates
15520 * This applies both to the {@link ng.directive:ngInclude `ng-include`} directive as well as
15521 * `templateUrl`'s specified by {@link guide/directive directives}.
15523 * By default, Angular only loads templates from the same domain and protocol as the application
15524 * document. This is done by calling {@link ng.$sce#getTrustedResourceUrl
15525 * $sce.getTrustedResourceUrl} on the template URL. To load templates from other domains and/or
15526 * protocols, you may either either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist
15527 * them} or {@link ng.$sce#trustAsResourceUrl wrap it} into a trusted value.
15531 * [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest)
15532 * and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/)
15533 * policy apply in addition to this and may further restrict whether the template is successfully
15534 * loaded. This means that without the right CORS policy, loading templates from a different domain
15535 * won't work on all browsers. Also, loading templates from `file://` URL does not work on some
15538 * ## This feels like too much overhead
15540 * It's important to remember that SCE only applies to interpolation expressions.
15542 * If your expressions are constant literals, they're automatically trusted and you don't need to
15543 * call `$sce.trustAs` on them (remember to include the `ngSanitize` module) (e.g.
15544 * `<div ng-bind-html="'<b>implicitly trusted</b>'"></div>`) just works.
15546 * Additionally, `a[href]` and `img[src]` automatically sanitize their URLs and do not pass them
15547 * through {@link ng.$sce#getTrusted $sce.getTrusted}. SCE doesn't play a role here.
15549 * The included {@link ng.$sceDelegate $sceDelegate} comes with sane defaults to allow you to load
15550 * templates in `ng-include` from your application's domain without having to even know about SCE.
15551 * It blocks loading templates from other domains or loading templates over http from an https
15552 * served document. You can change these by setting your own custom {@link
15553 * ng.$sceDelegateProvider#resourceUrlWhitelist whitelists} and {@link
15554 * ng.$sceDelegateProvider#resourceUrlBlacklist blacklists} for matching such URLs.
15556 * This significantly reduces the overhead. It is far easier to pay the small overhead and have an
15557 * application that's secure and can be audited to verify that with much more ease than bolting
15558 * security onto an application later.
15560 * <a name="contexts"></a>
15561 * ## What trusted context types are supported?
15563 * | Context | Notes |
15564 * |---------------------|----------------|
15565 * | `$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. |
15566 * | `$sce.CSS` | For CSS that's safe to source into the application. Currently unused. Feel free to use it in your own directives. |
15567 * | `$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. |
15568 * | `$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. |
15569 * | `$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. |
15571 * ## Format of items in {@link ng.$sceDelegateProvider#resourceUrlWhitelist resourceUrlWhitelist}/{@link ng.$sceDelegateProvider#resourceUrlBlacklist Blacklist} <a name="resourceUrlPatternItem"></a>
15573 * Each element in these arrays must be one of the following:
15576 * - The special **string**, `'self'`, can be used to match against all URLs of the **same
15577 * domain** as the application document using the **same protocol**.
15578 * - **String** (except the special value `'self'`)
15579 * - The string is matched against the full *normalized / absolute URL* of the resource
15580 * being tested (substring matches are not good enough.)
15581 * - There are exactly **two wildcard sequences** - `*` and `**`. All other characters
15582 * match themselves.
15583 * - `*`: matches zero or more occurrences of any character other than one of the following 6
15584 * characters: '`:`', '`/`', '`.`', '`?`', '`&`' and ';'. It's a useful wildcard for use
15586 * - `**`: matches zero or more occurrences of *any* character. As such, it's not
15587 * appropriate for use in a scheme, domain, etc. as it would match too much. (e.g.
15588 * http://**.example.com/ would match http://evil.com/?ignore=.example.com/ and that might
15589 * not have been the intention.) Its usage at the very end of the path is ok. (e.g.
15590 * http://foo.example.com/templates/**).
15591 * - **RegExp** (*see caveat below*)
15592 * - *Caveat*: While regular expressions are powerful and offer great flexibility, their syntax
15593 * (and all the inevitable escaping) makes them *harder to maintain*. It's easy to
15594 * accidentally introduce a bug when one updates a complex expression (imho, all regexes should
15595 * have good test coverage). For instance, the use of `.` in the regex is correct only in a
15596 * small number of cases. A `.` character in the regex used when matching the scheme or a
15597 * subdomain could be matched against a `:` or literal `.` that was likely not intended. It
15598 * is highly recommended to use the string patterns and only fall back to regular expressions
15599 * as a last resort.
15600 * - The regular expression must be an instance of RegExp (i.e. not a string.) It is
15601 * matched against the **entire** *normalized / absolute URL* of the resource being tested
15602 * (even when the RegExp did not have the `^` and `$` codes.) In addition, any flags
15603 * present on the RegExp (such as multiline, global, ignoreCase) are ignored.
15604 * - If you are generating your JavaScript from some other templating engine (not
15605 * recommended, e.g. in issue [#4006](https://github.com/angular/angular.js/issues/4006)),
15606 * remember to escape your regular expression (and be aware that you might need more than
15607 * one level of escaping depending on your templating engine and the way you interpolated
15608 * the value.) Do make use of your platform's escaping mechanism as it might be good
15609 * enough before coding your own. E.g. Ruby has
15610 * [Regexp.escape(str)](http://www.ruby-doc.org/core-2.0.0/Regexp.html#method-c-escape)
15611 * and Python has [re.escape](http://docs.python.org/library/re.html#re.escape).
15612 * Javascript lacks a similar built in function for escaping. Take a look at Google
15613 * Closure library's [goog.string.regExpEscape(s)](
15614 * http://docs.closure-library.googlecode.com/git/closure_goog_string_string.js.source.html#line962).
15616 * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} for an example.
15618 * ## Show me an example using SCE.
15620 * <example module="mySceApp" deps="angular-sanitize.js">
15621 * <file name="index.html">
15622 * <div ng-controller="AppController as myCtrl">
15623 * <i ng-bind-html="myCtrl.explicitlyTrustedHtml" id="explicitlyTrustedHtml"></i><br><br>
15624 * <b>User comments</b><br>
15625 * By default, HTML that isn't explicitly trusted (e.g. Alice's comment) is sanitized when
15626 * $sanitize is available. If $sanitize isn't available, this results in an error instead of an
15628 * <div class="well">
15629 * <div ng-repeat="userComment in myCtrl.userComments">
15630 * <b>{{userComment.name}}</b>:
15631 * <span ng-bind-html="userComment.htmlComment" class="htmlComment"></span>
15638 * <file name="script.js">
15639 * angular.module('mySceApp', ['ngSanitize'])
15640 * .controller('AppController', ['$http', '$templateCache', '$sce',
15641 * function($http, $templateCache, $sce) {
15643 * $http.get("test_data.json", {cache: $templateCache}).success(function(userComments) {
15644 * self.userComments = userComments;
15646 * self.explicitlyTrustedHtml = $sce.trustAsHtml(
15647 * '<span onmouseover="this.textContent="Explicitly trusted HTML bypasses ' +
15648 * 'sanitization."">Hover over this text.</span>');
15652 * <file name="test_data.json">
15654 * { "name": "Alice",
15656 * "<span onmouseover='this.textContent=\"PWN3D!\"'>Is <i>anyone</i> reading this?</span>"
15659 * "htmlComment": "<i>Yes!</i> Am I the only other one?"
15664 * <file name="protractor.js" type="protractor">
15665 * describe('SCE doc demo', function() {
15666 * it('should sanitize untrusted values', function() {
15667 * expect(element.all(by.css('.htmlComment')).first().getInnerHtml())
15668 * .toBe('<span>Is <i>anyone</i> reading this?</span>');
15671 * it('should NOT sanitize explicitly trusted values', function() {
15672 * expect(element(by.id('explicitlyTrustedHtml')).getInnerHtml()).toBe(
15673 * '<span onmouseover="this.textContent="Explicitly trusted HTML bypasses ' +
15674 * 'sanitization."">Hover over this text.</span>');
15682 * ## Can I disable SCE completely?
15684 * Yes, you can. However, this is strongly discouraged. SCE gives you a lot of security benefits
15685 * for little coding overhead. It will be much harder to take an SCE disabled application and
15686 * either secure it on your own or enable SCE at a later stage. It might make sense to disable SCE
15687 * for cases where you have a lot of existing code that was written before SCE was introduced and
15688 * you're migrating them a module at a time.
15690 * That said, here's how you can completely disable SCE:
15693 * angular.module('myAppWithSceDisabledmyApp', []).config(function($sceProvider) {
15694 * // Completely disable SCE. For demonstration purposes only!
15695 * // Do not use in new projects.
15696 * $sceProvider.enabled(false);
15701 /* jshint maxlen: 100 */
15703 function $SceProvider() {
15704 var enabled = true;
15708 * @name $sceProvider#enabled
15711 * @param {boolean=} value If provided, then enables/disables SCE.
15712 * @return {boolean} true if SCE is enabled, false otherwise.
15715 * Enables/disables SCE and returns the current value.
15717 this.enabled = function(value) {
15718 if (arguments.length) {
15725 /* Design notes on the default implementation for SCE.
15727 * The API contract for the SCE delegate
15728 * -------------------------------------
15729 * The SCE delegate object must provide the following 3 methods:
15731 * - trustAs(contextEnum, value)
15732 * This method is used to tell the SCE service that the provided value is OK to use in the
15733 * contexts specified by contextEnum. It must return an object that will be accepted by
15734 * getTrusted() for a compatible contextEnum and return this value.
15737 * For values that were not produced by trustAs(), return them as is. For values that were
15738 * produced by trustAs(), return the corresponding input value to trustAs. Basically, if
15739 * trustAs is wrapping the given values into some type, this operation unwraps it when given
15742 * - getTrusted(contextEnum, value)
15743 * This function should return the a value that is safe to use in the context specified by
15744 * contextEnum or throw and exception otherwise.
15746 * NOTE: This contract deliberately does NOT state that values returned by trustAs() must be
15747 * opaque or wrapped in some holder object. That happens to be an implementation detail. For
15748 * instance, an implementation could maintain a registry of all trusted objects by context. In
15749 * such a case, trustAs() would return the same object that was passed in. getTrusted() would
15750 * return the same object passed in if it was found in the registry under a compatible context or
15751 * throw an exception otherwise. An implementation might only wrap values some of the time based
15752 * on some criteria. getTrusted() might return a value and not throw an exception for special
15753 * constants or objects even if not wrapped. All such implementations fulfill this contract.
15756 * A note on the inheritance model for SCE contexts
15757 * ------------------------------------------------
15758 * I've used inheritance and made RESOURCE_URL wrapped types a subtype of URL wrapped types. This
15759 * is purely an implementation details.
15761 * The contract is simply this:
15763 * getTrusted($sce.RESOURCE_URL, value) succeeding implies that getTrusted($sce.URL, value)
15764 * will also succeed.
15766 * Inheritance happens to capture this in a natural way. In some future, we
15767 * may not use inheritance anymore. That is OK because no code outside of
15768 * sce.js and sceSpecs.js would need to be aware of this detail.
15771 this.$get = ['$parse', '$sceDelegate', function(
15772 $parse, $sceDelegate) {
15773 // Prereq: Ensure that we're not running in IE<11 quirks mode. In that mode, IE < 11 allow
15774 // the "expression(javascript expression)" syntax which is insecure.
15775 if (enabled && msie < 8) {
15776 throw $sceMinErr('iequirks',
15777 'Strict Contextual Escaping does not support Internet Explorer version < 11 in quirks ' +
15778 'mode. You can fix this by adding the text <!doctype html> to the top of your HTML ' +
15779 'document. See http://docs.angularjs.org/api/ng.$sce for more information.');
15782 var sce = shallowCopy(SCE_CONTEXTS);
15786 * @name $sce#isEnabled
15789 * @return {Boolean} true if SCE is enabled, false otherwise. If you want to set the value, you
15790 * have to do it at module config time on {@link ng.$sceProvider $sceProvider}.
15793 * Returns a boolean indicating if SCE is enabled.
15795 sce.isEnabled = function() {
15798 sce.trustAs = $sceDelegate.trustAs;
15799 sce.getTrusted = $sceDelegate.getTrusted;
15800 sce.valueOf = $sceDelegate.valueOf;
15803 sce.trustAs = sce.getTrusted = function(type, value) { return value; };
15804 sce.valueOf = identity;
15809 * @name $sce#parseAs
15812 * Converts Angular {@link guide/expression expression} into a function. This is like {@link
15813 * ng.$parse $parse} and is identical when the expression is a literal constant. Otherwise, it
15814 * wraps the expression in a call to {@link ng.$sce#getTrusted $sce.getTrusted(*type*,
15817 * @param {string} type The kind of SCE context in which this result will be used.
15818 * @param {string} expression String expression to compile.
15819 * @returns {function(context, locals)} a function which represents the compiled expression:
15821 * * `context` – `{object}` – an object against which any expressions embedded in the strings
15822 * are evaluated against (typically a scope object).
15823 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
15826 sce.parseAs = function sceParseAs(type, expr) {
15827 var parsed = $parse(expr);
15828 if (parsed.literal && parsed.constant) {
15831 return $parse(expr, function(value) {
15832 return sce.getTrusted(type, value);
15839 * @name $sce#trustAs
15842 * Delegates to {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}. As such,
15843 * returns an object that is trusted by angular for use in specified strict contextual
15844 * escaping contexts (such as ng-bind-html, ng-include, any src attribute
15845 * interpolation, any dom event binding attribute interpolation such as for onclick, etc.)
15846 * that uses the provided value. See * {@link ng.$sce $sce} for enabling strict contextual
15849 * @param {string} type The kind of context in which this value is safe for use. e.g. url,
15850 * resource_url, html, js and css.
15851 * @param {*} value The value that that should be considered trusted/safe.
15852 * @returns {*} A value that can be used to stand in for the provided `value` in places
15853 * where Angular expects a $sce.trustAs() return value.
15858 * @name $sce#trustAsHtml
15861 * Shorthand method. `$sce.trustAsHtml(value)` →
15862 * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.HTML, value)`}
15864 * @param {*} value The value to trustAs.
15865 * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedHtml
15866 * $sce.getTrustedHtml(value)} to obtain the original value. (privileged directives
15867 * only accept expressions that are either literal constants or are the
15868 * return value of {@link ng.$sce#trustAs $sce.trustAs}.)
15873 * @name $sce#trustAsUrl
15876 * Shorthand method. `$sce.trustAsUrl(value)` →
15877 * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.URL, value)`}
15879 * @param {*} value The value to trustAs.
15880 * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedUrl
15881 * $sce.getTrustedUrl(value)} to obtain the original value. (privileged directives
15882 * only accept expressions that are either literal constants or are the
15883 * return value of {@link ng.$sce#trustAs $sce.trustAs}.)
15888 * @name $sce#trustAsResourceUrl
15891 * Shorthand method. `$sce.trustAsResourceUrl(value)` →
15892 * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.RESOURCE_URL, value)`}
15894 * @param {*} value The value to trustAs.
15895 * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedResourceUrl
15896 * $sce.getTrustedResourceUrl(value)} to obtain the original value. (privileged directives
15897 * only accept expressions that are either literal constants or are the return
15898 * value of {@link ng.$sce#trustAs $sce.trustAs}.)
15903 * @name $sce#trustAsJs
15906 * Shorthand method. `$sce.trustAsJs(value)` →
15907 * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.JS, value)`}
15909 * @param {*} value The value to trustAs.
15910 * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedJs
15911 * $sce.getTrustedJs(value)} to obtain the original value. (privileged directives
15912 * only accept expressions that are either literal constants or are the
15913 * return value of {@link ng.$sce#trustAs $sce.trustAs}.)
15918 * @name $sce#getTrusted
15921 * Delegates to {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted`}. As such,
15922 * takes the result of a {@link ng.$sce#trustAs `$sce.trustAs`}() call and returns the
15923 * originally supplied value if the queried context type is a supertype of the created type.
15924 * If this condition isn't satisfied, throws an exception.
15926 * @param {string} type The kind of context in which this value is to be used.
15927 * @param {*} maybeTrusted The result of a prior {@link ng.$sce#trustAs `$sce.trustAs`}
15929 * @returns {*} The value the was originally provided to
15930 * {@link ng.$sce#trustAs `$sce.trustAs`} if valid in this context.
15931 * Otherwise, throws an exception.
15936 * @name $sce#getTrustedHtml
15939 * Shorthand method. `$sce.getTrustedHtml(value)` →
15940 * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.HTML, value)`}
15942 * @param {*} value The value to pass to `$sce.getTrusted`.
15943 * @returns {*} The return value of `$sce.getTrusted($sce.HTML, value)`
15948 * @name $sce#getTrustedCss
15951 * Shorthand method. `$sce.getTrustedCss(value)` →
15952 * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.CSS, value)`}
15954 * @param {*} value The value to pass to `$sce.getTrusted`.
15955 * @returns {*} The return value of `$sce.getTrusted($sce.CSS, value)`
15960 * @name $sce#getTrustedUrl
15963 * Shorthand method. `$sce.getTrustedUrl(value)` →
15964 * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.URL, value)`}
15966 * @param {*} value The value to pass to `$sce.getTrusted`.
15967 * @returns {*} The return value of `$sce.getTrusted($sce.URL, value)`
15972 * @name $sce#getTrustedResourceUrl
15975 * Shorthand method. `$sce.getTrustedResourceUrl(value)` →
15976 * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.RESOURCE_URL, value)`}
15978 * @param {*} value The value to pass to `$sceDelegate.getTrusted`.
15979 * @returns {*} The return value of `$sce.getTrusted($sce.RESOURCE_URL, value)`
15984 * @name $sce#getTrustedJs
15987 * Shorthand method. `$sce.getTrustedJs(value)` →
15988 * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.JS, value)`}
15990 * @param {*} value The value to pass to `$sce.getTrusted`.
15991 * @returns {*} The return value of `$sce.getTrusted($sce.JS, value)`
15996 * @name $sce#parseAsHtml
15999 * Shorthand method. `$sce.parseAsHtml(expression string)` →
16000 * {@link ng.$sce#parseAs `$sce.parseAs($sce.HTML, value)`}
16002 * @param {string} expression String expression to compile.
16003 * @returns {function(context, locals)} a function which represents the compiled expression:
16005 * * `context` – `{object}` – an object against which any expressions embedded in the strings
16006 * are evaluated against (typically a scope object).
16007 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
16013 * @name $sce#parseAsCss
16016 * Shorthand method. `$sce.parseAsCss(value)` →
16017 * {@link ng.$sce#parseAs `$sce.parseAs($sce.CSS, value)`}
16019 * @param {string} expression String expression to compile.
16020 * @returns {function(context, locals)} a function which represents the compiled expression:
16022 * * `context` – `{object}` – an object against which any expressions embedded in the strings
16023 * are evaluated against (typically a scope object).
16024 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
16030 * @name $sce#parseAsUrl
16033 * Shorthand method. `$sce.parseAsUrl(value)` →
16034 * {@link ng.$sce#parseAs `$sce.parseAs($sce.URL, value)`}
16036 * @param {string} expression String expression to compile.
16037 * @returns {function(context, locals)} a function which represents the compiled expression:
16039 * * `context` – `{object}` – an object against which any expressions embedded in the strings
16040 * are evaluated against (typically a scope object).
16041 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
16047 * @name $sce#parseAsResourceUrl
16050 * Shorthand method. `$sce.parseAsResourceUrl(value)` →
16051 * {@link ng.$sce#parseAs `$sce.parseAs($sce.RESOURCE_URL, value)`}
16053 * @param {string} expression String expression to compile.
16054 * @returns {function(context, locals)} a function which represents the compiled expression:
16056 * * `context` – `{object}` – an object against which any expressions embedded in the strings
16057 * are evaluated against (typically a scope object).
16058 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
16064 * @name $sce#parseAsJs
16067 * Shorthand method. `$sce.parseAsJs(value)` →
16068 * {@link ng.$sce#parseAs `$sce.parseAs($sce.JS, value)`}
16070 * @param {string} expression String expression to compile.
16071 * @returns {function(context, locals)} a function which represents the compiled expression:
16073 * * `context` – `{object}` – an object against which any expressions embedded in the strings
16074 * are evaluated against (typically a scope object).
16075 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
16079 // Shorthand delegations.
16080 var parse = sce.parseAs,
16081 getTrusted = sce.getTrusted,
16082 trustAs = sce.trustAs;
16084 forEach(SCE_CONTEXTS, function(enumValue, name) {
16085 var lName = lowercase(name);
16086 sce[camelCase("parse_as_" + lName)] = function(expr) {
16087 return parse(enumValue, expr);
16089 sce[camelCase("get_trusted_" + lName)] = function(value) {
16090 return getTrusted(enumValue, value);
16092 sce[camelCase("trust_as_" + lName)] = function(value) {
16093 return trustAs(enumValue, value);
16102 * !!! This is an undocumented "private" service !!!
16105 * @requires $window
16106 * @requires $document
16108 * @property {boolean} history Does the browser support html5 history api ?
16109 * @property {boolean} transitions Does the browser support CSS transition events ?
16110 * @property {boolean} animations Does the browser support CSS animation events ?
16113 * This is very simple implementation of testing browser's features.
16115 function $SnifferProvider() {
16116 this.$get = ['$window', '$document', function($window, $document) {
16117 var eventSupport = {},
16119 int((/android (\d+)/.exec(lowercase(($window.navigator || {}).userAgent)) || [])[1]),
16120 boxee = /Boxee/i.test(($window.navigator || {}).userAgent),
16121 document = $document[0] || {},
16123 vendorRegex = /^(Moz|webkit|ms)(?=[A-Z])/,
16124 bodyStyle = document.body && document.body.style,
16125 transitions = false,
16126 animations = false,
16130 for (var prop in bodyStyle) {
16131 if (match = vendorRegex.exec(prop)) {
16132 vendorPrefix = match[0];
16133 vendorPrefix = vendorPrefix.substr(0, 1).toUpperCase() + vendorPrefix.substr(1);
16138 if (!vendorPrefix) {
16139 vendorPrefix = ('WebkitOpacity' in bodyStyle) && 'webkit';
16142 transitions = !!(('transition' in bodyStyle) || (vendorPrefix + 'Transition' in bodyStyle));
16143 animations = !!(('animation' in bodyStyle) || (vendorPrefix + 'Animation' in bodyStyle));
16145 if (android && (!transitions || !animations)) {
16146 transitions = isString(document.body.style.webkitTransition);
16147 animations = isString(document.body.style.webkitAnimation);
16153 // Android has history.pushState, but it does not update location correctly
16154 // so let's not use the history API at all.
16155 // http://code.google.com/p/android/issues/detail?id=17471
16156 // https://github.com/angular/angular.js/issues/904
16158 // older webkit browser (533.9) on Boxee box has exactly the same problem as Android has
16159 // so let's not use the history API also
16160 // We are purposefully using `!(android < 4)` to cover the case when `android` is undefined
16162 history: !!($window.history && $window.history.pushState && !(android < 4) && !boxee),
16164 hasEvent: function(event) {
16165 // IE9 implements 'input' event it's so fubared that we rather pretend that it doesn't have
16166 // it. In particular the event is not fired when backspace or delete key are pressed or
16167 // when cut operation is performed.
16168 // IE10+ implements 'input' event but it erroneously fires under various situations,
16169 // e.g. when placeholder changes, or a form is focused.
16170 if (event === 'input' && msie <= 11) return false;
16172 if (isUndefined(eventSupport[event])) {
16173 var divElm = document.createElement('div');
16174 eventSupport[event] = 'on' + event in divElm;
16177 return eventSupport[event];
16180 vendorPrefix: vendorPrefix,
16181 transitions: transitions,
16182 animations: animations,
16188 var $compileMinErr = minErr('$compile');
16192 * @name $templateRequest
16195 * The `$templateRequest` service runs security checks then downloads the provided template using
16196 * `$http` and, upon success, stores the contents inside of `$templateCache`. If the HTTP request
16197 * fails or the response data of the HTTP request is empty, a `$compile` error will be thrown (the
16198 * exception can be thwarted by setting the 2nd parameter of the function to true). Note that the
16199 * contents of `$templateCache` are trusted, so the call to `$sce.getTrustedUrl(tpl)` is omitted
16200 * when `tpl` is of type string and `$templateCache` has the matching entry.
16202 * @param {string|TrustedResourceUrl} tpl The HTTP request template URL
16203 * @param {boolean=} ignoreRequestError Whether or not to ignore the exception when the request fails or the template is empty
16205 * @return {Promise} the HTTP Promise for the given.
16207 * @property {number} totalPendingRequests total amount of pending template requests being downloaded.
16209 function $TemplateRequestProvider() {
16210 this.$get = ['$templateCache', '$http', '$q', '$sce', function($templateCache, $http, $q, $sce) {
16211 function handleRequestFn(tpl, ignoreRequestError) {
16212 handleRequestFn.totalPendingRequests++;
16214 // We consider the template cache holds only trusted templates, so
16215 // there's no need to go through whitelisting again for keys that already
16216 // are included in there. This also makes Angular accept any script
16217 // directive, no matter its name. However, we still need to unwrap trusted
16219 if (!isString(tpl) || !$templateCache.get(tpl)) {
16220 tpl = $sce.getTrustedResourceUrl(tpl);
16223 var transformResponse = $http.defaults && $http.defaults.transformResponse;
16225 if (isArray(transformResponse)) {
16226 transformResponse = transformResponse.filter(function(transformer) {
16227 return transformer !== defaultHttpResponseTransform;
16229 } else if (transformResponse === defaultHttpResponseTransform) {
16230 transformResponse = null;
16233 var httpOptions = {
16234 cache: $templateCache,
16235 transformResponse: transformResponse
16238 return $http.get(tpl, httpOptions)
16239 ['finally'](function() {
16240 handleRequestFn.totalPendingRequests--;
16242 .then(function(response) {
16243 return response.data;
16246 function handleError(resp) {
16247 if (!ignoreRequestError) {
16248 throw $compileMinErr('tpload', 'Failed to load template: {0}', tpl);
16250 return $q.reject(resp);
16254 handleRequestFn.totalPendingRequests = 0;
16256 return handleRequestFn;
16260 function $$TestabilityProvider() {
16261 this.$get = ['$rootScope', '$browser', '$location',
16262 function($rootScope, $browser, $location) {
16265 * @name $testability
16268 * The private $$testability service provides a collection of methods for use when debugging
16269 * or by automated test and debugging tools.
16271 var testability = {};
16274 * @name $$testability#findBindings
16277 * Returns an array of elements that are bound (via ng-bind or {{}})
16278 * to expressions matching the input.
16280 * @param {Element} element The element root to search from.
16281 * @param {string} expression The binding expression to match.
16282 * @param {boolean} opt_exactMatch If true, only returns exact matches
16283 * for the expression. Filters and whitespace are ignored.
16285 testability.findBindings = function(element, expression, opt_exactMatch) {
16286 var bindings = element.getElementsByClassName('ng-binding');
16288 forEach(bindings, function(binding) {
16289 var dataBinding = angular.element(binding).data('$binding');
16291 forEach(dataBinding, function(bindingName) {
16292 if (opt_exactMatch) {
16293 var matcher = new RegExp('(^|\\s)' + escapeForRegexp(expression) + '(\\s|\\||$)');
16294 if (matcher.test(bindingName)) {
16295 matches.push(binding);
16298 if (bindingName.indexOf(expression) != -1) {
16299 matches.push(binding);
16309 * @name $$testability#findModels
16312 * Returns an array of elements that are two-way found via ng-model to
16313 * expressions matching the input.
16315 * @param {Element} element The element root to search from.
16316 * @param {string} expression The model expression to match.
16317 * @param {boolean} opt_exactMatch If true, only returns exact matches
16318 * for the expression.
16320 testability.findModels = function(element, expression, opt_exactMatch) {
16321 var prefixes = ['ng-', 'data-ng-', 'ng\\:'];
16322 for (var p = 0; p < prefixes.length; ++p) {
16323 var attributeEquals = opt_exactMatch ? '=' : '*=';
16324 var selector = '[' + prefixes[p] + 'model' + attributeEquals + '"' + expression + '"]';
16325 var elements = element.querySelectorAll(selector);
16326 if (elements.length) {
16333 * @name $$testability#getLocation
16336 * Shortcut for getting the location in a browser agnostic way. Returns
16337 * the path, search, and hash. (e.g. /path?a=b#hash)
16339 testability.getLocation = function() {
16340 return $location.url();
16344 * @name $$testability#setLocation
16347 * Shortcut for navigating to a location without doing a full page reload.
16349 * @param {string} url The location url (path, search and hash,
16350 * e.g. /path?a=b#hash) to go to.
16352 testability.setLocation = function(url) {
16353 if (url !== $location.url()) {
16354 $location.url(url);
16355 $rootScope.$digest();
16360 * @name $$testability#whenStable
16363 * Calls the callback when $timeout and $http requests are completed.
16365 * @param {function} callback
16367 testability.whenStable = function(callback) {
16368 $browser.notifyWhenNoOutstandingRequests(callback);
16371 return testability;
16375 function $TimeoutProvider() {
16376 this.$get = ['$rootScope', '$browser', '$q', '$$q', '$exceptionHandler',
16377 function($rootScope, $browser, $q, $$q, $exceptionHandler) {
16378 var deferreds = {};
16386 * Angular's wrapper for `window.setTimeout`. The `fn` function is wrapped into a try/catch
16387 * block and delegates any exceptions to
16388 * {@link ng.$exceptionHandler $exceptionHandler} service.
16390 * The return value of registering a timeout function is a promise, which will be resolved when
16391 * the timeout is reached and the timeout function is executed.
16393 * To cancel a timeout request, call `$timeout.cancel(promise)`.
16395 * In tests you can use {@link ngMock.$timeout `$timeout.flush()`} to
16396 * synchronously flush the queue of deferred functions.
16398 * @param {function()} fn A function, whose execution should be delayed.
16399 * @param {number=} [delay=0] Delay in milliseconds.
16400 * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
16401 * will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
16402 * @returns {Promise} Promise that will be resolved when the timeout is reached. The value this
16403 * promise will be resolved with is the return value of the `fn` function.
16406 function timeout(fn, delay, invokeApply) {
16407 var skipApply = (isDefined(invokeApply) && !invokeApply),
16408 deferred = (skipApply ? $$q : $q).defer(),
16409 promise = deferred.promise,
16412 timeoutId = $browser.defer(function() {
16414 deferred.resolve(fn());
16416 deferred.reject(e);
16417 $exceptionHandler(e);
16420 delete deferreds[promise.$$timeoutId];
16423 if (!skipApply) $rootScope.$apply();
16426 promise.$$timeoutId = timeoutId;
16427 deferreds[timeoutId] = deferred;
16435 * @name $timeout#cancel
16438 * Cancels a task associated with the `promise`. As a result of this, the promise will be
16439 * resolved with a rejection.
16441 * @param {Promise=} promise Promise returned by the `$timeout` function.
16442 * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully
16445 timeout.cancel = function(promise) {
16446 if (promise && promise.$$timeoutId in deferreds) {
16447 deferreds[promise.$$timeoutId].reject('canceled');
16448 delete deferreds[promise.$$timeoutId];
16449 return $browser.defer.cancel(promise.$$timeoutId);
16458 // NOTE: The usage of window and document instead of $window and $document here is
16459 // deliberate. This service depends on the specific behavior of anchor nodes created by the
16460 // browser (resolving and parsing URLs) that is unlikely to be provided by mock objects and
16461 // cause us to break tests. In addition, when the browser resolves a URL for XHR, it
16462 // doesn't know about mocked locations and resolves URLs to the real document - which is
16463 // exactly the behavior needed here. There is little value is mocking these out for this
16465 var urlParsingNode = document.createElement("a");
16466 var originUrl = urlResolve(window.location.href);
16471 * Implementation Notes for non-IE browsers
16472 * ----------------------------------------
16473 * Assigning a URL to the href property of an anchor DOM node, even one attached to the DOM,
16474 * results both in the normalizing and parsing of the URL. Normalizing means that a relative
16475 * URL will be resolved into an absolute URL in the context of the application document.
16476 * Parsing means that the anchor node's host, hostname, protocol, port, pathname and related
16477 * properties are all populated to reflect the normalized URL. This approach has wide
16478 * compatibility - Safari 1+, Mozilla 1+, Opera 7+,e etc. See
16479 * http://www.aptana.com/reference/html/api/HTMLAnchorElement.html
16481 * Implementation Notes for IE
16482 * ---------------------------
16483 * IE <= 10 normalizes the URL when assigned to the anchor node similar to the other
16484 * browsers. However, the parsed components will not be set if the URL assigned did not specify
16485 * them. (e.g. if you assign a.href = "foo", then a.protocol, a.host, etc. will be empty.) We
16486 * work around that by performing the parsing in a 2nd step by taking a previously normalized
16487 * URL (e.g. by assigning to a.href) and assigning it a.href again. This correctly populates the
16488 * properties such as protocol, hostname, port, etc.
16491 * http://developer.mozilla.org/en-US/docs/Web/API/HTMLAnchorElement
16492 * http://www.aptana.com/reference/html/api/HTMLAnchorElement.html
16493 * http://url.spec.whatwg.org/#urlutils
16494 * https://github.com/angular/angular.js/pull/2902
16495 * http://james.padolsey.com/javascript/parsing-urls-with-the-dom/
16498 * @param {string} url The URL to be parsed.
16499 * @description Normalizes and parses a URL.
16500 * @returns {object} Returns the normalized URL as a dictionary.
16502 * | member name | Description |
16503 * |---------------|----------------|
16504 * | href | A normalized version of the provided URL if it was not an absolute URL |
16505 * | protocol | The protocol including the trailing colon |
16506 * | host | The host and port (if the port is non-default) of the normalizedUrl |
16507 * | search | The search params, minus the question mark |
16508 * | hash | The hash string, minus the hash symbol
16509 * | hostname | The hostname
16510 * | port | The port, without ":"
16511 * | pathname | The pathname, beginning with "/"
16514 function urlResolve(url) {
16518 // Normalize before parse. Refer Implementation Notes on why this is
16519 // done in two steps on IE.
16520 urlParsingNode.setAttribute("href", href);
16521 href = urlParsingNode.href;
16524 urlParsingNode.setAttribute('href', href);
16526 // urlParsingNode provides the UrlUtils interface - http://url.spec.whatwg.org/#urlutils
16528 href: urlParsingNode.href,
16529 protocol: urlParsingNode.protocol ? urlParsingNode.protocol.replace(/:$/, '') : '',
16530 host: urlParsingNode.host,
16531 search: urlParsingNode.search ? urlParsingNode.search.replace(/^\?/, '') : '',
16532 hash: urlParsingNode.hash ? urlParsingNode.hash.replace(/^#/, '') : '',
16533 hostname: urlParsingNode.hostname,
16534 port: urlParsingNode.port,
16535 pathname: (urlParsingNode.pathname.charAt(0) === '/')
16536 ? urlParsingNode.pathname
16537 : '/' + urlParsingNode.pathname
16542 * Parse a request URL and determine whether this is a same-origin request as the application document.
16544 * @param {string|object} requestUrl The url of the request as a string that will be resolved
16545 * or a parsed URL object.
16546 * @returns {boolean} Whether the request is for the same origin as the application document.
16548 function urlIsSameOrigin(requestUrl) {
16549 var parsed = (isString(requestUrl)) ? urlResolve(requestUrl) : requestUrl;
16550 return (parsed.protocol === originUrl.protocol &&
16551 parsed.host === originUrl.host);
16559 * A reference to the browser's `window` object. While `window`
16560 * is globally available in JavaScript, it causes testability problems, because
16561 * it is a global variable. In angular we always refer to it through the
16562 * `$window` service, so it may be overridden, removed or mocked for testing.
16564 * Expressions, like the one defined for the `ngClick` directive in the example
16565 * below, are evaluated with respect to the current scope. Therefore, there is
16566 * no risk of inadvertently coding in a dependency on a global value in such an
16570 <example module="windowExample">
16571 <file name="index.html">
16573 angular.module('windowExample', [])
16574 .controller('ExampleController', ['$scope', '$window', function($scope, $window) {
16575 $scope.greeting = 'Hello, World!';
16576 $scope.doGreeting = function(greeting) {
16577 $window.alert(greeting);
16581 <div ng-controller="ExampleController">
16582 <input type="text" ng-model="greeting" />
16583 <button ng-click="doGreeting(greeting)">ALERT</button>
16586 <file name="protractor.js" type="protractor">
16587 it('should display the greeting in the input box', function() {
16588 element(by.model('greeting')).sendKeys('Hello, E2E Tests');
16589 // If we click the button it will block the test runner
16590 // element(':button').click();
16595 function $WindowProvider() {
16596 this.$get = valueFn(window);
16599 /* global currencyFilter: true,
16601 filterFilter: true,
16603 limitToFilter: true,
16604 lowercaseFilter: true,
16605 numberFilter: true,
16606 orderByFilter: true,
16607 uppercaseFilter: true,
16612 * @name $filterProvider
16615 * Filters are just functions which transform input to an output. However filters need to be
16616 * Dependency Injected. To achieve this a filter definition consists of a factory function which is
16617 * annotated with dependencies and is responsible for creating a filter function.
16619 * <div class="alert alert-warning">
16620 * **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`.
16621 * Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace
16622 * your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
16623 * (`myapp_subsection_filterx`).
16627 * // Filter registration
16628 * function MyModule($provide, $filterProvider) {
16629 * // create a service to demonstrate injection (not always needed)
16630 * $provide.value('greet', function(name){
16631 * return 'Hello ' + name + '!';
16634 * // register a filter factory which uses the
16635 * // greet service to demonstrate DI.
16636 * $filterProvider.register('greet', function(greet){
16637 * // return the filter function which uses the greet service
16638 * // to generate salutation
16639 * return function(text) {
16640 * // filters need to be forgiving so check input validity
16641 * return text && greet(text) || text;
16647 * The filter function is registered with the `$injector` under the filter name suffix with
16651 * it('should be the same instance', inject(
16652 * function($filterProvider) {
16653 * $filterProvider.register('reverse', function(){
16657 * function($filter, reverseFilter) {
16658 * expect($filter('reverse')).toBe(reverseFilter);
16663 * For more information about how angular filters work, and how to create your own filters, see
16664 * {@link guide/filter Filters} in the Angular Developer Guide.
16672 * Filters are used for formatting data displayed to the user.
16674 * The general syntax in templates is as follows:
16676 * {{ expression [| filter_name[:parameter_value] ... ] }}
16678 * @param {String} name Name of the filter function to retrieve
16679 * @return {Function} the filter function
16681 <example name="$filter" module="filterExample">
16682 <file name="index.html">
16683 <div ng-controller="MainCtrl">
16684 <h3>{{ originalText }}</h3>
16685 <h3>{{ filteredText }}</h3>
16689 <file name="script.js">
16690 angular.module('filterExample', [])
16691 .controller('MainCtrl', function($scope, $filter) {
16692 $scope.originalText = 'hello';
16693 $scope.filteredText = $filter('uppercase')($scope.originalText);
16698 $FilterProvider.$inject = ['$provide'];
16699 function $FilterProvider($provide) {
16700 var suffix = 'Filter';
16704 * @name $filterProvider#register
16705 * @param {string|Object} name Name of the filter function, or an object map of filters where
16706 * the keys are the filter names and the values are the filter factories.
16708 * <div class="alert alert-warning">
16709 * **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`.
16710 * Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace
16711 * your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
16712 * (`myapp_subsection_filterx`).
16714 * @returns {Object} Registered filter instance, or if a map of filters was provided then a map
16715 * of the registered filter instances.
16717 function register(name, factory) {
16718 if (isObject(name)) {
16720 forEach(name, function(filter, key) {
16721 filters[key] = register(key, filter);
16725 return $provide.factory(name + suffix, factory);
16728 this.register = register;
16730 this.$get = ['$injector', function($injector) {
16731 return function(name) {
16732 return $injector.get(name + suffix);
16736 ////////////////////////////////////////
16739 currencyFilter: false,
16741 filterFilter: false,
16743 limitToFilter: false,
16744 lowercaseFilter: false,
16745 numberFilter: false,
16746 orderByFilter: false,
16747 uppercaseFilter: false,
16750 register('currency', currencyFilter);
16751 register('date', dateFilter);
16752 register('filter', filterFilter);
16753 register('json', jsonFilter);
16754 register('limitTo', limitToFilter);
16755 register('lowercase', lowercaseFilter);
16756 register('number', numberFilter);
16757 register('orderBy', orderByFilter);
16758 register('uppercase', uppercaseFilter);
16767 * Selects a subset of items from `array` and returns it as a new array.
16769 * @param {Array} array The source array.
16770 * @param {string|Object|function()} expression The predicate to be used for selecting items from
16775 * - `string`: The string is used for matching against the contents of the `array`. All strings or
16776 * objects with string properties in `array` that match this string will be returned. This also
16777 * applies to nested object properties.
16778 * The predicate can be negated by prefixing the string with `!`.
16780 * - `Object`: A pattern object can be used to filter specific properties on objects contained
16781 * by `array`. For example `{name:"M", phone:"1"}` predicate will return an array of items
16782 * which have property `name` containing "M" and property `phone` containing "1". A special
16783 * property name `$` can be used (as in `{$:"text"}`) to accept a match against any
16784 * property of the object or its nested object properties. That's equivalent to the simple
16785 * substring match with a `string` as described above. The predicate can be negated by prefixing
16786 * the string with `!`.
16787 * For example `{name: "!M"}` predicate will return an array of items which have property `name`
16788 * not containing "M".
16790 * Note that a named property will match properties on the same level only, while the special
16791 * `$` property will match properties on the same level or deeper. E.g. an array item like
16792 * `{name: {first: 'John', last: 'Doe'}}` will **not** be matched by `{name: 'John'}`, but
16793 * **will** be matched by `{$: 'John'}`.
16795 * - `function(value, index)`: A predicate function can be used to write arbitrary filters. The
16796 * function is called for each element of `array`. The final result is an array of those
16797 * elements that the predicate returned true for.
16799 * @param {function(actual, expected)|true|undefined} comparator Comparator which is used in
16800 * determining if the expected value (from the filter expression) and actual value (from
16801 * the object in the array) should be considered a match.
16805 * - `function(actual, expected)`:
16806 * The function will be given the object value and the predicate value to compare and
16807 * should return true if both values should be considered equal.
16809 * - `true`: A shorthand for `function(actual, expected) { return angular.equals(actual, expected)}`.
16810 * This is essentially strict comparison of expected and actual.
16812 * - `false|undefined`: A short hand for a function which will look for a substring match in case
16817 <file name="index.html">
16818 <div ng-init="friends = [{name:'John', phone:'555-1276'},
16819 {name:'Mary', phone:'800-BIG-MARY'},
16820 {name:'Mike', phone:'555-4321'},
16821 {name:'Adam', phone:'555-5678'},
16822 {name:'Julie', phone:'555-8765'},
16823 {name:'Juliette', phone:'555-5678'}]"></div>
16825 Search: <input ng-model="searchText">
16826 <table id="searchTextResults">
16827 <tr><th>Name</th><th>Phone</th></tr>
16828 <tr ng-repeat="friend in friends | filter:searchText">
16829 <td>{{friend.name}}</td>
16830 <td>{{friend.phone}}</td>
16834 Any: <input ng-model="search.$"> <br>
16835 Name only <input ng-model="search.name"><br>
16836 Phone only <input ng-model="search.phone"><br>
16837 Equality <input type="checkbox" ng-model="strict"><br>
16838 <table id="searchObjResults">
16839 <tr><th>Name</th><th>Phone</th></tr>
16840 <tr ng-repeat="friendObj in friends | filter:search:strict">
16841 <td>{{friendObj.name}}</td>
16842 <td>{{friendObj.phone}}</td>
16846 <file name="protractor.js" type="protractor">
16847 var expectFriendNames = function(expectedNames, key) {
16848 element.all(by.repeater(key + ' in friends').column(key + '.name')).then(function(arr) {
16849 arr.forEach(function(wd, i) {
16850 expect(wd.getText()).toMatch(expectedNames[i]);
16855 it('should search across all fields when filtering with a string', function() {
16856 var searchText = element(by.model('searchText'));
16857 searchText.clear();
16858 searchText.sendKeys('m');
16859 expectFriendNames(['Mary', 'Mike', 'Adam'], 'friend');
16861 searchText.clear();
16862 searchText.sendKeys('76');
16863 expectFriendNames(['John', 'Julie'], 'friend');
16866 it('should search in specific fields when filtering with a predicate object', function() {
16867 var searchAny = element(by.model('search.$'));
16869 searchAny.sendKeys('i');
16870 expectFriendNames(['Mary', 'Mike', 'Julie', 'Juliette'], 'friendObj');
16872 it('should use a equal comparison when comparator is true', function() {
16873 var searchName = element(by.model('search.name'));
16874 var strict = element(by.model('strict'));
16875 searchName.clear();
16876 searchName.sendKeys('Julie');
16878 expectFriendNames(['Julie'], 'friendObj');
16883 function filterFilter() {
16884 return function(array, expression, comparator) {
16885 if (!isArray(array)) return array;
16887 var expressionType = (expression !== null) ? typeof expression : 'null';
16889 var matchAgainstAnyProp;
16891 switch (expressionType) {
16893 predicateFn = expression;
16899 matchAgainstAnyProp = true;
16903 predicateFn = createPredicateFn(expression, comparator, matchAgainstAnyProp);
16909 return array.filter(predicateFn);
16913 // Helper functions for `filterFilter`
16914 function createPredicateFn(expression, comparator, matchAgainstAnyProp) {
16915 var shouldMatchPrimitives = isObject(expression) && ('$' in expression);
16918 if (comparator === true) {
16919 comparator = equals;
16920 } else if (!isFunction(comparator)) {
16921 comparator = function(actual, expected) {
16922 if (isUndefined(actual)) {
16923 // No substring matching against `undefined`
16926 if ((actual === null) || (expected === null)) {
16927 // No substring matching against `null`; only match against `null`
16928 return actual === expected;
16930 if (isObject(actual) || isObject(expected)) {
16931 // Prevent an object to be considered equal to a string like `'[object'`
16935 actual = lowercase('' + actual);
16936 expected = lowercase('' + expected);
16937 return actual.indexOf(expected) !== -1;
16941 predicateFn = function(item) {
16942 if (shouldMatchPrimitives && !isObject(item)) {
16943 return deepCompare(item, expression.$, comparator, false);
16945 return deepCompare(item, expression, comparator, matchAgainstAnyProp);
16948 return predicateFn;
16951 function deepCompare(actual, expected, comparator, matchAgainstAnyProp, dontMatchWholeObject) {
16952 var actualType = (actual !== null) ? typeof actual : 'null';
16953 var expectedType = (expected !== null) ? typeof expected : 'null';
16955 if ((expectedType === 'string') && (expected.charAt(0) === '!')) {
16956 return !deepCompare(actual, expected.substring(1), comparator, matchAgainstAnyProp);
16957 } else if (isArray(actual)) {
16958 // In case `actual` is an array, consider it a match
16959 // if ANY of it's items matches `expected`
16960 return actual.some(function(item) {
16961 return deepCompare(item, expected, comparator, matchAgainstAnyProp);
16965 switch (actualType) {
16968 if (matchAgainstAnyProp) {
16969 for (key in actual) {
16970 if ((key.charAt(0) !== '$') && deepCompare(actual[key], expected, comparator, true)) {
16974 return dontMatchWholeObject ? false : deepCompare(actual, expected, comparator, false);
16975 } else if (expectedType === 'object') {
16976 for (key in expected) {
16977 var expectedVal = expected[key];
16978 if (isFunction(expectedVal) || isUndefined(expectedVal)) {
16982 var matchAnyProperty = key === '$';
16983 var actualVal = matchAnyProperty ? actual : actual[key];
16984 if (!deepCompare(actualVal, expectedVal, comparator, matchAnyProperty, matchAnyProperty)) {
16990 return comparator(actual, expected);
16996 return comparator(actual, expected);
17006 * Formats a number as a currency (ie $1,234.56). When no currency symbol is provided, default
17007 * symbol for current locale is used.
17009 * @param {number} amount Input to filter.
17010 * @param {string=} symbol Currency symbol or identifier to be displayed.
17011 * @param {number=} fractionSize Number of decimal places to round the amount to, defaults to default max fraction size for current locale
17012 * @returns {string} Formatted number.
17016 <example module="currencyExample">
17017 <file name="index.html">
17019 angular.module('currencyExample', [])
17020 .controller('ExampleController', ['$scope', function($scope) {
17021 $scope.amount = 1234.56;
17024 <div ng-controller="ExampleController">
17025 <input type="number" ng-model="amount"> <br>
17026 default currency symbol ($): <span id="currency-default">{{amount | currency}}</span><br>
17027 custom currency identifier (USD$): <span id="currency-custom">{{amount | currency:"USD$"}}</span>
17028 no fractions (0): <span id="currency-no-fractions">{{amount | currency:"USD$":0}}</span>
17031 <file name="protractor.js" type="protractor">
17032 it('should init with 1234.56', function() {
17033 expect(element(by.id('currency-default')).getText()).toBe('$1,234.56');
17034 expect(element(by.id('currency-custom')).getText()).toBe('USD$1,234.56');
17035 expect(element(by.id('currency-no-fractions')).getText()).toBe('USD$1,235');
17037 it('should update', function() {
17038 if (browser.params.browser == 'safari') {
17039 // Safari does not understand the minus key. See
17040 // https://github.com/angular/protractor/issues/481
17043 element(by.model('amount')).clear();
17044 element(by.model('amount')).sendKeys('-1234');
17045 expect(element(by.id('currency-default')).getText()).toBe('($1,234.00)');
17046 expect(element(by.id('currency-custom')).getText()).toBe('(USD$1,234.00)');
17047 expect(element(by.id('currency-no-fractions')).getText()).toBe('(USD$1,234)');
17052 currencyFilter.$inject = ['$locale'];
17053 function currencyFilter($locale) {
17054 var formats = $locale.NUMBER_FORMATS;
17055 return function(amount, currencySymbol, fractionSize) {
17056 if (isUndefined(currencySymbol)) {
17057 currencySymbol = formats.CURRENCY_SYM;
17060 if (isUndefined(fractionSize)) {
17061 fractionSize = formats.PATTERNS[1].maxFrac;
17064 // if null or undefined pass it through
17065 return (amount == null)
17067 : formatNumber(amount, formats.PATTERNS[1], formats.GROUP_SEP, formats.DECIMAL_SEP, fractionSize).
17068 replace(/\u00A4/g, currencySymbol);
17078 * Formats a number as text.
17080 * If the input is null or undefined, it will just be returned.
17081 * If the input is infinite (Infinity/-Infinity) the Infinity symbol '∞' is returned.
17082 * If the input is not a number an empty string is returned.
17084 * @param {number|string} number Number to format.
17085 * @param {(number|string)=} fractionSize Number of decimal places to round the number to.
17086 * If this is not provided then the fraction size is computed from the current locale's number
17087 * formatting pattern. In the case of the default locale, it will be 3.
17088 * @returns {string} Number rounded to decimalPlaces and places a “,” after each third digit.
17091 <example module="numberFilterExample">
17092 <file name="index.html">
17094 angular.module('numberFilterExample', [])
17095 .controller('ExampleController', ['$scope', function($scope) {
17096 $scope.val = 1234.56789;
17099 <div ng-controller="ExampleController">
17100 Enter number: <input ng-model='val'><br>
17101 Default formatting: <span id='number-default'>{{val | number}}</span><br>
17102 No fractions: <span>{{val | number:0}}</span><br>
17103 Negative number: <span>{{-val | number:4}}</span>
17106 <file name="protractor.js" type="protractor">
17107 it('should format numbers', function() {
17108 expect(element(by.id('number-default')).getText()).toBe('1,234.568');
17109 expect(element(by.binding('val | number:0')).getText()).toBe('1,235');
17110 expect(element(by.binding('-val | number:4')).getText()).toBe('-1,234.5679');
17113 it('should update', function() {
17114 element(by.model('val')).clear();
17115 element(by.model('val')).sendKeys('3374.333');
17116 expect(element(by.id('number-default')).getText()).toBe('3,374.333');
17117 expect(element(by.binding('val | number:0')).getText()).toBe('3,374');
17118 expect(element(by.binding('-val | number:4')).getText()).toBe('-3,374.3330');
17125 numberFilter.$inject = ['$locale'];
17126 function numberFilter($locale) {
17127 var formats = $locale.NUMBER_FORMATS;
17128 return function(number, fractionSize) {
17130 // if null or undefined pass it through
17131 return (number == null)
17133 : formatNumber(number, formats.PATTERNS[0], formats.GROUP_SEP, formats.DECIMAL_SEP,
17138 var DECIMAL_SEP = '.';
17139 function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) {
17140 if (!isFinite(number) || isObject(number)) return '';
17142 var isNegative = number < 0;
17143 number = Math.abs(number);
17144 var numStr = number + '',
17148 var hasExponent = false;
17149 if (numStr.indexOf('e') !== -1) {
17150 var match = numStr.match(/([\d\.]+)e(-?)(\d+)/);
17151 if (match && match[2] == '-' && match[3] > fractionSize + 1) {
17154 formatedText = numStr;
17155 hasExponent = true;
17159 if (!hasExponent) {
17160 var fractionLen = (numStr.split(DECIMAL_SEP)[1] || '').length;
17162 // determine fractionSize if it is not specified
17163 if (isUndefined(fractionSize)) {
17164 fractionSize = Math.min(Math.max(pattern.minFrac, fractionLen), pattern.maxFrac);
17167 // safely round numbers in JS without hitting imprecisions of floating-point arithmetics
17169 // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round
17170 number = +(Math.round(+(number.toString() + 'e' + fractionSize)).toString() + 'e' + -fractionSize);
17172 var fraction = ('' + number).split(DECIMAL_SEP);
17173 var whole = fraction[0];
17174 fraction = fraction[1] || '';
17177 lgroup = pattern.lgSize,
17178 group = pattern.gSize;
17180 if (whole.length >= (lgroup + group)) {
17181 pos = whole.length - lgroup;
17182 for (i = 0; i < pos; i++) {
17183 if ((pos - i) % group === 0 && i !== 0) {
17184 formatedText += groupSep;
17186 formatedText += whole.charAt(i);
17190 for (i = pos; i < whole.length; i++) {
17191 if ((whole.length - i) % lgroup === 0 && i !== 0) {
17192 formatedText += groupSep;
17194 formatedText += whole.charAt(i);
17197 // format fraction part.
17198 while (fraction.length < fractionSize) {
17202 if (fractionSize && fractionSize !== "0") formatedText += decimalSep + fraction.substr(0, fractionSize);
17204 if (fractionSize > 0 && number < 1) {
17205 formatedText = number.toFixed(fractionSize);
17206 number = parseFloat(formatedText);
17210 if (number === 0) {
17211 isNegative = false;
17214 parts.push(isNegative ? pattern.negPre : pattern.posPre,
17216 isNegative ? pattern.negSuf : pattern.posSuf);
17217 return parts.join('');
17220 function padNumber(num, digits, trim) {
17227 while (num.length < digits) num = '0' + num;
17229 num = num.substr(num.length - digits);
17234 function dateGetter(name, size, offset, trim) {
17235 offset = offset || 0;
17236 return function(date) {
17237 var value = date['get' + name]();
17238 if (offset > 0 || value > -offset)
17240 if (value === 0 && offset == -12) value = 12;
17241 return padNumber(value, size, trim);
17245 function dateStrGetter(name, shortForm) {
17246 return function(date, formats) {
17247 var value = date['get' + name]();
17248 var get = uppercase(shortForm ? ('SHORT' + name) : name);
17250 return formats[get][value];
17254 function timeZoneGetter(date) {
17255 var zone = -1 * date.getTimezoneOffset();
17256 var paddedZone = (zone >= 0) ? "+" : "";
17258 paddedZone += padNumber(Math[zone > 0 ? 'floor' : 'ceil'](zone / 60), 2) +
17259 padNumber(Math.abs(zone % 60), 2);
17264 function getFirstThursdayOfYear(year) {
17265 // 0 = index of January
17266 var dayOfWeekOnFirst = (new Date(year, 0, 1)).getDay();
17267 // 4 = index of Thursday (+1 to account for 1st = 5)
17268 // 11 = index of *next* Thursday (+1 account for 1st = 12)
17269 return new Date(year, 0, ((dayOfWeekOnFirst <= 4) ? 5 : 12) - dayOfWeekOnFirst);
17272 function getThursdayThisWeek(datetime) {
17273 return new Date(datetime.getFullYear(), datetime.getMonth(),
17274 // 4 = index of Thursday
17275 datetime.getDate() + (4 - datetime.getDay()));
17278 function weekGetter(size) {
17279 return function(date) {
17280 var firstThurs = getFirstThursdayOfYear(date.getFullYear()),
17281 thisThurs = getThursdayThisWeek(date);
17283 var diff = +thisThurs - +firstThurs,
17284 result = 1 + Math.round(diff / 6.048e8); // 6.048e8 ms per week
17286 return padNumber(result, size);
17290 function ampmGetter(date, formats) {
17291 return date.getHours() < 12 ? formats.AMPMS[0] : formats.AMPMS[1];
17294 function eraGetter(date, formats) {
17295 return date.getFullYear() <= 0 ? formats.ERAS[0] : formats.ERAS[1];
17298 function longEraGetter(date, formats) {
17299 return date.getFullYear() <= 0 ? formats.ERANAMES[0] : formats.ERANAMES[1];
17302 var DATE_FORMATS = {
17303 yyyy: dateGetter('FullYear', 4),
17304 yy: dateGetter('FullYear', 2, 0, true),
17305 y: dateGetter('FullYear', 1),
17306 MMMM: dateStrGetter('Month'),
17307 MMM: dateStrGetter('Month', true),
17308 MM: dateGetter('Month', 2, 1),
17309 M: dateGetter('Month', 1, 1),
17310 dd: dateGetter('Date', 2),
17311 d: dateGetter('Date', 1),
17312 HH: dateGetter('Hours', 2),
17313 H: dateGetter('Hours', 1),
17314 hh: dateGetter('Hours', 2, -12),
17315 h: dateGetter('Hours', 1, -12),
17316 mm: dateGetter('Minutes', 2),
17317 m: dateGetter('Minutes', 1),
17318 ss: dateGetter('Seconds', 2),
17319 s: dateGetter('Seconds', 1),
17320 // while ISO 8601 requires fractions to be prefixed with `.` or `,`
17321 // we can be just safely rely on using `sss` since we currently don't support single or two digit fractions
17322 sss: dateGetter('Milliseconds', 3),
17323 EEEE: dateStrGetter('Day'),
17324 EEE: dateStrGetter('Day', true),
17332 GGGG: longEraGetter
17335 var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZEwG']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z|G+|w+))(.*)/,
17336 NUMBER_STRING = /^\-?\d+$/;
17344 * Formats `date` to a string based on the requested `format`.
17346 * `format` string can be composed of the following elements:
17348 * * `'yyyy'`: 4 digit representation of year (e.g. AD 1 => 0001, AD 2010 => 2010)
17349 * * `'yy'`: 2 digit representation of year, padded (00-99). (e.g. AD 2001 => 01, AD 2010 => 10)
17350 * * `'y'`: 1 digit representation of year, e.g. (AD 1 => 1, AD 199 => 199)
17351 * * `'MMMM'`: Month in year (January-December)
17352 * * `'MMM'`: Month in year (Jan-Dec)
17353 * * `'MM'`: Month in year, padded (01-12)
17354 * * `'M'`: Month in year (1-12)
17355 * * `'dd'`: Day in month, padded (01-31)
17356 * * `'d'`: Day in month (1-31)
17357 * * `'EEEE'`: Day in Week,(Sunday-Saturday)
17358 * * `'EEE'`: Day in Week, (Sun-Sat)
17359 * * `'HH'`: Hour in day, padded (00-23)
17360 * * `'H'`: Hour in day (0-23)
17361 * * `'hh'`: Hour in AM/PM, padded (01-12)
17362 * * `'h'`: Hour in AM/PM, (1-12)
17363 * * `'mm'`: Minute in hour, padded (00-59)
17364 * * `'m'`: Minute in hour (0-59)
17365 * * `'ss'`: Second in minute, padded (00-59)
17366 * * `'s'`: Second in minute (0-59)
17367 * * `'sss'`: Millisecond in second, padded (000-999)
17368 * * `'a'`: AM/PM marker
17369 * * `'Z'`: 4 digit (+sign) representation of the timezone offset (-1200-+1200)
17370 * * `'ww'`: Week of year, padded (00-53). Week 01 is the week with the first Thursday of the year
17371 * * `'w'`: Week of year (0-53). Week 1 is the week with the first Thursday of the year
17372 * * `'G'`, `'GG'`, `'GGG'`: The abbreviated form of the era string (e.g. 'AD')
17373 * * `'GGGG'`: The long form of the era string (e.g. 'Anno Domini')
17375 * `format` string can also be one of the following predefined
17376 * {@link guide/i18n localizable formats}:
17378 * * `'medium'`: equivalent to `'MMM d, y h:mm:ss a'` for en_US locale
17379 * (e.g. Sep 3, 2010 12:05:08 PM)
17380 * * `'short'`: equivalent to `'M/d/yy h:mm a'` for en_US locale (e.g. 9/3/10 12:05 PM)
17381 * * `'fullDate'`: equivalent to `'EEEE, MMMM d, y'` for en_US locale
17382 * (e.g. Friday, September 3, 2010)
17383 * * `'longDate'`: equivalent to `'MMMM d, y'` for en_US locale (e.g. September 3, 2010)
17384 * * `'mediumDate'`: equivalent to `'MMM d, y'` for en_US locale (e.g. Sep 3, 2010)
17385 * * `'shortDate'`: equivalent to `'M/d/yy'` for en_US locale (e.g. 9/3/10)
17386 * * `'mediumTime'`: equivalent to `'h:mm:ss a'` for en_US locale (e.g. 12:05:08 PM)
17387 * * `'shortTime'`: equivalent to `'h:mm a'` for en_US locale (e.g. 12:05 PM)
17389 * `format` string can contain literal values. These need to be escaped by surrounding with single quotes (e.g.
17390 * `"h 'in the morning'"`). In order to output a single quote, escape it - i.e., two single quotes in a sequence
17391 * (e.g. `"h 'o''clock'"`).
17393 * @param {(Date|number|string)} date Date to format either as Date object, milliseconds (string or
17394 * number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.sssZ and its
17395 * shorter versions like yyyy-MM-ddTHH:mmZ, yyyy-MM-dd or yyyyMMddTHHmmssZ). If no timezone is
17396 * specified in the string input, the time is considered to be in the local timezone.
17397 * @param {string=} format Formatting rules (see Description). If not specified,
17398 * `mediumDate` is used.
17399 * @param {string=} timezone Timezone to be used for formatting. Right now, only `'UTC'` is supported.
17400 * If not specified, the timezone of the browser will be used.
17401 * @returns {string} Formatted string or the input if input is not recognized as date/millis.
17405 <file name="index.html">
17406 <span ng-non-bindable>{{1288323623006 | date:'medium'}}</span>:
17407 <span>{{1288323623006 | date:'medium'}}</span><br>
17408 <span ng-non-bindable>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span>:
17409 <span>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span><br>
17410 <span ng-non-bindable>{{1288323623006 | date:'MM/dd/yyyy @ h:mma'}}</span>:
17411 <span>{{'1288323623006' | date:'MM/dd/yyyy @ h:mma'}}</span><br>
17412 <span ng-non-bindable>{{1288323623006 | date:"MM/dd/yyyy 'at' h:mma"}}</span>:
17413 <span>{{'1288323623006' | date:"MM/dd/yyyy 'at' h:mma"}}</span><br>
17415 <file name="protractor.js" type="protractor">
17416 it('should format date', function() {
17417 expect(element(by.binding("1288323623006 | date:'medium'")).getText()).
17418 toMatch(/Oct 2\d, 2010 \d{1,2}:\d{2}:\d{2} (AM|PM)/);
17419 expect(element(by.binding("1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'")).getText()).
17420 toMatch(/2010\-10\-2\d \d{2}:\d{2}:\d{2} (\-|\+)?\d{4}/);
17421 expect(element(by.binding("'1288323623006' | date:'MM/dd/yyyy @ h:mma'")).getText()).
17422 toMatch(/10\/2\d\/2010 @ \d{1,2}:\d{2}(AM|PM)/);
17423 expect(element(by.binding("'1288323623006' | date:\"MM/dd/yyyy 'at' h:mma\"")).getText()).
17424 toMatch(/10\/2\d\/2010 at \d{1,2}:\d{2}(AM|PM)/);
17429 dateFilter.$inject = ['$locale'];
17430 function dateFilter($locale) {
17433 var R_ISO8601_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/;
17434 // 1 2 3 4 5 6 7 8 9 10 11
17435 function jsonStringToDate(string) {
17437 if (match = string.match(R_ISO8601_STR)) {
17438 var date = new Date(0),
17441 dateSetter = match[8] ? date.setUTCFullYear : date.setFullYear,
17442 timeSetter = match[8] ? date.setUTCHours : date.setHours;
17445 tzHour = int(match[9] + match[10]);
17446 tzMin = int(match[9] + match[11]);
17448 dateSetter.call(date, int(match[1]), int(match[2]) - 1, int(match[3]));
17449 var h = int(match[4] || 0) - tzHour;
17450 var m = int(match[5] || 0) - tzMin;
17451 var s = int(match[6] || 0);
17452 var ms = Math.round(parseFloat('0.' + (match[7] || 0)) * 1000);
17453 timeSetter.call(date, h, m, s, ms);
17460 return function(date, format, timezone) {
17465 format = format || 'mediumDate';
17466 format = $locale.DATETIME_FORMATS[format] || format;
17467 if (isString(date)) {
17468 date = NUMBER_STRING.test(date) ? int(date) : jsonStringToDate(date);
17471 if (isNumber(date)) {
17472 date = new Date(date);
17475 if (!isDate(date)) {
17480 match = DATE_FORMATS_SPLIT.exec(format);
17482 parts = concat(parts, match, 1);
17483 format = parts.pop();
17485 parts.push(format);
17490 if (timezone && timezone === 'UTC') {
17491 date = new Date(date.getTime());
17492 date.setMinutes(date.getMinutes() + date.getTimezoneOffset());
17494 forEach(parts, function(value) {
17495 fn = DATE_FORMATS[value];
17496 text += fn ? fn(date, $locale.DATETIME_FORMATS)
17497 : value.replace(/(^'|'$)/g, '').replace(/''/g, "'");
17511 * Allows you to convert a JavaScript object into JSON string.
17513 * This filter is mostly useful for debugging. When using the double curly {{value}} notation
17514 * the binding is automatically converted to JSON.
17516 * @param {*} object Any JavaScript object (including arrays and primitive types) to filter.
17517 * @param {number=} spacing The number of spaces to use per indentation, defaults to 2.
17518 * @returns {string} JSON string.
17523 <file name="index.html">
17524 <pre id="default-spacing">{{ {'name':'value'} | json }}</pre>
17525 <pre id="custom-spacing">{{ {'name':'value'} | json:4 }}</pre>
17527 <file name="protractor.js" type="protractor">
17528 it('should jsonify filtered objects', function() {
17529 expect(element(by.id('default-spacing')).getText()).toMatch(/\{\n "name": ?"value"\n}/);
17530 expect(element(by.id('custom-spacing')).getText()).toMatch(/\{\n "name": ?"value"\n}/);
17536 function jsonFilter() {
17537 return function(object, spacing) {
17538 if (isUndefined(spacing)) {
17541 return toJson(object, spacing);
17551 * Converts string to lowercase.
17552 * @see angular.lowercase
17554 var lowercaseFilter = valueFn(lowercase);
17562 * Converts string to uppercase.
17563 * @see angular.uppercase
17565 var uppercaseFilter = valueFn(uppercase);
17573 * Creates a new array or string containing only a specified number of elements. The elements
17574 * are taken from either the beginning or the end of the source array, string or number, as specified by
17575 * the value and sign (positive or negative) of `limit`. If a number is used as input, it is
17576 * converted to a string.
17578 * @param {Array|string|number} input Source array, string or number to be limited.
17579 * @param {string|number} limit The length of the returned array or string. If the `limit` number
17580 * is positive, `limit` number of items from the beginning of the source array/string are copied.
17581 * If the number is negative, `limit` number of items from the end of the source array/string
17582 * are copied. The `limit` will be trimmed if it exceeds `array.length`
17583 * @returns {Array|string} A new sub-array or substring of length `limit` or less if input array
17584 * had less than `limit` elements.
17587 <example module="limitToExample">
17588 <file name="index.html">
17590 angular.module('limitToExample', [])
17591 .controller('ExampleController', ['$scope', function($scope) {
17592 $scope.numbers = [1,2,3,4,5,6,7,8,9];
17593 $scope.letters = "abcdefghi";
17594 $scope.longNumber = 2345432342;
17595 $scope.numLimit = 3;
17596 $scope.letterLimit = 3;
17597 $scope.longNumberLimit = 3;
17600 <div ng-controller="ExampleController">
17601 Limit {{numbers}} to: <input type="number" step="1" ng-model="numLimit">
17602 <p>Output numbers: {{ numbers | limitTo:numLimit }}</p>
17603 Limit {{letters}} to: <input type="number" step="1" ng-model="letterLimit">
17604 <p>Output letters: {{ letters | limitTo:letterLimit }}</p>
17605 Limit {{longNumber}} to: <input type="number" step="1" ng-model="longNumberLimit">
17606 <p>Output long number: {{ longNumber | limitTo:longNumberLimit }}</p>
17609 <file name="protractor.js" type="protractor">
17610 var numLimitInput = element(by.model('numLimit'));
17611 var letterLimitInput = element(by.model('letterLimit'));
17612 var longNumberLimitInput = element(by.model('longNumberLimit'));
17613 var limitedNumbers = element(by.binding('numbers | limitTo:numLimit'));
17614 var limitedLetters = element(by.binding('letters | limitTo:letterLimit'));
17615 var limitedLongNumber = element(by.binding('longNumber | limitTo:longNumberLimit'));
17617 it('should limit the number array to first three items', function() {
17618 expect(numLimitInput.getAttribute('value')).toBe('3');
17619 expect(letterLimitInput.getAttribute('value')).toBe('3');
17620 expect(longNumberLimitInput.getAttribute('value')).toBe('3');
17621 expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3]');
17622 expect(limitedLetters.getText()).toEqual('Output letters: abc');
17623 expect(limitedLongNumber.getText()).toEqual('Output long number: 234');
17626 // There is a bug in safari and protractor that doesn't like the minus key
17627 // it('should update the output when -3 is entered', function() {
17628 // numLimitInput.clear();
17629 // numLimitInput.sendKeys('-3');
17630 // letterLimitInput.clear();
17631 // letterLimitInput.sendKeys('-3');
17632 // longNumberLimitInput.clear();
17633 // longNumberLimitInput.sendKeys('-3');
17634 // expect(limitedNumbers.getText()).toEqual('Output numbers: [7,8,9]');
17635 // expect(limitedLetters.getText()).toEqual('Output letters: ghi');
17636 // expect(limitedLongNumber.getText()).toEqual('Output long number: 342');
17639 it('should not exceed the maximum size of input array', function() {
17640 numLimitInput.clear();
17641 numLimitInput.sendKeys('100');
17642 letterLimitInput.clear();
17643 letterLimitInput.sendKeys('100');
17644 longNumberLimitInput.clear();
17645 longNumberLimitInput.sendKeys('100');
17646 expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3,4,5,6,7,8,9]');
17647 expect(limitedLetters.getText()).toEqual('Output letters: abcdefghi');
17648 expect(limitedLongNumber.getText()).toEqual('Output long number: 2345432342');
17653 function limitToFilter() {
17654 return function(input, limit) {
17655 if (isNumber(input)) input = input.toString();
17656 if (!isArray(input) && !isString(input)) return input;
17658 if (Math.abs(Number(limit)) === Infinity) {
17659 limit = Number(limit);
17661 limit = int(limit);
17664 //NaN check on limit
17666 return limit > 0 ? input.slice(0, limit) : input.slice(limit);
17668 return isString(input) ? "" : [];
17679 * Orders a specified `array` by the `expression` predicate. It is ordered alphabetically
17680 * for strings and numerically for numbers. Note: if you notice numbers are not being sorted
17681 * correctly, make sure they are actually being saved as numbers and not strings.
17683 * @param {Array} array The array to sort.
17684 * @param {function(*)|string|Array.<(function(*)|string)>=} expression A predicate to be
17685 * used by the comparator to determine the order of elements.
17689 * - `function`: Getter function. The result of this function will be sorted using the
17690 * `<`, `===`, `>` operator.
17691 * - `string`: An Angular expression. The result of this expression is used to compare elements
17692 * (for example `name` to sort by a property called `name` or `name.substr(0, 3)` to sort by
17693 * 3 first characters of a property called `name`). The result of a constant expression
17694 * is interpreted as a property name to be used in comparisons (for example `"special name"`
17695 * to sort object by the value of their `special name` property). An expression can be
17696 * optionally prefixed with `+` or `-` to control ascending or descending sort order
17697 * (for example, `+name` or `-name`). If no property is provided, (e.g. `'+'`) then the array
17698 * element itself is used to compare where sorting.
17699 * - `Array`: An array of function or string predicates. The first predicate in the array
17700 * is used for sorting, but when two items are equivalent, the next predicate is used.
17702 * If the predicate is missing or empty then it defaults to `'+'`.
17704 * @param {boolean=} reverse Reverse the order of the array.
17705 * @returns {Array} Sorted copy of the source array.
17709 * The example below demonstrates a simple ngRepeat, where the data is sorted
17710 * by age in descending order (predicate is set to `'-age'`).
17711 * `reverse` is not set, which means it defaults to `false`.
17712 <example module="orderByExample">
17713 <file name="index.html">
17715 angular.module('orderByExample', [])
17716 .controller('ExampleController', ['$scope', function($scope) {
17718 [{name:'John', phone:'555-1212', age:10},
17719 {name:'Mary', phone:'555-9876', age:19},
17720 {name:'Mike', phone:'555-4321', age:21},
17721 {name:'Adam', phone:'555-5678', age:35},
17722 {name:'Julie', phone:'555-8765', age:29}];
17725 <div ng-controller="ExampleController">
17726 <table class="friend">
17729 <th>Phone Number</th>
17732 <tr ng-repeat="friend in friends | orderBy:'-age'">
17733 <td>{{friend.name}}</td>
17734 <td>{{friend.phone}}</td>
17735 <td>{{friend.age}}</td>
17742 * The predicate and reverse parameters can be controlled dynamically through scope properties,
17743 * as shown in the next example.
17745 <example module="orderByExample">
17746 <file name="index.html">
17748 angular.module('orderByExample', [])
17749 .controller('ExampleController', ['$scope', function($scope) {
17751 [{name:'John', phone:'555-1212', age:10},
17752 {name:'Mary', phone:'555-9876', age:19},
17753 {name:'Mike', phone:'555-4321', age:21},
17754 {name:'Adam', phone:'555-5678', age:35},
17755 {name:'Julie', phone:'555-8765', age:29}];
17756 $scope.predicate = '-age';
17759 <div ng-controller="ExampleController">
17760 <pre>Sorting predicate = {{predicate}}; reverse = {{reverse}}</pre>
17762 [ <a href="" ng-click="predicate=''">unsorted</a> ]
17763 <table class="friend">
17765 <th><a href="" ng-click="predicate = 'name'; reverse=false">Name</a>
17766 (<a href="" ng-click="predicate = '-name'; reverse=false">^</a>)</th>
17767 <th><a href="" ng-click="predicate = 'phone'; reverse=!reverse">Phone Number</a></th>
17768 <th><a href="" ng-click="predicate = 'age'; reverse=!reverse">Age</a></th>
17770 <tr ng-repeat="friend in friends | orderBy:predicate:reverse">
17771 <td>{{friend.name}}</td>
17772 <td>{{friend.phone}}</td>
17773 <td>{{friend.age}}</td>
17780 * It's also possible to call the orderBy filter manually, by injecting `$filter`, retrieving the
17781 * filter routine with `$filter('orderBy')`, and calling the returned filter routine with the
17782 * desired parameters.
17787 <example module="orderByExample">
17788 <file name="index.html">
17789 <div ng-controller="ExampleController">
17790 <table class="friend">
17792 <th><a href="" ng-click="reverse=false;order('name', false)">Name</a>
17793 (<a href="" ng-click="order('-name',false)">^</a>)</th>
17794 <th><a href="" ng-click="reverse=!reverse;order('phone', reverse)">Phone Number</a></th>
17795 <th><a href="" ng-click="reverse=!reverse;order('age',reverse)">Age</a></th>
17797 <tr ng-repeat="friend in friends">
17798 <td>{{friend.name}}</td>
17799 <td>{{friend.phone}}</td>
17800 <td>{{friend.age}}</td>
17806 <file name="script.js">
17807 angular.module('orderByExample', [])
17808 .controller('ExampleController', ['$scope', '$filter', function($scope, $filter) {
17809 var orderBy = $filter('orderBy');
17811 { name: 'John', phone: '555-1212', age: 10 },
17812 { name: 'Mary', phone: '555-9876', age: 19 },
17813 { name: 'Mike', phone: '555-4321', age: 21 },
17814 { name: 'Adam', phone: '555-5678', age: 35 },
17815 { name: 'Julie', phone: '555-8765', age: 29 }
17817 $scope.order = function(predicate, reverse) {
17818 $scope.friends = orderBy($scope.friends, predicate, reverse);
17820 $scope.order('-age',false);
17825 orderByFilter.$inject = ['$parse'];
17826 function orderByFilter($parse) {
17827 return function(array, sortPredicate, reverseOrder) {
17828 if (!(isArrayLike(array))) return array;
17829 sortPredicate = isArray(sortPredicate) ? sortPredicate : [sortPredicate];
17830 if (sortPredicate.length === 0) { sortPredicate = ['+']; }
17831 sortPredicate = sortPredicate.map(function(predicate) {
17832 var descending = false, get = predicate || identity;
17833 if (isString(predicate)) {
17834 if ((predicate.charAt(0) == '+' || predicate.charAt(0) == '-')) {
17835 descending = predicate.charAt(0) == '-';
17836 predicate = predicate.substring(1);
17838 if (predicate === '') {
17839 // Effectively no predicate was passed so we compare identity
17840 return reverseComparator(compare, descending);
17842 get = $parse(predicate);
17843 if (get.constant) {
17845 return reverseComparator(function(a, b) {
17846 return compare(a[key], b[key]);
17850 return reverseComparator(function(a, b) {
17851 return compare(get(a),get(b));
17854 return slice.call(array).sort(reverseComparator(comparator, reverseOrder));
17856 function comparator(o1, o2) {
17857 for (var i = 0; i < sortPredicate.length; i++) {
17858 var comp = sortPredicate[i](o1, o2);
17859 if (comp !== 0) return comp;
17863 function reverseComparator(comp, descending) {
17865 ? function(a, b) {return comp(b,a);}
17869 function isPrimitive(value) {
17870 switch (typeof value) {
17871 case 'number': /* falls through */
17872 case 'boolean': /* falls through */
17880 function objectToString(value) {
17881 if (value === null) return 'null';
17882 if (typeof value.valueOf === 'function') {
17883 value = value.valueOf();
17884 if (isPrimitive(value)) return value;
17886 if (typeof value.toString === 'function') {
17887 value = value.toString();
17888 if (isPrimitive(value)) return value;
17893 function compare(v1, v2) {
17894 var t1 = typeof v1;
17895 var t2 = typeof v2;
17896 if (t1 === t2 && t1 === "object") {
17897 v1 = objectToString(v1);
17898 v2 = objectToString(v2);
17901 if (t1 === "string") {
17902 v1 = v1.toLowerCase();
17903 v2 = v2.toLowerCase();
17905 if (v1 === v2) return 0;
17906 return v1 < v2 ? -1 : 1;
17908 return t1 < t2 ? -1 : 1;
17914 function ngDirective(directive) {
17915 if (isFunction(directive)) {
17920 directive.restrict = directive.restrict || 'AC';
17921 return valueFn(directive);
17930 * Modifies the default behavior of the html A tag so that the default action is prevented when
17931 * the href attribute is empty.
17933 * This change permits the easy creation of action links with the `ngClick` directive
17934 * without changing the location or causing page reloads, e.g.:
17935 * `<a href="" ng-click="list.addItem()">Add Item</a>`
17937 var htmlAnchorDirective = valueFn({
17939 compile: function(element, attr) {
17940 if (!attr.href && !attr.xlinkHref && !attr.name) {
17941 return function(scope, element) {
17942 // If the linked element is not an anchor tag anymore, do nothing
17943 if (element[0].nodeName.toLowerCase() !== 'a') return;
17945 // SVGAElement does not use the href attribute, but rather the 'xlinkHref' attribute.
17946 var href = toString.call(element.prop('href')) === '[object SVGAnimatedString]' ?
17947 'xlink:href' : 'href';
17948 element.on('click', function(event) {
17949 // if we have no href url, then don't navigate anywhere.
17950 if (!element.attr(href)) {
17951 event.preventDefault();
17966 * Using Angular markup like `{{hash}}` in an href attribute will
17967 * make the link go to the wrong URL if the user clicks it before
17968 * Angular has a chance to replace the `{{hash}}` markup with its
17969 * value. Until Angular replaces the markup the link will be broken
17970 * and will most likely return a 404 error. The `ngHref` directive
17971 * solves this problem.
17973 * The wrong way to write it:
17975 * <a href="http://www.gravatar.com/avatar/{{hash}}">link1</a>
17978 * The correct way to write it:
17980 * <a ng-href="http://www.gravatar.com/avatar/{{hash}}">link1</a>
17984 * @param {template} ngHref any string which can contain `{{}}` markup.
17987 * This example shows various combinations of `href`, `ng-href` and `ng-click` attributes
17988 * in links and their different behaviors:
17990 <file name="index.html">
17991 <input ng-model="value" /><br />
17992 <a id="link-1" href ng-click="value = 1">link 1</a> (link, don't reload)<br />
17993 <a id="link-2" href="" ng-click="value = 2">link 2</a> (link, don't reload)<br />
17994 <a id="link-3" ng-href="/{{'123'}}">link 3</a> (link, reload!)<br />
17995 <a id="link-4" href="" name="xx" ng-click="value = 4">anchor</a> (link, don't reload)<br />
17996 <a id="link-5" name="xxx" ng-click="value = 5">anchor</a> (no link)<br />
17997 <a id="link-6" ng-href="{{value}}">link</a> (link, change location)
17999 <file name="protractor.js" type="protractor">
18000 it('should execute ng-click but not reload when href without value', function() {
18001 element(by.id('link-1')).click();
18002 expect(element(by.model('value')).getAttribute('value')).toEqual('1');
18003 expect(element(by.id('link-1')).getAttribute('href')).toBe('');
18006 it('should execute ng-click but not reload when href empty string', function() {
18007 element(by.id('link-2')).click();
18008 expect(element(by.model('value')).getAttribute('value')).toEqual('2');
18009 expect(element(by.id('link-2')).getAttribute('href')).toBe('');
18012 it('should execute ng-click and change url when ng-href specified', function() {
18013 expect(element(by.id('link-3')).getAttribute('href')).toMatch(/\/123$/);
18015 element(by.id('link-3')).click();
18017 // At this point, we navigate away from an Angular page, so we need
18018 // to use browser.driver to get the base webdriver.
18020 browser.wait(function() {
18021 return browser.driver.getCurrentUrl().then(function(url) {
18022 return url.match(/\/123$/);
18024 }, 5000, 'page should navigate to /123');
18027 xit('should execute ng-click but not reload when href empty string and name specified', function() {
18028 element(by.id('link-4')).click();
18029 expect(element(by.model('value')).getAttribute('value')).toEqual('4');
18030 expect(element(by.id('link-4')).getAttribute('href')).toBe('');
18033 it('should execute ng-click but not reload when no href but name specified', function() {
18034 element(by.id('link-5')).click();
18035 expect(element(by.model('value')).getAttribute('value')).toEqual('5');
18036 expect(element(by.id('link-5')).getAttribute('href')).toBe(null);
18039 it('should only change url when only ng-href', function() {
18040 element(by.model('value')).clear();
18041 element(by.model('value')).sendKeys('6');
18042 expect(element(by.id('link-6')).getAttribute('href')).toMatch(/\/6$/);
18044 element(by.id('link-6')).click();
18046 // At this point, we navigate away from an Angular page, so we need
18047 // to use browser.driver to get the base webdriver.
18048 browser.wait(function() {
18049 return browser.driver.getCurrentUrl().then(function(url) {
18050 return url.match(/\/6$/);
18052 }, 5000, 'page should navigate to /6');
18065 * Using Angular markup like `{{hash}}` in a `src` attribute doesn't
18066 * work right: The browser will fetch from the URL with the literal
18067 * text `{{hash}}` until Angular replaces the expression inside
18068 * `{{hash}}`. The `ngSrc` directive solves this problem.
18070 * The buggy way to write it:
18072 * <img src="http://www.gravatar.com/avatar/{{hash}}"/>
18075 * The correct way to write it:
18077 * <img ng-src="http://www.gravatar.com/avatar/{{hash}}"/>
18081 * @param {template} ngSrc any string which can contain `{{}}` markup.
18091 * Using Angular markup like `{{hash}}` in a `srcset` attribute doesn't
18092 * work right: The browser will fetch from the URL with the literal
18093 * text `{{hash}}` until Angular replaces the expression inside
18094 * `{{hash}}`. The `ngSrcset` directive solves this problem.
18096 * The buggy way to write it:
18098 * <img srcset="http://www.gravatar.com/avatar/{{hash}} 2x"/>
18101 * The correct way to write it:
18103 * <img ng-srcset="http://www.gravatar.com/avatar/{{hash}} 2x"/>
18107 * @param {template} ngSrcset any string which can contain `{{}}` markup.
18118 * This directive sets the `disabled` attribute on the element if the
18119 * {@link guide/expression expression} inside `ngDisabled` evaluates to truthy.
18121 * A special directive is necessary because we cannot use interpolation inside the `disabled`
18122 * attribute. The following example would make the button enabled on Chrome/Firefox
18123 * but not on older IEs:
18126 * <!-- See below for an example of ng-disabled being used correctly -->
18127 * <div ng-init="isDisabled = false">
18128 * <button disabled="{{isDisabled}}">Disabled</button>
18132 * This is because the HTML specification does not require browsers to preserve the values of
18133 * boolean attributes such as `disabled` (Their presence means true and their absence means false.)
18134 * If we put an Angular interpolation expression into such an attribute then the
18135 * binding information would be lost when the browser removes the attribute.
18139 <file name="index.html">
18140 Click me to toggle: <input type="checkbox" ng-model="checked"><br/>
18141 <button ng-model="button" ng-disabled="checked">Button</button>
18143 <file name="protractor.js" type="protractor">
18144 it('should toggle button', function() {
18145 expect(element(by.css('button')).getAttribute('disabled')).toBeFalsy();
18146 element(by.model('checked')).click();
18147 expect(element(by.css('button')).getAttribute('disabled')).toBeTruthy();
18153 * @param {expression} ngDisabled If the {@link guide/expression expression} is truthy,
18154 * then the `disabled` attribute will be set on the element
18165 * The HTML specification does not require browsers to preserve the values of boolean attributes
18166 * such as checked. (Their presence means true and their absence means false.)
18167 * If we put an Angular interpolation expression into such an attribute then the
18168 * binding information would be lost when the browser removes the attribute.
18169 * The `ngChecked` directive solves this problem for the `checked` attribute.
18170 * This complementary directive is not removed by the browser and so provides
18171 * a permanent reliable place to store the binding information.
18174 <file name="index.html">
18175 Check me to check both: <input type="checkbox" ng-model="master"><br/>
18176 <input id="checkSlave" type="checkbox" ng-checked="master">
18178 <file name="protractor.js" type="protractor">
18179 it('should check both checkBoxes', function() {
18180 expect(element(by.id('checkSlave')).getAttribute('checked')).toBeFalsy();
18181 element(by.model('master')).click();
18182 expect(element(by.id('checkSlave')).getAttribute('checked')).toBeTruthy();
18188 * @param {expression} ngChecked If the {@link guide/expression expression} is truthy,
18189 * then special attribute "checked" will be set on the element
18200 * The HTML specification does not require browsers to preserve the values of boolean attributes
18201 * such as readonly. (Their presence means true and their absence means false.)
18202 * If we put an Angular interpolation expression into such an attribute then the
18203 * binding information would be lost when the browser removes the attribute.
18204 * The `ngReadonly` directive solves this problem for the `readonly` attribute.
18205 * This complementary directive is not removed by the browser and so provides
18206 * a permanent reliable place to store the binding information.
18209 <file name="index.html">
18210 Check me to make text readonly: <input type="checkbox" ng-model="checked"><br/>
18211 <input type="text" ng-readonly="checked" value="I'm Angular"/>
18213 <file name="protractor.js" type="protractor">
18214 it('should toggle readonly attr', function() {
18215 expect(element(by.css('[type="text"]')).getAttribute('readonly')).toBeFalsy();
18216 element(by.model('checked')).click();
18217 expect(element(by.css('[type="text"]')).getAttribute('readonly')).toBeTruthy();
18223 * @param {expression} ngReadonly If the {@link guide/expression expression} is truthy,
18224 * then special attribute "readonly" will be set on the element
18235 * The HTML specification does not require browsers to preserve the values of boolean attributes
18236 * such as selected. (Their presence means true and their absence means false.)
18237 * If we put an Angular interpolation expression into such an attribute then the
18238 * binding information would be lost when the browser removes the attribute.
18239 * The `ngSelected` directive solves this problem for the `selected` attribute.
18240 * This complementary directive is not removed by the browser and so provides
18241 * a permanent reliable place to store the binding information.
18245 <file name="index.html">
18246 Check me to select: <input type="checkbox" ng-model="selected"><br/>
18248 <option>Hello!</option>
18249 <option id="greet" ng-selected="selected">Greetings!</option>
18252 <file name="protractor.js" type="protractor">
18253 it('should select Greetings!', function() {
18254 expect(element(by.id('greet')).getAttribute('selected')).toBeFalsy();
18255 element(by.model('selected')).click();
18256 expect(element(by.id('greet')).getAttribute('selected')).toBeTruthy();
18262 * @param {expression} ngSelected If the {@link guide/expression expression} is truthy,
18263 * then special attribute "selected" will be set on the element
18273 * The HTML specification does not require browsers to preserve the values of boolean attributes
18274 * such as open. (Their presence means true and their absence means false.)
18275 * If we put an Angular interpolation expression into such an attribute then the
18276 * binding information would be lost when the browser removes the attribute.
18277 * The `ngOpen` directive solves this problem for the `open` attribute.
18278 * This complementary directive is not removed by the browser and so provides
18279 * a permanent reliable place to store the binding information.
18282 <file name="index.html">
18283 Check me check multiple: <input type="checkbox" ng-model="open"><br/>
18284 <details id="details" ng-open="open">
18285 <summary>Show/Hide me</summary>
18288 <file name="protractor.js" type="protractor">
18289 it('should toggle open', function() {
18290 expect(element(by.id('details')).getAttribute('open')).toBeFalsy();
18291 element(by.model('open')).click();
18292 expect(element(by.id('details')).getAttribute('open')).toBeTruthy();
18298 * @param {expression} ngOpen If the {@link guide/expression expression} is truthy,
18299 * then special attribute "open" will be set on the element
18302 var ngAttributeAliasDirectives = {};
18305 // boolean attrs are evaluated
18306 forEach(BOOLEAN_ATTR, function(propName, attrName) {
18307 // binding to multiple is not supported
18308 if (propName == "multiple") return;
18310 var normalized = directiveNormalize('ng-' + attrName);
18311 ngAttributeAliasDirectives[normalized] = function() {
18315 link: function(scope, element, attr) {
18316 scope.$watch(attr[normalized], function ngBooleanAttrWatchAction(value) {
18317 attr.$set(attrName, !!value);
18324 // aliased input attrs are evaluated
18325 forEach(ALIASED_ATTR, function(htmlAttr, ngAttr) {
18326 ngAttributeAliasDirectives[ngAttr] = function() {
18329 link: function(scope, element, attr) {
18330 //special case ngPattern when a literal regular expression value
18331 //is used as the expression (this way we don't have to watch anything).
18332 if (ngAttr === "ngPattern" && attr.ngPattern.charAt(0) == "/") {
18333 var match = attr.ngPattern.match(REGEX_STRING_REGEXP);
18335 attr.$set("ngPattern", new RegExp(match[1], match[2]));
18340 scope.$watch(attr[ngAttr], function ngAttrAliasWatchAction(value) {
18341 attr.$set(ngAttr, value);
18348 // ng-src, ng-srcset, ng-href are interpolated
18349 forEach(['src', 'srcset', 'href'], function(attrName) {
18350 var normalized = directiveNormalize('ng-' + attrName);
18351 ngAttributeAliasDirectives[normalized] = function() {
18353 priority: 99, // it needs to run after the attributes are interpolated
18354 link: function(scope, element, attr) {
18355 var propName = attrName,
18358 if (attrName === 'href' &&
18359 toString.call(element.prop('href')) === '[object SVGAnimatedString]') {
18360 name = 'xlinkHref';
18361 attr.$attr[name] = 'xlink:href';
18365 attr.$observe(normalized, function(value) {
18367 if (attrName === 'href') {
18368 attr.$set(name, null);
18373 attr.$set(name, value);
18375 // on IE, if "ng:src" directive declaration is used and "src" attribute doesn't exist
18376 // then calling element.setAttribute('src', 'foo') doesn't do anything, so we need
18377 // to set the property as well to achieve the desired effect.
18378 // we use attr[attrName] value since $set can sanitize the url.
18379 if (msie && propName) element.prop(propName, attr[name]);
18386 /* global -nullFormCtrl, -SUBMITTED_CLASS, addSetValidityMethod: true
18388 var nullFormCtrl = {
18390 $$renameControl: nullFormRenameControl,
18391 $removeControl: noop,
18392 $setValidity: noop,
18394 $setPristine: noop,
18395 $setSubmitted: noop
18397 SUBMITTED_CLASS = 'ng-submitted';
18399 function nullFormRenameControl(control, name) {
18400 control.$name = name;
18405 * @name form.FormController
18407 * @property {boolean} $pristine True if user has not interacted with the form yet.
18408 * @property {boolean} $dirty True if user has already interacted with the form.
18409 * @property {boolean} $valid True if all of the containing forms and controls are valid.
18410 * @property {boolean} $invalid True if at least one containing control or form is invalid.
18411 * @property {boolean} $submitted True if user has submitted the form even if its invalid.
18413 * @property {Object} $error Is an object hash, containing references to controls or
18414 * forms with failing validators, where:
18416 * - keys are validation tokens (error names),
18417 * - values are arrays of controls or forms that have a failing validator for given error name.
18419 * Built-in validation tokens:
18431 * - `datetimelocal`
18437 * `FormController` keeps track of all its controls and nested forms as well as the state of them,
18438 * such as being valid/invalid or dirty/pristine.
18440 * Each {@link ng.directive:form form} directive creates an instance
18441 * of `FormController`.
18444 //asks for $scope to fool the BC controller module
18445 FormController.$inject = ['$element', '$attrs', '$scope', '$animate', '$interpolate'];
18446 function FormController(element, attrs, $scope, $animate, $interpolate) {
18450 var parentForm = form.$$parentForm = element.parent().controller('form') || nullFormCtrl;
18454 form.$$success = {};
18455 form.$pending = undefined;
18456 form.$name = $interpolate(attrs.name || attrs.ngForm || '')($scope);
18457 form.$dirty = false;
18458 form.$pristine = true;
18459 form.$valid = true;
18460 form.$invalid = false;
18461 form.$submitted = false;
18463 parentForm.$addControl(form);
18467 * @name form.FormController#$rollbackViewValue
18470 * Rollback all form controls pending updates to the `$modelValue`.
18472 * Updates may be pending by a debounced event or because the input is waiting for a some future
18473 * event defined in `ng-model-options`. This method is typically needed by the reset button of
18474 * a form that uses `ng-model-options` to pend updates.
18476 form.$rollbackViewValue = function() {
18477 forEach(controls, function(control) {
18478 control.$rollbackViewValue();
18484 * @name form.FormController#$commitViewValue
18487 * Commit all form controls pending updates to the `$modelValue`.
18489 * Updates may be pending by a debounced event or because the input is waiting for a some future
18490 * event defined in `ng-model-options`. This method is rarely needed as `NgModelController`
18491 * usually handles calling this in response to input events.
18493 form.$commitViewValue = function() {
18494 forEach(controls, function(control) {
18495 control.$commitViewValue();
18501 * @name form.FormController#$addControl
18504 * Register a control with the form.
18506 * Input elements using ngModelController do this automatically when they are linked.
18508 form.$addControl = function(control) {
18509 // Breaking change - before, inputs whose name was "hasOwnProperty" were quietly ignored
18510 // and not added to the scope. Now we throw an error.
18511 assertNotHasOwnProperty(control.$name, 'input');
18512 controls.push(control);
18514 if (control.$name) {
18515 form[control.$name] = control;
18519 // Private API: rename a form control
18520 form.$$renameControl = function(control, newName) {
18521 var oldName = control.$name;
18523 if (form[oldName] === control) {
18524 delete form[oldName];
18526 form[newName] = control;
18527 control.$name = newName;
18532 * @name form.FormController#$removeControl
18535 * Deregister a control from the form.
18537 * Input elements using ngModelController do this automatically when they are destroyed.
18539 form.$removeControl = function(control) {
18540 if (control.$name && form[control.$name] === control) {
18541 delete form[control.$name];
18543 forEach(form.$pending, function(value, name) {
18544 form.$setValidity(name, null, control);
18546 forEach(form.$error, function(value, name) {
18547 form.$setValidity(name, null, control);
18549 forEach(form.$$success, function(value, name) {
18550 form.$setValidity(name, null, control);
18553 arrayRemove(controls, control);
18559 * @name form.FormController#$setValidity
18562 * Sets the validity of a form control.
18564 * This method will also propagate to parent forms.
18566 addSetValidityMethod({
18569 set: function(object, property, controller) {
18570 var list = object[property];
18572 object[property] = [controller];
18574 var index = list.indexOf(controller);
18575 if (index === -1) {
18576 list.push(controller);
18580 unset: function(object, property, controller) {
18581 var list = object[property];
18585 arrayRemove(list, controller);
18586 if (list.length === 0) {
18587 delete object[property];
18590 parentForm: parentForm,
18596 * @name form.FormController#$setDirty
18599 * Sets the form to a dirty state.
18601 * This method can be called to add the 'ng-dirty' class and set the form to a dirty
18602 * state (ng-dirty class). This method will also propagate to parent forms.
18604 form.$setDirty = function() {
18605 $animate.removeClass(element, PRISTINE_CLASS);
18606 $animate.addClass(element, DIRTY_CLASS);
18607 form.$dirty = true;
18608 form.$pristine = false;
18609 parentForm.$setDirty();
18614 * @name form.FormController#$setPristine
18617 * Sets the form to its pristine state.
18619 * This method can be called to remove the 'ng-dirty' class and set the form to its pristine
18620 * state (ng-pristine class). This method will also propagate to all the controls contained
18623 * Setting a form back to a pristine state is often useful when we want to 'reuse' a form after
18624 * saving or resetting it.
18626 form.$setPristine = function() {
18627 $animate.setClass(element, PRISTINE_CLASS, DIRTY_CLASS + ' ' + SUBMITTED_CLASS);
18628 form.$dirty = false;
18629 form.$pristine = true;
18630 form.$submitted = false;
18631 forEach(controls, function(control) {
18632 control.$setPristine();
18638 * @name form.FormController#$setUntouched
18641 * Sets the form to its untouched state.
18643 * This method can be called to remove the 'ng-touched' class and set the form controls to their
18644 * untouched state (ng-untouched class).
18646 * Setting a form controls back to their untouched state is often useful when setting the form
18647 * back to its pristine state.
18649 form.$setUntouched = function() {
18650 forEach(controls, function(control) {
18651 control.$setUntouched();
18657 * @name form.FormController#$setSubmitted
18660 * Sets the form to its submitted state.
18662 form.$setSubmitted = function() {
18663 $animate.addClass(element, SUBMITTED_CLASS);
18664 form.$submitted = true;
18665 parentForm.$setSubmitted();
18675 * Nestable alias of {@link ng.directive:form `form`} directive. HTML
18676 * does not allow nesting of form elements. It is useful to nest forms, for example if the validity of a
18677 * sub-group of controls needs to be determined.
18679 * Note: the purpose of `ngForm` is to group controls,
18680 * but not to be a replacement for the `<form>` tag with all of its capabilities
18681 * (e.g. posting to the server, ...).
18683 * @param {string=} ngForm|name Name of the form. If specified, the form controller will be published into
18684 * related scope, under this name.
18694 * Directive that instantiates
18695 * {@link form.FormController FormController}.
18697 * If the `name` attribute is specified, the form controller is published onto the current scope under
18700 * # Alias: {@link ng.directive:ngForm `ngForm`}
18702 * In Angular, forms can be nested. This means that the outer form is valid when all of the child
18703 * forms are valid as well. However, browsers do not allow nesting of `<form>` elements, so
18704 * Angular provides the {@link ng.directive:ngForm `ngForm`} directive which behaves identically to
18705 * `<form>` but can be nested. This allows you to have nested forms, which is very useful when
18706 * using Angular validation directives in forms that are dynamically generated using the
18707 * {@link ng.directive:ngRepeat `ngRepeat`} directive. Since you cannot dynamically generate the `name`
18708 * attribute of input elements using interpolation, you have to wrap each set of repeated inputs in an
18709 * `ngForm` directive and nest these in an outer `form` element.
18713 * - `ng-valid` is set if the form is valid.
18714 * - `ng-invalid` is set if the form is invalid.
18715 * - `ng-pristine` is set if the form is pristine.
18716 * - `ng-dirty` is set if the form is dirty.
18717 * - `ng-submitted` is set if the form was submitted.
18719 * Keep in mind that ngAnimate can detect each of these classes when added and removed.
18722 * # Submitting a form and preventing the default action
18724 * Since the role of forms in client-side Angular applications is different than in classical
18725 * roundtrip apps, it is desirable for the browser not to translate the form submission into a full
18726 * page reload that sends the data to the server. Instead some javascript logic should be triggered
18727 * to handle the form submission in an application-specific way.
18729 * For this reason, Angular prevents the default action (form submission to the server) unless the
18730 * `<form>` element has an `action` attribute specified.
18732 * You can use one of the following two ways to specify what javascript method should be called when
18733 * a form is submitted:
18735 * - {@link ng.directive:ngSubmit ngSubmit} directive on the form element
18736 * - {@link ng.directive:ngClick ngClick} directive on the first
18737 * button or input field of type submit (input[type=submit])
18739 * To prevent double execution of the handler, use only one of the {@link ng.directive:ngSubmit ngSubmit}
18740 * or {@link ng.directive:ngClick ngClick} directives.
18741 * This is because of the following form submission rules in the HTML specification:
18743 * - If a form has only one input field then hitting enter in this field triggers form submit
18745 * - if a form has 2+ input fields and no buttons or input[type=submit] then hitting enter
18746 * doesn't trigger submit
18747 * - if a form has one or more input fields and one or more buttons or input[type=submit] then
18748 * hitting enter in any of the input fields will trigger the click handler on the *first* button or
18749 * input[type=submit] (`ngClick`) *and* a submit handler on the enclosing form (`ngSubmit`)
18751 * Any pending `ngModelOptions` changes will take place immediately when an enclosing form is
18752 * submitted. Note that `ngClick` events will occur before the model is updated. Use `ngSubmit`
18753 * to have access to the updated model.
18755 * ## Animation Hooks
18757 * Animations in ngForm are triggered when any of the associated CSS classes are added and removed.
18758 * These classes are: `.ng-pristine`, `.ng-dirty`, `.ng-invalid` and `.ng-valid` as well as any
18759 * other validations that are performed within the form. Animations in ngForm are similar to how
18760 * they work in ngClass and animations can be hooked into using CSS transitions, keyframes as well
18761 * as JS animations.
18763 * The following example shows a simple way to utilize CSS transitions to style a form element
18764 * that has been rendered as invalid after it has been validated:
18767 * //be sure to include ngAnimate as a module to hook into more
18768 * //advanced animations
18770 * transition:0.5s linear all;
18771 * background: white;
18773 * .my-form.ng-invalid {
18780 <example deps="angular-animate.js" animations="true" fixBase="true" module="formExample">
18781 <file name="index.html">
18783 angular.module('formExample', [])
18784 .controller('FormController', ['$scope', function($scope) {
18785 $scope.userType = 'guest';
18790 -webkit-transition:all linear 0.5s;
18791 transition:all linear 0.5s;
18792 background: transparent;
18794 .my-form.ng-invalid {
18798 <form name="myForm" ng-controller="FormController" class="my-form">
18799 userType: <input name="input" ng-model="userType" required>
18800 <span class="error" ng-show="myForm.input.$error.required">Required!</span><br>
18801 <code>userType = {{userType}}</code><br>
18802 <code>myForm.input.$valid = {{myForm.input.$valid}}</code><br>
18803 <code>myForm.input.$error = {{myForm.input.$error}}</code><br>
18804 <code>myForm.$valid = {{myForm.$valid}}</code><br>
18805 <code>myForm.$error.required = {{!!myForm.$error.required}}</code><br>
18808 <file name="protractor.js" type="protractor">
18809 it('should initialize to model', function() {
18810 var userType = element(by.binding('userType'));
18811 var valid = element(by.binding('myForm.input.$valid'));
18813 expect(userType.getText()).toContain('guest');
18814 expect(valid.getText()).toContain('true');
18817 it('should be invalid if empty', function() {
18818 var userType = element(by.binding('userType'));
18819 var valid = element(by.binding('myForm.input.$valid'));
18820 var userInput = element(by.model('userType'));
18823 userInput.sendKeys('');
18825 expect(userType.getText()).toEqual('userType =');
18826 expect(valid.getText()).toContain('false');
18831 * @param {string=} name Name of the form. If specified, the form controller will be published into
18832 * related scope, under this name.
18834 var formDirectiveFactory = function(isNgForm) {
18835 return ['$timeout', function($timeout) {
18836 var formDirective = {
18838 restrict: isNgForm ? 'EAC' : 'E',
18839 controller: FormController,
18840 compile: function ngFormCompile(formElement, attr) {
18841 // Setup initial state of the control
18842 formElement.addClass(PRISTINE_CLASS).addClass(VALID_CLASS);
18844 var nameAttr = attr.name ? 'name' : (isNgForm && attr.ngForm ? 'ngForm' : false);
18847 pre: function ngFormPreLink(scope, formElement, attr, controller) {
18848 // if `action` attr is not present on the form, prevent the default action (submission)
18849 if (!('action' in attr)) {
18850 // we can't use jq events because if a form is destroyed during submission the default
18851 // action is not prevented. see #1238
18853 // IE 9 is not affected because it doesn't fire a submit event and try to do a full
18854 // page reload if the form was destroyed by submission of the form via a click handler
18855 // on a button in the form. Looks like an IE9 specific bug.
18856 var handleFormSubmission = function(event) {
18857 scope.$apply(function() {
18858 controller.$commitViewValue();
18859 controller.$setSubmitted();
18862 event.preventDefault();
18865 addEventListenerFn(formElement[0], 'submit', handleFormSubmission);
18867 // unregister the preventDefault listener so that we don't not leak memory but in a
18868 // way that will achieve the prevention of the default action.
18869 formElement.on('$destroy', function() {
18870 $timeout(function() {
18871 removeEventListenerFn(formElement[0], 'submit', handleFormSubmission);
18876 var parentFormCtrl = controller.$$parentForm;
18879 setter(scope, null, controller.$name, controller, controller.$name);
18880 attr.$observe(nameAttr, function(newValue) {
18881 if (controller.$name === newValue) return;
18882 setter(scope, null, controller.$name, undefined, controller.$name);
18883 parentFormCtrl.$$renameControl(controller, newValue);
18884 setter(scope, null, controller.$name, controller, controller.$name);
18887 formElement.on('$destroy', function() {
18888 parentFormCtrl.$removeControl(controller);
18890 setter(scope, null, attr[nameAttr], undefined, controller.$name);
18892 extend(controller, nullFormCtrl); //stop propagating child destruction handlers upwards
18899 return formDirective;
18903 var formDirective = formDirectiveFactory();
18904 var ngFormDirective = formDirectiveFactory(true);
18906 /* global VALID_CLASS: false,
18907 INVALID_CLASS: false,
18908 PRISTINE_CLASS: false,
18909 DIRTY_CLASS: false,
18910 UNTOUCHED_CLASS: false,
18911 TOUCHED_CLASS: false,
18912 ngModelMinErr: false,
18915 // Regex code is obtained from SO: https://stackoverflow.com/questions/3143070/javascript-regex-iso-datetime#answer-3143231
18916 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)/;
18917 var URL_REGEXP = /^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/;
18918 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;
18919 var NUMBER_REGEXP = /^\s*(\-|\+)?(\d+|(\d*(\.\d*)))\s*$/;
18920 var DATE_REGEXP = /^(\d{4})-(\d{2})-(\d{2})$/;
18921 var DATETIMELOCAL_REGEXP = /^(\d{4})-(\d\d)-(\d\d)T(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
18922 var WEEK_REGEXP = /^(\d{4})-W(\d\d)$/;
18923 var MONTH_REGEXP = /^(\d{4})-(\d\d)$/;
18924 var TIME_REGEXP = /^(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
18930 * @name input[text]
18933 * Standard HTML text input with angular data binding, inherited by most of the `input` elements.
18936 * @param {string} ngModel Assignable angular expression to data-bind to.
18937 * @param {string=} name Property name of the form under which the control is published.
18938 * @param {string=} required Adds `required` validation error key if the value is not entered.
18939 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
18940 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
18941 * `required` when you want to data-bind to the `required` attribute.
18942 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
18944 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
18945 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
18947 * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
18948 * that contains the regular expression body that will be converted to a regular expression
18949 * as in the ngPattern directive.
18950 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
18951 * a RegExp found by evaluating the Angular expression given in the attribute value.
18952 * If the expression evaluates to a RegExp object then this is used directly.
18953 * If the expression is a string then it will be converted to a RegExp after wrapping it in `^` and `$`
18954 * characters. For instance, `"abc"` will be converted to `new RegExp('^abc$')`.
18955 * @param {string=} ngChange Angular expression to be executed when input changes due to user
18956 * interaction with the input element.
18957 * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
18958 * This parameter is ignored for input[type=password] controls, which will never trim the
18962 <example name="text-input-directive" module="textInputExample">
18963 <file name="index.html">
18965 angular.module('textInputExample', [])
18966 .controller('ExampleController', ['$scope', function($scope) {
18969 word: /^\s*\w*\s*$/
18973 <form name="myForm" ng-controller="ExampleController">
18974 Single word: <input type="text" name="input" ng-model="example.text"
18975 ng-pattern="example.word" required ng-trim="false">
18976 <span class="error" ng-show="myForm.input.$error.required">
18978 <span class="error" ng-show="myForm.input.$error.pattern">
18979 Single word only!</span>
18981 <tt>text = {{example.text}}</tt><br/>
18982 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
18983 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
18984 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
18985 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
18988 <file name="protractor.js" type="protractor">
18989 var text = element(by.binding('example.text'));
18990 var valid = element(by.binding('myForm.input.$valid'));
18991 var input = element(by.model('example.text'));
18993 it('should initialize to model', function() {
18994 expect(text.getText()).toContain('guest');
18995 expect(valid.getText()).toContain('true');
18998 it('should be invalid if empty', function() {
19000 input.sendKeys('');
19002 expect(text.getText()).toEqual('text =');
19003 expect(valid.getText()).toContain('false');
19006 it('should be invalid if multi word', function() {
19008 input.sendKeys('hello world');
19010 expect(valid.getText()).toContain('false');
19015 'text': textInputType,
19019 * @name input[date]
19022 * Input with date validation and transformation. In browsers that do not yet support
19023 * the HTML5 date input, a text element will be used. In that case, text must be entered in a valid ISO-8601
19024 * date format (yyyy-MM-dd), for example: `2009-01-06`. Since many
19025 * modern browsers do not yet support this input type, it is important to provide cues to users on the
19026 * expected input format via a placeholder or label.
19028 * The model must always be a Date object, otherwise Angular will throw an error.
19029 * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
19031 * The timezone to be used to read/write the `Date` instance in the model can be defined using
19032 * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
19034 * @param {string} ngModel Assignable angular expression to data-bind to.
19035 * @param {string=} name Property name of the form under which the control is published.
19036 * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be a
19037 * valid ISO date string (yyyy-MM-dd).
19038 * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must be
19039 * a valid ISO date string (yyyy-MM-dd).
19040 * @param {string=} required Sets `required` validation error key if the value is not entered.
19041 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
19042 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
19043 * `required` when you want to data-bind to the `required` attribute.
19044 * @param {string=} ngChange Angular expression to be executed when input changes due to user
19045 * interaction with the input element.
19048 <example name="date-input-directive" module="dateInputExample">
19049 <file name="index.html">
19051 angular.module('dateInputExample', [])
19052 .controller('DateController', ['$scope', function($scope) {
19054 value: new Date(2013, 9, 22)
19058 <form name="myForm" ng-controller="DateController as dateCtrl">
19059 Pick a date in 2013:
19060 <input type="date" id="exampleInput" name="input" ng-model="example.value"
19061 placeholder="yyyy-MM-dd" min="2013-01-01" max="2013-12-31" required />
19062 <span class="error" ng-show="myForm.input.$error.required">
19064 <span class="error" ng-show="myForm.input.$error.date">
19065 Not a valid date!</span>
19066 <tt>value = {{example.value | date: "yyyy-MM-dd"}}</tt><br/>
19067 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
19068 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
19069 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
19070 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
19073 <file name="protractor.js" type="protractor">
19074 var value = element(by.binding('example.value | date: "yyyy-MM-dd"'));
19075 var valid = element(by.binding('myForm.input.$valid'));
19076 var input = element(by.model('example.value'));
19078 // currently protractor/webdriver does not support
19079 // sending keys to all known HTML5 input controls
19080 // for various browsers (see https://github.com/angular/protractor/issues/562).
19081 function setInput(val) {
19082 // set the value of the element and force validation.
19083 var scr = "var ipt = document.getElementById('exampleInput'); " +
19084 "ipt.value = '" + val + "';" +
19085 "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
19086 browser.executeScript(scr);
19089 it('should initialize to model', function() {
19090 expect(value.getText()).toContain('2013-10-22');
19091 expect(valid.getText()).toContain('myForm.input.$valid = true');
19094 it('should be invalid if empty', function() {
19096 expect(value.getText()).toEqual('value =');
19097 expect(valid.getText()).toContain('myForm.input.$valid = false');
19100 it('should be invalid if over max', function() {
19101 setInput('2015-01-01');
19102 expect(value.getText()).toContain('');
19103 expect(valid.getText()).toContain('myForm.input.$valid = false');
19108 'date': createDateInputType('date', DATE_REGEXP,
19109 createDateParser(DATE_REGEXP, ['yyyy', 'MM', 'dd']),
19114 * @name input[datetime-local]
19117 * Input with datetime validation and transformation. In browsers that do not yet support
19118 * the HTML5 date input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
19119 * local datetime format (yyyy-MM-ddTHH:mm:ss), for example: `2010-12-28T14:57:00`.
19121 * The model must always be a Date object, otherwise Angular will throw an error.
19122 * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
19124 * The timezone to be used to read/write the `Date` instance in the model can be defined using
19125 * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
19127 * @param {string} ngModel Assignable angular expression to data-bind to.
19128 * @param {string=} name Property name of the form under which the control is published.
19129 * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be a
19130 * valid ISO datetime format (yyyy-MM-ddTHH:mm:ss).
19131 * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must be
19132 * a valid ISO datetime format (yyyy-MM-ddTHH:mm:ss).
19133 * @param {string=} required Sets `required` validation error key if the value is not entered.
19134 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
19135 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
19136 * `required` when you want to data-bind to the `required` attribute.
19137 * @param {string=} ngChange Angular expression to be executed when input changes due to user
19138 * interaction with the input element.
19141 <example name="datetimelocal-input-directive" module="dateExample">
19142 <file name="index.html">
19144 angular.module('dateExample', [])
19145 .controller('DateController', ['$scope', function($scope) {
19147 value: new Date(2010, 11, 28, 14, 57)
19151 <form name="myForm" ng-controller="DateController as dateCtrl">
19152 Pick a date between in 2013:
19153 <input type="datetime-local" id="exampleInput" name="input" ng-model="example.value"
19154 placeholder="yyyy-MM-ddTHH:mm:ss" min="2001-01-01T00:00:00" max="2013-12-31T00:00:00" required />
19155 <span class="error" ng-show="myForm.input.$error.required">
19157 <span class="error" ng-show="myForm.input.$error.datetimelocal">
19158 Not a valid date!</span>
19159 <tt>value = {{example.value | date: "yyyy-MM-ddTHH:mm:ss"}}</tt><br/>
19160 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
19161 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
19162 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
19163 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
19166 <file name="protractor.js" type="protractor">
19167 var value = element(by.binding('example.value | date: "yyyy-MM-ddTHH:mm:ss"'));
19168 var valid = element(by.binding('myForm.input.$valid'));
19169 var input = element(by.model('example.value'));
19171 // currently protractor/webdriver does not support
19172 // sending keys to all known HTML5 input controls
19173 // for various browsers (https://github.com/angular/protractor/issues/562).
19174 function setInput(val) {
19175 // set the value of the element and force validation.
19176 var scr = "var ipt = document.getElementById('exampleInput'); " +
19177 "ipt.value = '" + val + "';" +
19178 "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
19179 browser.executeScript(scr);
19182 it('should initialize to model', function() {
19183 expect(value.getText()).toContain('2010-12-28T14:57:00');
19184 expect(valid.getText()).toContain('myForm.input.$valid = true');
19187 it('should be invalid if empty', function() {
19189 expect(value.getText()).toEqual('value =');
19190 expect(valid.getText()).toContain('myForm.input.$valid = false');
19193 it('should be invalid if over max', function() {
19194 setInput('2015-01-01T23:59:00');
19195 expect(value.getText()).toContain('');
19196 expect(valid.getText()).toContain('myForm.input.$valid = false');
19201 'datetime-local': createDateInputType('datetimelocal', DATETIMELOCAL_REGEXP,
19202 createDateParser(DATETIMELOCAL_REGEXP, ['yyyy', 'MM', 'dd', 'HH', 'mm', 'ss', 'sss']),
19203 'yyyy-MM-ddTHH:mm:ss.sss'),
19207 * @name input[time]
19210 * Input with time validation and transformation. In browsers that do not yet support
19211 * the HTML5 date input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
19212 * local time format (HH:mm:ss), for example: `14:57:00`. Model must be a Date object. This binding will always output a
19213 * Date object to the model of January 1, 1970, or local date `new Date(1970, 0, 1, HH, mm, ss)`.
19215 * The model must always be a Date object, otherwise Angular will throw an error.
19216 * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
19218 * The timezone to be used to read/write the `Date` instance in the model can be defined using
19219 * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
19221 * @param {string} ngModel Assignable angular expression to data-bind to.
19222 * @param {string=} name Property name of the form under which the control is published.
19223 * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be a
19224 * valid ISO time format (HH:mm:ss).
19225 * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must be a
19226 * valid ISO time format (HH:mm:ss).
19227 * @param {string=} required Sets `required` validation error key if the value is not entered.
19228 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
19229 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
19230 * `required` when you want to data-bind to the `required` attribute.
19231 * @param {string=} ngChange Angular expression to be executed when input changes due to user
19232 * interaction with the input element.
19235 <example name="time-input-directive" module="timeExample">
19236 <file name="index.html">
19238 angular.module('timeExample', [])
19239 .controller('DateController', ['$scope', function($scope) {
19241 value: new Date(1970, 0, 1, 14, 57, 0)
19245 <form name="myForm" ng-controller="DateController as dateCtrl">
19246 Pick a between 8am and 5pm:
19247 <input type="time" id="exampleInput" name="input" ng-model="example.value"
19248 placeholder="HH:mm:ss" min="08:00:00" max="17:00:00" required />
19249 <span class="error" ng-show="myForm.input.$error.required">
19251 <span class="error" ng-show="myForm.input.$error.time">
19252 Not a valid date!</span>
19253 <tt>value = {{example.value | date: "HH:mm:ss"}}</tt><br/>
19254 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
19255 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
19256 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
19257 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
19260 <file name="protractor.js" type="protractor">
19261 var value = element(by.binding('example.value | date: "HH:mm:ss"'));
19262 var valid = element(by.binding('myForm.input.$valid'));
19263 var input = element(by.model('example.value'));
19265 // currently protractor/webdriver does not support
19266 // sending keys to all known HTML5 input controls
19267 // for various browsers (https://github.com/angular/protractor/issues/562).
19268 function setInput(val) {
19269 // set the value of the element and force validation.
19270 var scr = "var ipt = document.getElementById('exampleInput'); " +
19271 "ipt.value = '" + val + "';" +
19272 "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
19273 browser.executeScript(scr);
19276 it('should initialize to model', function() {
19277 expect(value.getText()).toContain('14:57:00');
19278 expect(valid.getText()).toContain('myForm.input.$valid = true');
19281 it('should be invalid if empty', function() {
19283 expect(value.getText()).toEqual('value =');
19284 expect(valid.getText()).toContain('myForm.input.$valid = false');
19287 it('should be invalid if over max', function() {
19288 setInput('23:59:00');
19289 expect(value.getText()).toContain('');
19290 expect(valid.getText()).toContain('myForm.input.$valid = false');
19295 'time': createDateInputType('time', TIME_REGEXP,
19296 createDateParser(TIME_REGEXP, ['HH', 'mm', 'ss', 'sss']),
19301 * @name input[week]
19304 * Input with week-of-the-year validation and transformation to Date. In browsers that do not yet support
19305 * the HTML5 week input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
19306 * week format (yyyy-W##), for example: `2013-W02`.
19308 * The model must always be a Date object, otherwise Angular will throw an error.
19309 * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
19311 * The timezone to be used to read/write the `Date` instance in the model can be defined using
19312 * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
19314 * @param {string} ngModel Assignable angular expression to data-bind to.
19315 * @param {string=} name Property name of the form under which the control is published.
19316 * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be a
19317 * valid ISO week format (yyyy-W##).
19318 * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must be
19319 * a valid ISO week format (yyyy-W##).
19320 * @param {string=} required Sets `required` validation error key if the value is not entered.
19321 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
19322 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
19323 * `required` when you want to data-bind to the `required` attribute.
19324 * @param {string=} ngChange Angular expression to be executed when input changes due to user
19325 * interaction with the input element.
19328 <example name="week-input-directive" module="weekExample">
19329 <file name="index.html">
19331 angular.module('weekExample', [])
19332 .controller('DateController', ['$scope', function($scope) {
19334 value: new Date(2013, 0, 3)
19338 <form name="myForm" ng-controller="DateController as dateCtrl">
19339 Pick a date between in 2013:
19340 <input id="exampleInput" type="week" name="input" ng-model="example.value"
19341 placeholder="YYYY-W##" min="2012-W32" max="2013-W52" required />
19342 <span class="error" ng-show="myForm.input.$error.required">
19344 <span class="error" ng-show="myForm.input.$error.week">
19345 Not a valid date!</span>
19346 <tt>value = {{example.value | date: "yyyy-Www"}}</tt><br/>
19347 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
19348 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
19349 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
19350 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
19353 <file name="protractor.js" type="protractor">
19354 var value = element(by.binding('example.value | date: "yyyy-Www"'));
19355 var valid = element(by.binding('myForm.input.$valid'));
19356 var input = element(by.model('example.value'));
19358 // currently protractor/webdriver does not support
19359 // sending keys to all known HTML5 input controls
19360 // for various browsers (https://github.com/angular/protractor/issues/562).
19361 function setInput(val) {
19362 // set the value of the element and force validation.
19363 var scr = "var ipt = document.getElementById('exampleInput'); " +
19364 "ipt.value = '" + val + "';" +
19365 "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
19366 browser.executeScript(scr);
19369 it('should initialize to model', function() {
19370 expect(value.getText()).toContain('2013-W01');
19371 expect(valid.getText()).toContain('myForm.input.$valid = true');
19374 it('should be invalid if empty', function() {
19376 expect(value.getText()).toEqual('value =');
19377 expect(valid.getText()).toContain('myForm.input.$valid = false');
19380 it('should be invalid if over max', function() {
19381 setInput('2015-W01');
19382 expect(value.getText()).toContain('');
19383 expect(valid.getText()).toContain('myForm.input.$valid = false');
19388 'week': createDateInputType('week', WEEK_REGEXP, weekParser, 'yyyy-Www'),
19392 * @name input[month]
19395 * Input with month validation and transformation. In browsers that do not yet support
19396 * the HTML5 month input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
19397 * month format (yyyy-MM), for example: `2009-01`.
19399 * The model must always be a Date object, otherwise Angular will throw an error.
19400 * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
19401 * If the model is not set to the first of the month, the next view to model update will set it
19402 * to the first of the month.
19404 * The timezone to be used to read/write the `Date` instance in the model can be defined using
19405 * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
19407 * @param {string} ngModel Assignable angular expression to data-bind to.
19408 * @param {string=} name Property name of the form under which the control is published.
19409 * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be
19410 * a valid ISO month format (yyyy-MM).
19411 * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must
19412 * be a valid ISO month format (yyyy-MM).
19413 * @param {string=} required Sets `required` validation error key if the value is not entered.
19414 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
19415 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
19416 * `required` when you want to data-bind to the `required` attribute.
19417 * @param {string=} ngChange Angular expression to be executed when input changes due to user
19418 * interaction with the input element.
19421 <example name="month-input-directive" module="monthExample">
19422 <file name="index.html">
19424 angular.module('monthExample', [])
19425 .controller('DateController', ['$scope', function($scope) {
19427 value: new Date(2013, 9, 1)
19431 <form name="myForm" ng-controller="DateController as dateCtrl">
19432 Pick a month in 2013:
19433 <input id="exampleInput" type="month" name="input" ng-model="example.value"
19434 placeholder="yyyy-MM" min="2013-01" max="2013-12" required />
19435 <span class="error" ng-show="myForm.input.$error.required">
19437 <span class="error" ng-show="myForm.input.$error.month">
19438 Not a valid month!</span>
19439 <tt>value = {{example.value | date: "yyyy-MM"}}</tt><br/>
19440 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
19441 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
19442 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
19443 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
19446 <file name="protractor.js" type="protractor">
19447 var value = element(by.binding('example.value | date: "yyyy-MM"'));
19448 var valid = element(by.binding('myForm.input.$valid'));
19449 var input = element(by.model('example.value'));
19451 // currently protractor/webdriver does not support
19452 // sending keys to all known HTML5 input controls
19453 // for various browsers (https://github.com/angular/protractor/issues/562).
19454 function setInput(val) {
19455 // set the value of the element and force validation.
19456 var scr = "var ipt = document.getElementById('exampleInput'); " +
19457 "ipt.value = '" + val + "';" +
19458 "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
19459 browser.executeScript(scr);
19462 it('should initialize to model', function() {
19463 expect(value.getText()).toContain('2013-10');
19464 expect(valid.getText()).toContain('myForm.input.$valid = true');
19467 it('should be invalid if empty', function() {
19469 expect(value.getText()).toEqual('value =');
19470 expect(valid.getText()).toContain('myForm.input.$valid = false');
19473 it('should be invalid if over max', function() {
19474 setInput('2015-01');
19475 expect(value.getText()).toContain('');
19476 expect(valid.getText()).toContain('myForm.input.$valid = false');
19481 'month': createDateInputType('month', MONTH_REGEXP,
19482 createDateParser(MONTH_REGEXP, ['yyyy', 'MM']),
19487 * @name input[number]
19490 * Text input with number validation and transformation. Sets the `number` validation
19491 * error if not a valid number.
19493 * <div class="alert alert-warning">
19494 * The model must always be of type `number` otherwise Angular will throw an error.
19495 * Be aware that a string containing a number is not enough. See the {@link ngModel:numfmt}
19496 * error docs for more information and an example of how to convert your model if necessary.
19499 * @param {string} ngModel Assignable angular expression to data-bind to.
19500 * @param {string=} name Property name of the form under which the control is published.
19501 * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
19502 * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
19503 * @param {string=} required Sets `required` validation error key if the value is not entered.
19504 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
19505 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
19506 * `required` when you want to data-bind to the `required` attribute.
19507 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
19509 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
19510 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
19512 * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
19513 * that contains the regular expression body that will be converted to a regular expression
19514 * as in the ngPattern directive.
19515 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
19516 * a RegExp found by evaluating the Angular expression given in the attribute value.
19517 * If the expression evaluates to a RegExp object then this is used directly.
19518 * If the expression is a string then it will be converted to a RegExp after wrapping it in `^` and `$`
19519 * characters. For instance, `"abc"` will be converted to `new RegExp('^abc$')`.
19520 * @param {string=} ngChange Angular expression to be executed when input changes due to user
19521 * interaction with the input element.
19524 <example name="number-input-directive" module="numberExample">
19525 <file name="index.html">
19527 angular.module('numberExample', [])
19528 .controller('ExampleController', ['$scope', function($scope) {
19534 <form name="myForm" ng-controller="ExampleController">
19535 Number: <input type="number" name="input" ng-model="example.value"
19536 min="0" max="99" required>
19537 <span class="error" ng-show="myForm.input.$error.required">
19539 <span class="error" ng-show="myForm.input.$error.number">
19540 Not valid number!</span>
19541 <tt>value = {{example.value}}</tt><br/>
19542 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
19543 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
19544 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
19545 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
19548 <file name="protractor.js" type="protractor">
19549 var value = element(by.binding('example.value'));
19550 var valid = element(by.binding('myForm.input.$valid'));
19551 var input = element(by.model('example.value'));
19553 it('should initialize to model', function() {
19554 expect(value.getText()).toContain('12');
19555 expect(valid.getText()).toContain('true');
19558 it('should be invalid if empty', function() {
19560 input.sendKeys('');
19561 expect(value.getText()).toEqual('value =');
19562 expect(valid.getText()).toContain('false');
19565 it('should be invalid if over max', function() {
19567 input.sendKeys('123');
19568 expect(value.getText()).toEqual('value =');
19569 expect(valid.getText()).toContain('false');
19574 'number': numberInputType,
19582 * Text input with URL validation. Sets the `url` validation error key if the content is not a
19585 * <div class="alert alert-warning">
19586 * **Note:** `input[url]` uses a regex to validate urls that is derived from the regex
19587 * used in Chromium. If you need stricter validation, you can use `ng-pattern` or modify
19588 * the built-in validators (see the {@link guide/forms Forms guide})
19591 * @param {string} ngModel Assignable angular expression to data-bind to.
19592 * @param {string=} name Property name of the form under which the control is published.
19593 * @param {string=} required Sets `required` validation error key if the value is not entered.
19594 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
19595 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
19596 * `required` when you want to data-bind to the `required` attribute.
19597 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
19599 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
19600 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
19602 * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
19603 * that contains the regular expression body that will be converted to a regular expression
19604 * as in the ngPattern directive.
19605 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
19606 * a RegExp found by evaluating the Angular expression given in the attribute value.
19607 * If the expression evaluates to a RegExp object then this is used directly.
19608 * If the expression is a string then it will be converted to a RegExp after wrapping it in `^` and `$`
19609 * characters. For instance, `"abc"` will be converted to `new RegExp('^abc$')`.
19610 * @param {string=} ngChange Angular expression to be executed when input changes due to user
19611 * interaction with the input element.
19614 <example name="url-input-directive" module="urlExample">
19615 <file name="index.html">
19617 angular.module('urlExample', [])
19618 .controller('ExampleController', ['$scope', function($scope) {
19620 text: 'http://google.com'
19624 <form name="myForm" ng-controller="ExampleController">
19625 URL: <input type="url" name="input" ng-model="url.text" required>
19626 <span class="error" ng-show="myForm.input.$error.required">
19628 <span class="error" ng-show="myForm.input.$error.url">
19629 Not valid url!</span>
19630 <tt>text = {{url.text}}</tt><br/>
19631 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
19632 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
19633 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
19634 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
19635 <tt>myForm.$error.url = {{!!myForm.$error.url}}</tt><br/>
19638 <file name="protractor.js" type="protractor">
19639 var text = element(by.binding('url.text'));
19640 var valid = element(by.binding('myForm.input.$valid'));
19641 var input = element(by.model('url.text'));
19643 it('should initialize to model', function() {
19644 expect(text.getText()).toContain('http://google.com');
19645 expect(valid.getText()).toContain('true');
19648 it('should be invalid if empty', function() {
19650 input.sendKeys('');
19652 expect(text.getText()).toEqual('text =');
19653 expect(valid.getText()).toContain('false');
19656 it('should be invalid if not url', function() {
19658 input.sendKeys('box');
19660 expect(valid.getText()).toContain('false');
19665 'url': urlInputType,
19670 * @name input[email]
19673 * Text input with email validation. Sets the `email` validation error key if not a valid email
19676 * <div class="alert alert-warning">
19677 * **Note:** `input[email]` uses a regex to validate email addresses that is derived from the regex
19678 * used in Chromium. If you need stricter validation (e.g. requiring a top-level domain), you can
19679 * use `ng-pattern` or modify the built-in validators (see the {@link guide/forms Forms guide})
19682 * @param {string} ngModel Assignable angular expression to data-bind to.
19683 * @param {string=} name Property name of the form under which the control is published.
19684 * @param {string=} required Sets `required` validation error key if the value is not entered.
19685 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
19686 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
19687 * `required` when you want to data-bind to the `required` attribute.
19688 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
19690 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
19691 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
19693 * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
19694 * that contains the regular expression body that will be converted to a regular expression
19695 * as in the ngPattern directive.
19696 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
19697 * a RegExp found by evaluating the Angular expression given in the attribute value.
19698 * If the expression evaluates to a RegExp object then this is used directly.
19699 * If the expression is a string then it will be converted to a RegExp after wrapping it in `^` and `$`
19700 * characters. For instance, `"abc"` will be converted to `new RegExp('^abc$')`.
19701 * @param {string=} ngChange Angular expression to be executed when input changes due to user
19702 * interaction with the input element.
19705 <example name="email-input-directive" module="emailExample">
19706 <file name="index.html">
19708 angular.module('emailExample', [])
19709 .controller('ExampleController', ['$scope', function($scope) {
19711 text: 'me@example.com'
19715 <form name="myForm" ng-controller="ExampleController">
19716 Email: <input type="email" name="input" ng-model="email.text" required>
19717 <span class="error" ng-show="myForm.input.$error.required">
19719 <span class="error" ng-show="myForm.input.$error.email">
19720 Not valid email!</span>
19721 <tt>text = {{email.text}}</tt><br/>
19722 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
19723 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
19724 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
19725 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
19726 <tt>myForm.$error.email = {{!!myForm.$error.email}}</tt><br/>
19729 <file name="protractor.js" type="protractor">
19730 var text = element(by.binding('email.text'));
19731 var valid = element(by.binding('myForm.input.$valid'));
19732 var input = element(by.model('email.text'));
19734 it('should initialize to model', function() {
19735 expect(text.getText()).toContain('me@example.com');
19736 expect(valid.getText()).toContain('true');
19739 it('should be invalid if empty', function() {
19741 input.sendKeys('');
19742 expect(text.getText()).toEqual('text =');
19743 expect(valid.getText()).toContain('false');
19746 it('should be invalid if not email', function() {
19748 input.sendKeys('xxx');
19750 expect(valid.getText()).toContain('false');
19755 'email': emailInputType,
19760 * @name input[radio]
19763 * HTML radio button.
19765 * @param {string} ngModel Assignable angular expression to data-bind to.
19766 * @param {string} value The value to which the expression should be set when selected.
19767 * @param {string=} name Property name of the form under which the control is published.
19768 * @param {string=} ngChange Angular expression to be executed when input changes due to user
19769 * interaction with the input element.
19770 * @param {string} ngValue Angular expression which sets the value to which the expression should
19771 * be set when selected.
19774 <example name="radio-input-directive" module="radioExample">
19775 <file name="index.html">
19777 angular.module('radioExample', [])
19778 .controller('ExampleController', ['$scope', function($scope) {
19782 $scope.specialValue = {
19788 <form name="myForm" ng-controller="ExampleController">
19789 <input type="radio" ng-model="color.name" value="red"> Red <br/>
19790 <input type="radio" ng-model="color.name" ng-value="specialValue"> Green <br/>
19791 <input type="radio" ng-model="color.name" value="blue"> Blue <br/>
19792 <tt>color = {{color.name | json}}</tt><br/>
19794 Note that `ng-value="specialValue"` sets radio item's value to be the value of `$scope.specialValue`.
19796 <file name="protractor.js" type="protractor">
19797 it('should change state', function() {
19798 var color = element(by.binding('color.name'));
19800 expect(color.getText()).toContain('blue');
19802 element.all(by.model('color.name')).get(0).click();
19804 expect(color.getText()).toContain('red');
19809 'radio': radioInputType,
19814 * @name input[checkbox]
19819 * @param {string} ngModel Assignable angular expression to data-bind to.
19820 * @param {string=} name Property name of the form under which the control is published.
19821 * @param {expression=} ngTrueValue The value to which the expression should be set when selected.
19822 * @param {expression=} ngFalseValue The value to which the expression should be set when not selected.
19823 * @param {string=} ngChange Angular expression to be executed when input changes due to user
19824 * interaction with the input element.
19827 <example name="checkbox-input-directive" module="checkboxExample">
19828 <file name="index.html">
19830 angular.module('checkboxExample', [])
19831 .controller('ExampleController', ['$scope', function($scope) {
19832 $scope.checkboxModel = {
19838 <form name="myForm" ng-controller="ExampleController">
19839 Value1: <input type="checkbox" ng-model="checkboxModel.value1"> <br/>
19840 Value2: <input type="checkbox" ng-model="checkboxModel.value2"
19841 ng-true-value="'YES'" ng-false-value="'NO'"> <br/>
19842 <tt>value1 = {{checkboxModel.value1}}</tt><br/>
19843 <tt>value2 = {{checkboxModel.value2}}</tt><br/>
19846 <file name="protractor.js" type="protractor">
19847 it('should change state', function() {
19848 var value1 = element(by.binding('checkboxModel.value1'));
19849 var value2 = element(by.binding('checkboxModel.value2'));
19851 expect(value1.getText()).toContain('true');
19852 expect(value2.getText()).toContain('YES');
19854 element(by.model('checkboxModel.value1')).click();
19855 element(by.model('checkboxModel.value2')).click();
19857 expect(value1.getText()).toContain('false');
19858 expect(value2.getText()).toContain('NO');
19863 'checkbox': checkboxInputType,
19872 function stringBasedInputType(ctrl) {
19873 ctrl.$formatters.push(function(value) {
19874 return ctrl.$isEmpty(value) ? value : value.toString();
19878 function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
19879 baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
19880 stringBasedInputType(ctrl);
19883 function baseInputType(scope, element, attr, ctrl, $sniffer, $browser) {
19884 var type = lowercase(element[0].type);
19886 // In composition mode, users are still inputing intermediate text buffer,
19887 // hold the listener until composition is done.
19888 // More about composition events: https://developer.mozilla.org/en-US/docs/Web/API/CompositionEvent
19889 if (!$sniffer.android) {
19890 var composing = false;
19892 element.on('compositionstart', function(data) {
19896 element.on('compositionend', function() {
19902 var listener = function(ev) {
19904 $browser.defer.cancel(timeout);
19907 if (composing) return;
19908 var value = element.val(),
19909 event = ev && ev.type;
19911 // By default we will trim the value
19912 // If the attribute ng-trim exists we will avoid trimming
19913 // If input type is 'password', the value is never trimmed
19914 if (type !== 'password' && (!attr.ngTrim || attr.ngTrim !== 'false')) {
19915 value = trim(value);
19918 // If a control is suffering from bad input (due to native validators), browsers discard its
19919 // value, so it may be necessary to revalidate (by calling $setViewValue again) even if the
19920 // control's value is the same empty value twice in a row.
19921 if (ctrl.$viewValue !== value || (value === '' && ctrl.$$hasNativeValidators)) {
19922 ctrl.$setViewValue(value, event);
19926 // if the browser does support "input" event, we are fine - except on IE9 which doesn't fire the
19927 // input event on backspace, delete or cut
19928 if ($sniffer.hasEvent('input')) {
19929 element.on('input', listener);
19933 var deferListener = function(ev, input, origValue) {
19935 timeout = $browser.defer(function() {
19937 if (!input || input.value !== origValue) {
19944 element.on('keydown', function(event) {
19945 var key = event.keyCode;
19948 // command modifiers arrows
19949 if (key === 91 || (15 < key && key < 19) || (37 <= key && key <= 40)) return;
19951 deferListener(event, this, this.value);
19954 // if user modifies input value using context menu in IE, we need "paste" and "cut" events to catch it
19955 if ($sniffer.hasEvent('paste')) {
19956 element.on('paste cut', deferListener);
19960 // if user paste into input using mouse on older browser
19961 // or form autocomplete on newer browser, we need "change" event to catch it
19962 element.on('change', listener);
19964 ctrl.$render = function() {
19965 element.val(ctrl.$isEmpty(ctrl.$viewValue) ? '' : ctrl.$viewValue);
19969 function weekParser(isoWeek, existingDate) {
19970 if (isDate(isoWeek)) {
19974 if (isString(isoWeek)) {
19975 WEEK_REGEXP.lastIndex = 0;
19976 var parts = WEEK_REGEXP.exec(isoWeek);
19978 var year = +parts[1],
19984 firstThurs = getFirstThursdayOfYear(year),
19985 addDays = (week - 1) * 7;
19987 if (existingDate) {
19988 hours = existingDate.getHours();
19989 minutes = existingDate.getMinutes();
19990 seconds = existingDate.getSeconds();
19991 milliseconds = existingDate.getMilliseconds();
19994 return new Date(year, 0, firstThurs.getDate() + addDays, hours, minutes, seconds, milliseconds);
20001 function createDateParser(regexp, mapping) {
20002 return function(iso, date) {
20009 if (isString(iso)) {
20010 // When a date is JSON'ified to wraps itself inside of an extra
20011 // set of double quotes. This makes the date parsing code unable
20012 // to match the date string and parse it as a date.
20013 if (iso.charAt(0) == '"' && iso.charAt(iso.length - 1) == '"') {
20014 iso = iso.substring(1, iso.length - 1);
20016 if (ISO_DATE_REGEXP.test(iso)) {
20017 return new Date(iso);
20019 regexp.lastIndex = 0;
20020 parts = regexp.exec(iso);
20026 yyyy: date.getFullYear(),
20027 MM: date.getMonth() + 1,
20028 dd: date.getDate(),
20029 HH: date.getHours(),
20030 mm: date.getMinutes(),
20031 ss: date.getSeconds(),
20032 sss: date.getMilliseconds() / 1000
20035 map = { yyyy: 1970, MM: 1, dd: 1, HH: 0, mm: 0, ss: 0, sss: 0 };
20038 forEach(parts, function(part, index) {
20039 if (index < mapping.length) {
20040 map[mapping[index]] = +part;
20043 return new Date(map.yyyy, map.MM - 1, map.dd, map.HH, map.mm, map.ss || 0, map.sss * 1000 || 0);
20051 function createDateInputType(type, regexp, parseDate, format) {
20052 return function dynamicDateInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter) {
20053 badInputChecker(scope, element, attr, ctrl);
20054 baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
20055 var timezone = ctrl && ctrl.$options && ctrl.$options.timezone;
20058 ctrl.$$parserName = type;
20059 ctrl.$parsers.push(function(value) {
20060 if (ctrl.$isEmpty(value)) return null;
20061 if (regexp.test(value)) {
20062 // Note: We cannot read ctrl.$modelValue, as there might be a different
20063 // parser/formatter in the processing chain so that the model
20064 // contains some different data format!
20065 var parsedDate = parseDate(value, previousDate);
20066 if (timezone === 'UTC') {
20067 parsedDate.setMinutes(parsedDate.getMinutes() - parsedDate.getTimezoneOffset());
20074 ctrl.$formatters.push(function(value) {
20075 if (value && !isDate(value)) {
20076 throw ngModelMinErr('datefmt', 'Expected `{0}` to be a date', value);
20078 if (isValidDate(value)) {
20079 previousDate = value;
20080 if (previousDate && timezone === 'UTC') {
20081 var timezoneOffset = 60000 * previousDate.getTimezoneOffset();
20082 previousDate = new Date(previousDate.getTime() + timezoneOffset);
20084 return $filter('date')(value, format, timezone);
20086 previousDate = null;
20091 if (isDefined(attr.min) || attr.ngMin) {
20093 ctrl.$validators.min = function(value) {
20094 return !isValidDate(value) || isUndefined(minVal) || parseDate(value) >= minVal;
20096 attr.$observe('min', function(val) {
20097 minVal = parseObservedDateValue(val);
20102 if (isDefined(attr.max) || attr.ngMax) {
20104 ctrl.$validators.max = function(value) {
20105 return !isValidDate(value) || isUndefined(maxVal) || parseDate(value) <= maxVal;
20107 attr.$observe('max', function(val) {
20108 maxVal = parseObservedDateValue(val);
20113 function isValidDate(value) {
20114 // Invalid Date: getTime() returns NaN
20115 return value && !(value.getTime && value.getTime() !== value.getTime());
20118 function parseObservedDateValue(val) {
20119 return isDefined(val) ? (isDate(val) ? val : parseDate(val)) : undefined;
20124 function badInputChecker(scope, element, attr, ctrl) {
20125 var node = element[0];
20126 var nativeValidation = ctrl.$$hasNativeValidators = isObject(node.validity);
20127 if (nativeValidation) {
20128 ctrl.$parsers.push(function(value) {
20129 var validity = element.prop(VALIDITY_STATE_PROPERTY) || {};
20130 // Detect bug in FF35 for input[email] (https://bugzilla.mozilla.org/show_bug.cgi?id=1064430):
20131 // - also sets validity.badInput (should only be validity.typeMismatch).
20132 // - see http://www.whatwg.org/specs/web-apps/current-work/multipage/forms.html#e-mail-state-(type=email)
20133 // - can ignore this case as we can still read out the erroneous email...
20134 return validity.badInput && !validity.typeMismatch ? undefined : value;
20139 function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) {
20140 badInputChecker(scope, element, attr, ctrl);
20141 baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
20143 ctrl.$$parserName = 'number';
20144 ctrl.$parsers.push(function(value) {
20145 if (ctrl.$isEmpty(value)) return null;
20146 if (NUMBER_REGEXP.test(value)) return parseFloat(value);
20150 ctrl.$formatters.push(function(value) {
20151 if (!ctrl.$isEmpty(value)) {
20152 if (!isNumber(value)) {
20153 throw ngModelMinErr('numfmt', 'Expected `{0}` to be a number', value);
20155 value = value.toString();
20160 if (isDefined(attr.min) || attr.ngMin) {
20162 ctrl.$validators.min = function(value) {
20163 return ctrl.$isEmpty(value) || isUndefined(minVal) || value >= minVal;
20166 attr.$observe('min', function(val) {
20167 if (isDefined(val) && !isNumber(val)) {
20168 val = parseFloat(val, 10);
20170 minVal = isNumber(val) && !isNaN(val) ? val : undefined;
20171 // TODO(matsko): implement validateLater to reduce number of validations
20176 if (isDefined(attr.max) || attr.ngMax) {
20178 ctrl.$validators.max = function(value) {
20179 return ctrl.$isEmpty(value) || isUndefined(maxVal) || value <= maxVal;
20182 attr.$observe('max', function(val) {
20183 if (isDefined(val) && !isNumber(val)) {
20184 val = parseFloat(val, 10);
20186 maxVal = isNumber(val) && !isNaN(val) ? val : undefined;
20187 // TODO(matsko): implement validateLater to reduce number of validations
20193 function urlInputType(scope, element, attr, ctrl, $sniffer, $browser) {
20194 // Note: no badInputChecker here by purpose as `url` is only a validation
20195 // in browsers, i.e. we can always read out input.value even if it is not valid!
20196 baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
20197 stringBasedInputType(ctrl);
20199 ctrl.$$parserName = 'url';
20200 ctrl.$validators.url = function(modelValue, viewValue) {
20201 var value = modelValue || viewValue;
20202 return ctrl.$isEmpty(value) || URL_REGEXP.test(value);
20206 function emailInputType(scope, element, attr, ctrl, $sniffer, $browser) {
20207 // Note: no badInputChecker here by purpose as `url` is only a validation
20208 // in browsers, i.e. we can always read out input.value even if it is not valid!
20209 baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
20210 stringBasedInputType(ctrl);
20212 ctrl.$$parserName = 'email';
20213 ctrl.$validators.email = function(modelValue, viewValue) {
20214 var value = modelValue || viewValue;
20215 return ctrl.$isEmpty(value) || EMAIL_REGEXP.test(value);
20219 function radioInputType(scope, element, attr, ctrl) {
20220 // make the name unique, if not defined
20221 if (isUndefined(attr.name)) {
20222 element.attr('name', nextUid());
20225 var listener = function(ev) {
20226 if (element[0].checked) {
20227 ctrl.$setViewValue(attr.value, ev && ev.type);
20231 element.on('click', listener);
20233 ctrl.$render = function() {
20234 var value = attr.value;
20235 element[0].checked = (value == ctrl.$viewValue);
20238 attr.$observe('value', ctrl.$render);
20241 function parseConstantExpr($parse, context, name, expression, fallback) {
20243 if (isDefined(expression)) {
20244 parseFn = $parse(expression);
20245 if (!parseFn.constant) {
20246 throw ngModelMinErr('constexpr', 'Expected constant expression for `{0}`, but saw ' +
20247 '`{1}`.', name, expression);
20249 return parseFn(context);
20254 function checkboxInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter, $parse) {
20255 var trueValue = parseConstantExpr($parse, scope, 'ngTrueValue', attr.ngTrueValue, true);
20256 var falseValue = parseConstantExpr($parse, scope, 'ngFalseValue', attr.ngFalseValue, false);
20258 var listener = function(ev) {
20259 ctrl.$setViewValue(element[0].checked, ev && ev.type);
20262 element.on('click', listener);
20264 ctrl.$render = function() {
20265 element[0].checked = ctrl.$viewValue;
20268 // Override the standard `$isEmpty` because the $viewValue of an empty checkbox is always set to `false`
20269 // This is because of the parser below, which compares the `$modelValue` with `trueValue` to convert
20270 // it to a boolean.
20271 ctrl.$isEmpty = function(value) {
20272 return value === false;
20275 ctrl.$formatters.push(function(value) {
20276 return equals(value, trueValue);
20279 ctrl.$parsers.push(function(value) {
20280 return value ? trueValue : falseValue;
20291 * HTML textarea element control with angular data-binding. The data-binding and validation
20292 * properties of this element are exactly the same as those of the
20293 * {@link ng.directive:input input element}.
20295 * @param {string} ngModel Assignable angular expression to data-bind to.
20296 * @param {string=} name Property name of the form under which the control is published.
20297 * @param {string=} required Sets `required` validation error key if the value is not entered.
20298 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
20299 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
20300 * `required` when you want to data-bind to the `required` attribute.
20301 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
20303 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
20304 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any
20306 * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the
20307 * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for
20308 * patterns defined as scope expressions.
20309 * @param {string=} ngChange Angular expression to be executed when input changes due to user
20310 * interaction with the input element.
20311 * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
20321 * HTML input element control. When used together with {@link ngModel `ngModel`}, it provides data-binding,
20322 * input state control, and validation.
20323 * Input control follows HTML5 input types and polyfills the HTML5 validation behavior for older browsers.
20325 * <div class="alert alert-warning">
20326 * **Note:** Not every feature offered is available for all input types.
20327 * Specifically, data binding and event handling via `ng-model` is unsupported for `input[file]`.
20330 * @param {string} ngModel Assignable angular expression to data-bind to.
20331 * @param {string=} name Property name of the form under which the control is published.
20332 * @param {string=} required Sets `required` validation error key if the value is not entered.
20333 * @param {boolean=} ngRequired Sets `required` attribute if set to true
20334 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
20336 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
20337 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any
20339 * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the
20340 * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for
20341 * patterns defined as scope expressions.
20342 * @param {string=} ngChange Angular expression to be executed when input changes due to user
20343 * interaction with the input element.
20344 * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
20345 * This parameter is ignored for input[type=password] controls, which will never trim the
20349 <example name="input-directive" module="inputExample">
20350 <file name="index.html">
20352 angular.module('inputExample', [])
20353 .controller('ExampleController', ['$scope', function($scope) {
20354 $scope.user = {name: 'guest', last: 'visitor'};
20357 <div ng-controller="ExampleController">
20358 <form name="myForm">
20359 User name: <input type="text" name="userName" ng-model="user.name" required>
20360 <span class="error" ng-show="myForm.userName.$error.required">
20361 Required!</span><br>
20362 Last name: <input type="text" name="lastName" ng-model="user.last"
20363 ng-minlength="3" ng-maxlength="10">
20364 <span class="error" ng-show="myForm.lastName.$error.minlength">
20366 <span class="error" ng-show="myForm.lastName.$error.maxlength">
20367 Too long!</span><br>
20370 <tt>user = {{user}}</tt><br/>
20371 <tt>myForm.userName.$valid = {{myForm.userName.$valid}}</tt><br>
20372 <tt>myForm.userName.$error = {{myForm.userName.$error}}</tt><br>
20373 <tt>myForm.lastName.$valid = {{myForm.lastName.$valid}}</tt><br>
20374 <tt>myForm.lastName.$error = {{myForm.lastName.$error}}</tt><br>
20375 <tt>myForm.$valid = {{myForm.$valid}}</tt><br>
20376 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br>
20377 <tt>myForm.$error.minlength = {{!!myForm.$error.minlength}}</tt><br>
20378 <tt>myForm.$error.maxlength = {{!!myForm.$error.maxlength}}</tt><br>
20381 <file name="protractor.js" type="protractor">
20382 var user = element(by.exactBinding('user'));
20383 var userNameValid = element(by.binding('myForm.userName.$valid'));
20384 var lastNameValid = element(by.binding('myForm.lastName.$valid'));
20385 var lastNameError = element(by.binding('myForm.lastName.$error'));
20386 var formValid = element(by.binding('myForm.$valid'));
20387 var userNameInput = element(by.model('user.name'));
20388 var userLastInput = element(by.model('user.last'));
20390 it('should initialize to model', function() {
20391 expect(user.getText()).toContain('{"name":"guest","last":"visitor"}');
20392 expect(userNameValid.getText()).toContain('true');
20393 expect(formValid.getText()).toContain('true');
20396 it('should be invalid if empty when required', function() {
20397 userNameInput.clear();
20398 userNameInput.sendKeys('');
20400 expect(user.getText()).toContain('{"last":"visitor"}');
20401 expect(userNameValid.getText()).toContain('false');
20402 expect(formValid.getText()).toContain('false');
20405 it('should be valid if empty when min length is set', function() {
20406 userLastInput.clear();
20407 userLastInput.sendKeys('');
20409 expect(user.getText()).toContain('{"name":"guest","last":""}');
20410 expect(lastNameValid.getText()).toContain('true');
20411 expect(formValid.getText()).toContain('true');
20414 it('should be invalid if less than required min length', function() {
20415 userLastInput.clear();
20416 userLastInput.sendKeys('xx');
20418 expect(user.getText()).toContain('{"name":"guest"}');
20419 expect(lastNameValid.getText()).toContain('false');
20420 expect(lastNameError.getText()).toContain('minlength');
20421 expect(formValid.getText()).toContain('false');
20424 it('should be invalid if longer than max length', function() {
20425 userLastInput.clear();
20426 userLastInput.sendKeys('some ridiculously long name');
20428 expect(user.getText()).toContain('{"name":"guest"}');
20429 expect(lastNameValid.getText()).toContain('false');
20430 expect(lastNameError.getText()).toContain('maxlength');
20431 expect(formValid.getText()).toContain('false');
20436 var inputDirective = ['$browser', '$sniffer', '$filter', '$parse',
20437 function($browser, $sniffer, $filter, $parse) {
20440 require: ['?ngModel'],
20442 pre: function(scope, element, attr, ctrls) {
20444 (inputType[lowercase(attr.type)] || inputType.text)(scope, element, attr, ctrls[0], $sniffer,
20445 $browser, $filter, $parse);
20454 var CONSTANT_VALUE_REGEXP = /^(true|false|\d+)$/;
20460 * Binds the given expression to the value of `<option>` or {@link input[radio] `input[radio]`},
20461 * so that when the element is selected, the {@link ngModel `ngModel`} of that element is set to
20464 * `ngValue` is useful when dynamically generating lists of radio buttons using
20465 * {@link ngRepeat `ngRepeat`}, as shown below.
20467 * Likewise, `ngValue` can be used to generate `<option>` elements for
20468 * the {@link select `select`} element. In that case however, only strings are supported
20469 * for the `value `attribute, so the resulting `ngModel` will always be a string.
20470 * Support for `select` models with non-string values is available via `ngOptions`.
20473 * @param {string=} ngValue angular expression, whose value will be bound to the `value` attribute
20474 * of the `input` element
20477 <example name="ngValue-directive" module="valueExample">
20478 <file name="index.html">
20480 angular.module('valueExample', [])
20481 .controller('ExampleController', ['$scope', function($scope) {
20482 $scope.names = ['pizza', 'unicorns', 'robots'];
20483 $scope.my = { favorite: 'unicorns' };
20486 <form ng-controller="ExampleController">
20487 <h2>Which is your favorite?</h2>
20488 <label ng-repeat="name in names" for="{{name}}">
20490 <input type="radio"
20491 ng-model="my.favorite"
20496 <div>You chose {{my.favorite}}</div>
20499 <file name="protractor.js" type="protractor">
20500 var favorite = element(by.binding('my.favorite'));
20502 it('should initialize to model', function() {
20503 expect(favorite.getText()).toContain('unicorns');
20505 it('should bind the values to the inputs', function() {
20506 element.all(by.model('my.favorite')).get(0).click();
20507 expect(favorite.getText()).toContain('pizza');
20512 var ngValueDirective = function() {
20516 compile: function(tpl, tplAttr) {
20517 if (CONSTANT_VALUE_REGEXP.test(tplAttr.ngValue)) {
20518 return function ngValueConstantLink(scope, elm, attr) {
20519 attr.$set('value', scope.$eval(attr.ngValue));
20522 return function ngValueLink(scope, elm, attr) {
20523 scope.$watch(attr.ngValue, function valueWatchAction(value) {
20524 attr.$set('value', value);
20538 * The `ngBind` attribute tells Angular to replace the text content of the specified HTML element
20539 * with the value of a given expression, and to update the text content when the value of that
20540 * expression changes.
20542 * Typically, you don't use `ngBind` directly, but instead you use the double curly markup like
20543 * `{{ expression }}` which is similar but less verbose.
20545 * It is preferable to use `ngBind` instead of `{{ expression }}` if a template is momentarily
20546 * displayed by the browser in its raw state before Angular compiles it. Since `ngBind` is an
20547 * element attribute, it makes the bindings invisible to the user while the page is loading.
20549 * An alternative solution to this problem would be using the
20550 * {@link ng.directive:ngCloak ngCloak} directive.
20554 * @param {expression} ngBind {@link guide/expression Expression} to evaluate.
20557 * Enter a name in the Live Preview text box; the greeting below the text box changes instantly.
20558 <example module="bindExample">
20559 <file name="index.html">
20561 angular.module('bindExample', [])
20562 .controller('ExampleController', ['$scope', function($scope) {
20563 $scope.name = 'Whirled';
20566 <div ng-controller="ExampleController">
20567 Enter name: <input type="text" ng-model="name"><br>
20568 Hello <span ng-bind="name"></span>!
20571 <file name="protractor.js" type="protractor">
20572 it('should check ng-bind', function() {
20573 var nameInput = element(by.model('name'));
20575 expect(element(by.binding('name')).getText()).toBe('Whirled');
20577 nameInput.sendKeys('world');
20578 expect(element(by.binding('name')).getText()).toBe('world');
20583 var ngBindDirective = ['$compile', function($compile) {
20586 compile: function ngBindCompile(templateElement) {
20587 $compile.$$addBindingClass(templateElement);
20588 return function ngBindLink(scope, element, attr) {
20589 $compile.$$addBindingInfo(element, attr.ngBind);
20590 element = element[0];
20591 scope.$watch(attr.ngBind, function ngBindWatchAction(value) {
20592 element.textContent = value === undefined ? '' : value;
20602 * @name ngBindTemplate
20605 * The `ngBindTemplate` directive specifies that the element
20606 * text content should be replaced with the interpolation of the template
20607 * in the `ngBindTemplate` attribute.
20608 * Unlike `ngBind`, the `ngBindTemplate` can contain multiple `{{` `}}`
20609 * expressions. This directive is needed since some HTML elements
20610 * (such as TITLE and OPTION) cannot contain SPAN elements.
20613 * @param {string} ngBindTemplate template of form
20614 * <tt>{{</tt> <tt>expression</tt> <tt>}}</tt> to eval.
20617 * Try it here: enter text in text box and watch the greeting change.
20618 <example module="bindExample">
20619 <file name="index.html">
20621 angular.module('bindExample', [])
20622 .controller('ExampleController', ['$scope', function($scope) {
20623 $scope.salutation = 'Hello';
20624 $scope.name = 'World';
20627 <div ng-controller="ExampleController">
20628 Salutation: <input type="text" ng-model="salutation"><br>
20629 Name: <input type="text" ng-model="name"><br>
20630 <pre ng-bind-template="{{salutation}} {{name}}!"></pre>
20633 <file name="protractor.js" type="protractor">
20634 it('should check ng-bind', function() {
20635 var salutationElem = element(by.binding('salutation'));
20636 var salutationInput = element(by.model('salutation'));
20637 var nameInput = element(by.model('name'));
20639 expect(salutationElem.getText()).toBe('Hello World!');
20641 salutationInput.clear();
20642 salutationInput.sendKeys('Greetings');
20644 nameInput.sendKeys('user');
20646 expect(salutationElem.getText()).toBe('Greetings user!');
20651 var ngBindTemplateDirective = ['$interpolate', '$compile', function($interpolate, $compile) {
20653 compile: function ngBindTemplateCompile(templateElement) {
20654 $compile.$$addBindingClass(templateElement);
20655 return function ngBindTemplateLink(scope, element, attr) {
20656 var interpolateFn = $interpolate(element.attr(attr.$attr.ngBindTemplate));
20657 $compile.$$addBindingInfo(element, interpolateFn.expressions);
20658 element = element[0];
20659 attr.$observe('ngBindTemplate', function(value) {
20660 element.textContent = value === undefined ? '' : value;
20673 * Evaluates the expression and inserts the resulting HTML into the element in a secure way. By default,
20674 * the resulting HTML content will be sanitized using the {@link ngSanitize.$sanitize $sanitize} service.
20675 * To utilize this functionality, ensure that `$sanitize` is available, for example, by including {@link
20676 * ngSanitize} in your module's dependencies (not in core Angular). In order to use {@link ngSanitize}
20677 * in your module's dependencies, you need to include "angular-sanitize.js" in your application.
20679 * You may also bypass sanitization for values you know are safe. To do so, bind to
20680 * an explicitly trusted value via {@link ng.$sce#trustAsHtml $sce.trustAsHtml}. See the example
20681 * under {@link ng.$sce#show-me-an-example-using-sce- Strict Contextual Escaping (SCE)}.
20683 * Note: If a `$sanitize` service is unavailable and the bound value isn't explicitly trusted, you
20684 * will have an exception (instead of an exploit.)
20687 * @param {expression} ngBindHtml {@link guide/expression Expression} to evaluate.
20691 <example module="bindHtmlExample" deps="angular-sanitize.js">
20692 <file name="index.html">
20693 <div ng-controller="ExampleController">
20694 <p ng-bind-html="myHTML"></p>
20698 <file name="script.js">
20699 angular.module('bindHtmlExample', ['ngSanitize'])
20700 .controller('ExampleController', ['$scope', function($scope) {
20702 'I am an <code>HTML</code>string with ' +
20703 '<a href="#">links!</a> and other <em>stuff</em>';
20707 <file name="protractor.js" type="protractor">
20708 it('should check ng-bind-html', function() {
20709 expect(element(by.binding('myHTML')).getText()).toBe(
20710 'I am an HTMLstring with links! and other stuff');
20715 var ngBindHtmlDirective = ['$sce', '$parse', '$compile', function($sce, $parse, $compile) {
20718 compile: function ngBindHtmlCompile(tElement, tAttrs) {
20719 var ngBindHtmlGetter = $parse(tAttrs.ngBindHtml);
20720 var ngBindHtmlWatch = $parse(tAttrs.ngBindHtml, function getStringValue(value) {
20721 return (value || '').toString();
20723 $compile.$$addBindingClass(tElement);
20725 return function ngBindHtmlLink(scope, element, attr) {
20726 $compile.$$addBindingInfo(element, attr.ngBindHtml);
20728 scope.$watch(ngBindHtmlWatch, function ngBindHtmlWatchAction() {
20729 // we re-evaluate the expr because we want a TrustedValueHolderType
20730 // for $sce, not a string
20731 element.html($sce.getTrustedHtml(ngBindHtmlGetter(scope)) || '');
20743 * Evaluate the given expression when the user changes the input.
20744 * The expression is evaluated immediately, unlike the JavaScript onchange event
20745 * which only triggers at the end of a change (usually, when the user leaves the
20746 * form element or presses the return key).
20748 * The `ngChange` expression is only evaluated when a change in the input value causes
20749 * a new value to be committed to the model.
20751 * It will not be evaluated:
20752 * * if the value returned from the `$parsers` transformation pipeline has not changed
20753 * * if the input has continued to be invalid since the model will stay `null`
20754 * * if the model is changed programmatically and not by a change to the input value
20757 * Note, this directive requires `ngModel` to be present.
20760 * @param {expression} ngChange {@link guide/expression Expression} to evaluate upon change
20764 * <example name="ngChange-directive" module="changeExample">
20765 * <file name="index.html">
20767 * angular.module('changeExample', [])
20768 * .controller('ExampleController', ['$scope', function($scope) {
20769 * $scope.counter = 0;
20770 * $scope.change = function() {
20771 * $scope.counter++;
20775 * <div ng-controller="ExampleController">
20776 * <input type="checkbox" ng-model="confirmed" ng-change="change()" id="ng-change-example1" />
20777 * <input type="checkbox" ng-model="confirmed" id="ng-change-example2" />
20778 * <label for="ng-change-example2">Confirmed</label><br />
20779 * <tt>debug = {{confirmed}}</tt><br/>
20780 * <tt>counter = {{counter}}</tt><br/>
20783 * <file name="protractor.js" type="protractor">
20784 * var counter = element(by.binding('counter'));
20785 * var debug = element(by.binding('confirmed'));
20787 * it('should evaluate the expression if changing from view', function() {
20788 * expect(counter.getText()).toContain('0');
20790 * element(by.id('ng-change-example1')).click();
20792 * expect(counter.getText()).toContain('1');
20793 * expect(debug.getText()).toContain('true');
20796 * it('should not evaluate the expression if changing from model', function() {
20797 * element(by.id('ng-change-example2')).click();
20799 * expect(counter.getText()).toContain('0');
20800 * expect(debug.getText()).toContain('true');
20805 var ngChangeDirective = valueFn({
20807 require: 'ngModel',
20808 link: function(scope, element, attr, ctrl) {
20809 ctrl.$viewChangeListeners.push(function() {
20810 scope.$eval(attr.ngChange);
20815 function classDirective(name, selector) {
20816 name = 'ngClass' + name;
20817 return ['$animate', function($animate) {
20820 link: function(scope, element, attr) {
20823 scope.$watch(attr[name], ngClassWatchAction, true);
20825 attr.$observe('class', function(value) {
20826 ngClassWatchAction(scope.$eval(attr[name]));
20830 if (name !== 'ngClass') {
20831 scope.$watch('$index', function($index, old$index) {
20832 // jshint bitwise: false
20833 var mod = $index & 1;
20834 if (mod !== (old$index & 1)) {
20835 var classes = arrayClasses(scope.$eval(attr[name]));
20837 addClasses(classes) :
20838 removeClasses(classes);
20843 function addClasses(classes) {
20844 var newClasses = digestClassCounts(classes, 1);
20845 attr.$addClass(newClasses);
20848 function removeClasses(classes) {
20849 var newClasses = digestClassCounts(classes, -1);
20850 attr.$removeClass(newClasses);
20853 function digestClassCounts(classes, count) {
20854 var classCounts = element.data('$classCounts') || {};
20855 var classesToUpdate = [];
20856 forEach(classes, function(className) {
20857 if (count > 0 || classCounts[className]) {
20858 classCounts[className] = (classCounts[className] || 0) + count;
20859 if (classCounts[className] === +(count > 0)) {
20860 classesToUpdate.push(className);
20864 element.data('$classCounts', classCounts);
20865 return classesToUpdate.join(' ');
20868 function updateClasses(oldClasses, newClasses) {
20869 var toAdd = arrayDifference(newClasses, oldClasses);
20870 var toRemove = arrayDifference(oldClasses, newClasses);
20871 toAdd = digestClassCounts(toAdd, 1);
20872 toRemove = digestClassCounts(toRemove, -1);
20873 if (toAdd && toAdd.length) {
20874 $animate.addClass(element, toAdd);
20876 if (toRemove && toRemove.length) {
20877 $animate.removeClass(element, toRemove);
20881 function ngClassWatchAction(newVal) {
20882 if (selector === true || scope.$index % 2 === selector) {
20883 var newClasses = arrayClasses(newVal || []);
20885 addClasses(newClasses);
20886 } else if (!equals(newVal,oldVal)) {
20887 var oldClasses = arrayClasses(oldVal);
20888 updateClasses(oldClasses, newClasses);
20891 oldVal = shallowCopy(newVal);
20896 function arrayDifference(tokens1, tokens2) {
20900 for (var i = 0; i < tokens1.length; i++) {
20901 var token = tokens1[i];
20902 for (var j = 0; j < tokens2.length; j++) {
20903 if (token == tokens2[j]) continue outer;
20905 values.push(token);
20910 function arrayClasses(classVal) {
20911 if (isArray(classVal)) {
20913 } else if (isString(classVal)) {
20914 return classVal.split(' ');
20915 } else if (isObject(classVal)) {
20917 forEach(classVal, function(v, k) {
20919 classes = classes.concat(k.split(' '));
20935 * The `ngClass` directive allows you to dynamically set CSS classes on an HTML element by databinding
20936 * an expression that represents all classes to be added.
20938 * The directive operates in three different ways, depending on which of three types the expression
20941 * 1. If the expression evaluates to a string, the string should be one or more space-delimited class
20944 * 2. If the expression evaluates to an array, each element of the array should be a string that is
20945 * one or more space-delimited class names.
20947 * 3. If the expression evaluates to an object, then for each key-value pair of the
20948 * object with a truthy value the corresponding key is used as a class name.
20950 * The directive won't add duplicate classes if a particular class was already set.
20952 * When the expression changes, the previously added classes are removed and only then the
20953 * new classes are added.
20956 * **add** - happens just before the class is applied to the elements
20958 * **remove** - happens just before the class is removed from the element
20961 * @param {expression} ngClass {@link guide/expression Expression} to eval. The result
20962 * of the evaluation can be a string representing space delimited class
20963 * names, an array, or a map of class names to boolean values. In the case of a map, the
20964 * names of the properties whose values are truthy will be added as css classes to the
20967 * @example Example that demonstrates basic bindings via ngClass directive.
20969 <file name="index.html">
20970 <p ng-class="{strike: deleted, bold: important, red: error}">Map Syntax Example</p>
20971 <input type="checkbox" ng-model="deleted"> deleted (apply "strike" class)<br>
20972 <input type="checkbox" ng-model="important"> important (apply "bold" class)<br>
20973 <input type="checkbox" ng-model="error"> error (apply "red" class)
20975 <p ng-class="style">Using String Syntax</p>
20976 <input type="text" ng-model="style" placeholder="Type: bold strike red">
20978 <p ng-class="[style1, style2, style3]">Using Array Syntax</p>
20979 <input ng-model="style1" placeholder="Type: bold, strike or red"><br>
20980 <input ng-model="style2" placeholder="Type: bold, strike or red"><br>
20981 <input ng-model="style3" placeholder="Type: bold, strike or red"><br>
20983 <file name="style.css">
20985 text-decoration: line-through;
20994 <file name="protractor.js" type="protractor">
20995 var ps = element.all(by.css('p'));
20997 it('should let you toggle the class', function() {
20999 expect(ps.first().getAttribute('class')).not.toMatch(/bold/);
21000 expect(ps.first().getAttribute('class')).not.toMatch(/red/);
21002 element(by.model('important')).click();
21003 expect(ps.first().getAttribute('class')).toMatch(/bold/);
21005 element(by.model('error')).click();
21006 expect(ps.first().getAttribute('class')).toMatch(/red/);
21009 it('should let you toggle string example', function() {
21010 expect(ps.get(1).getAttribute('class')).toBe('');
21011 element(by.model('style')).clear();
21012 element(by.model('style')).sendKeys('red');
21013 expect(ps.get(1).getAttribute('class')).toBe('red');
21016 it('array example should have 3 classes', function() {
21017 expect(ps.last().getAttribute('class')).toBe('');
21018 element(by.model('style1')).sendKeys('bold');
21019 element(by.model('style2')).sendKeys('strike');
21020 element(by.model('style3')).sendKeys('red');
21021 expect(ps.last().getAttribute('class')).toBe('bold strike red');
21028 The example below demonstrates how to perform animations using ngClass.
21030 <example module="ngAnimate" deps="angular-animate.js" animations="true">
21031 <file name="index.html">
21032 <input id="setbtn" type="button" value="set" ng-click="myVar='my-class'">
21033 <input id="clearbtn" type="button" value="clear" ng-click="myVar=''">
21035 <span class="base-class" ng-class="myVar">Sample Text</span>
21037 <file name="style.css">
21039 -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
21040 transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
21043 .base-class.my-class {
21048 <file name="protractor.js" type="protractor">
21049 it('should check ng-class', function() {
21050 expect(element(by.css('.base-class')).getAttribute('class')).not.
21051 toMatch(/my-class/);
21053 element(by.id('setbtn')).click();
21055 expect(element(by.css('.base-class')).getAttribute('class')).
21056 toMatch(/my-class/);
21058 element(by.id('clearbtn')).click();
21060 expect(element(by.css('.base-class')).getAttribute('class')).not.
21061 toMatch(/my-class/);
21067 ## ngClass and pre-existing CSS3 Transitions/Animations
21068 The ngClass directive still supports CSS3 Transitions/Animations even if they do not follow the ngAnimate CSS naming structure.
21069 Upon animation ngAnimate will apply supplementary CSS classes to track the start and end of an animation, but this will not hinder
21070 any pre-existing CSS transitions already on the element. To get an idea of what happens during a class-based animation, be sure
21071 to view the step by step details of {@link ng.$animate#addClass $animate.addClass} and
21072 {@link ng.$animate#removeClass $animate.removeClass}.
21074 var ngClassDirective = classDirective('', true);
21082 * The `ngClassOdd` and `ngClassEven` directives work exactly as
21083 * {@link ng.directive:ngClass ngClass}, except they work in
21084 * conjunction with `ngRepeat` and take effect only on odd (even) rows.
21086 * This directive can be applied only within the scope of an
21087 * {@link ng.directive:ngRepeat ngRepeat}.
21090 * @param {expression} ngClassOdd {@link guide/expression Expression} to eval. The result
21091 * of the evaluation can be a string representing space delimited class names or an array.
21095 <file name="index.html">
21096 <ol ng-init="names=['John', 'Mary', 'Cate', 'Suz']">
21097 <li ng-repeat="name in names">
21098 <span ng-class-odd="'odd'" ng-class-even="'even'">
21104 <file name="style.css">
21112 <file name="protractor.js" type="protractor">
21113 it('should check ng-class-odd and ng-class-even', function() {
21114 expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')).
21116 expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')).
21122 var ngClassOddDirective = classDirective('Odd', 0);
21126 * @name ngClassEven
21130 * The `ngClassOdd` and `ngClassEven` directives work exactly as
21131 * {@link ng.directive:ngClass ngClass}, except they work in
21132 * conjunction with `ngRepeat` and take effect only on odd (even) rows.
21134 * This directive can be applied only within the scope of an
21135 * {@link ng.directive:ngRepeat ngRepeat}.
21138 * @param {expression} ngClassEven {@link guide/expression Expression} to eval. The
21139 * result of the evaluation can be a string representing space delimited class names or an array.
21143 <file name="index.html">
21144 <ol ng-init="names=['John', 'Mary', 'Cate', 'Suz']">
21145 <li ng-repeat="name in names">
21146 <span ng-class-odd="'odd'" ng-class-even="'even'">
21147 {{name}}
21152 <file name="style.css">
21160 <file name="protractor.js" type="protractor">
21161 it('should check ng-class-odd and ng-class-even', function() {
21162 expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')).
21164 expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')).
21170 var ngClassEvenDirective = classDirective('Even', 1);
21178 * The `ngCloak` directive is used to prevent the Angular html template from being briefly
21179 * displayed by the browser in its raw (uncompiled) form while your application is loading. Use this
21180 * directive to avoid the undesirable flicker effect caused by the html template display.
21182 * The directive can be applied to the `<body>` element, but the preferred usage is to apply
21183 * multiple `ngCloak` directives to small portions of the page to permit progressive rendering
21184 * of the browser view.
21186 * `ngCloak` works in cooperation with the following css rule embedded within `angular.js` and
21187 * `angular.min.js`.
21188 * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
21191 * [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
21192 * display: none !important;
21196 * When this css rule is loaded by the browser, all html elements (including their children) that
21197 * are tagged with the `ngCloak` directive are hidden. When Angular encounters this directive
21198 * during the compilation of the template it deletes the `ngCloak` element attribute, making
21199 * the compiled element visible.
21201 * For the best result, the `angular.js` script must be loaded in the head section of the html
21202 * document; alternatively, the css rule above must be included in the external stylesheet of the
21209 <file name="index.html">
21210 <div id="template1" ng-cloak>{{ 'hello' }}</div>
21211 <div id="template2" class="ng-cloak">{{ 'world' }}</div>
21213 <file name="protractor.js" type="protractor">
21214 it('should remove the template directive and css class', function() {
21215 expect($('#template1').getAttribute('ng-cloak')).
21217 expect($('#template2').getAttribute('ng-cloak')).
21224 var ngCloakDirective = ngDirective({
21225 compile: function(element, attr) {
21226 attr.$set('ngCloak', undefined);
21227 element.removeClass('ng-cloak');
21233 * @name ngController
21236 * The `ngController` directive attaches a controller class to the view. This is a key aspect of how angular
21237 * supports the principles behind the Model-View-Controller design pattern.
21239 * MVC components in angular:
21241 * * Model — Models are the properties of a scope; scopes are attached to the DOM where scope properties
21242 * are accessed through bindings.
21243 * * View — The template (HTML with data bindings) that is rendered into the View.
21244 * * Controller — The `ngController` directive specifies a Controller class; the class contains business
21245 * logic behind the application to decorate the scope with functions and values
21247 * Note that you can also attach controllers to the DOM by declaring it in a route definition
21248 * via the {@link ngRoute.$route $route} service. A common mistake is to declare the controller
21249 * again using `ng-controller` in the template itself. This will cause the controller to be attached
21250 * and executed twice.
21255 * @param {expression} ngController Name of a constructor function registered with the current
21256 * {@link ng.$controllerProvider $controllerProvider} or an {@link guide/expression expression}
21257 * that on the current scope evaluates to a constructor function.
21259 * The controller instance can be published into a scope property by specifying
21260 * `ng-controller="as propertyName"`.
21262 * If the current `$controllerProvider` is configured to use globals (via
21263 * {@link ng.$controllerProvider#allowGlobals `$controllerProvider.allowGlobals()` }), this may
21264 * also be the name of a globally accessible constructor function (not recommended).
21267 * Here is a simple form for editing user contact information. Adding, removing, clearing, and
21268 * greeting are methods declared on the controller (see source tab). These methods can
21269 * easily be called from the angular markup. Any changes to the data are automatically reflected
21270 * in the View without the need for a manual update.
21272 * Two different declaration styles are included below:
21274 * * one binds methods and properties directly onto the controller using `this`:
21275 * `ng-controller="SettingsController1 as settings"`
21276 * * one injects `$scope` into the controller:
21277 * `ng-controller="SettingsController2"`
21279 * The second option is more common in the Angular community, and is generally used in boilerplates
21280 * and in this guide. However, there are advantages to binding properties directly to the controller
21281 * and avoiding scope.
21283 * * Using `controller as` makes it obvious which controller you are accessing in the template when
21284 * multiple controllers apply to an element.
21285 * * If you are writing your controllers as classes you have easier access to the properties and
21286 * methods, which will appear on the scope, from inside the controller code.
21287 * * Since there is always a `.` in the bindings, you don't have to worry about prototypal
21288 * inheritance masking primitives.
21290 * This example demonstrates the `controller as` syntax.
21292 * <example name="ngControllerAs" module="controllerAsExample">
21293 * <file name="index.html">
21294 * <div id="ctrl-as-exmpl" ng-controller="SettingsController1 as settings">
21295 * Name: <input type="text" ng-model="settings.name"/>
21296 * [ <a href="" ng-click="settings.greet()">greet</a> ]<br/>
21299 * <li ng-repeat="contact in settings.contacts">
21300 * <select ng-model="contact.type">
21301 * <option>phone</option>
21302 * <option>email</option>
21304 * <input type="text" ng-model="contact.value"/>
21305 * [ <a href="" ng-click="settings.clearContact(contact)">clear</a>
21306 * | <a href="" ng-click="settings.removeContact(contact)">X</a> ]
21308 * <li>[ <a href="" ng-click="settings.addContact()">add</a> ]</li>
21312 * <file name="app.js">
21313 * angular.module('controllerAsExample', [])
21314 * .controller('SettingsController1', SettingsController1);
21316 * function SettingsController1() {
21317 * this.name = "John Smith";
21318 * this.contacts = [
21319 * {type: 'phone', value: '408 555 1212'},
21320 * {type: 'email', value: 'john.smith@example.org'} ];
21323 * SettingsController1.prototype.greet = function() {
21324 * alert(this.name);
21327 * SettingsController1.prototype.addContact = function() {
21328 * this.contacts.push({type: 'email', value: 'yourname@example.org'});
21331 * SettingsController1.prototype.removeContact = function(contactToRemove) {
21332 * var index = this.contacts.indexOf(contactToRemove);
21333 * this.contacts.splice(index, 1);
21336 * SettingsController1.prototype.clearContact = function(contact) {
21337 * contact.type = 'phone';
21338 * contact.value = '';
21341 * <file name="protractor.js" type="protractor">
21342 * it('should check controller as', function() {
21343 * var container = element(by.id('ctrl-as-exmpl'));
21344 * expect(container.element(by.model('settings.name'))
21345 * .getAttribute('value')).toBe('John Smith');
21347 * var firstRepeat =
21348 * container.element(by.repeater('contact in settings.contacts').row(0));
21349 * var secondRepeat =
21350 * container.element(by.repeater('contact in settings.contacts').row(1));
21352 * expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
21353 * .toBe('408 555 1212');
21355 * expect(secondRepeat.element(by.model('contact.value')).getAttribute('value'))
21356 * .toBe('john.smith@example.org');
21358 * firstRepeat.element(by.linkText('clear')).click();
21360 * expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
21363 * container.element(by.linkText('add')).click();
21365 * expect(container.element(by.repeater('contact in settings.contacts').row(2))
21366 * .element(by.model('contact.value'))
21367 * .getAttribute('value'))
21368 * .toBe('yourname@example.org');
21373 * This example demonstrates the "attach to `$scope`" style of controller.
21375 * <example name="ngController" module="controllerExample">
21376 * <file name="index.html">
21377 * <div id="ctrl-exmpl" ng-controller="SettingsController2">
21378 * Name: <input type="text" ng-model="name"/>
21379 * [ <a href="" ng-click="greet()">greet</a> ]<br/>
21382 * <li ng-repeat="contact in contacts">
21383 * <select ng-model="contact.type">
21384 * <option>phone</option>
21385 * <option>email</option>
21387 * <input type="text" ng-model="contact.value"/>
21388 * [ <a href="" ng-click="clearContact(contact)">clear</a>
21389 * | <a href="" ng-click="removeContact(contact)">X</a> ]
21391 * <li>[ <a href="" ng-click="addContact()">add</a> ]</li>
21395 * <file name="app.js">
21396 * angular.module('controllerExample', [])
21397 * .controller('SettingsController2', ['$scope', SettingsController2]);
21399 * function SettingsController2($scope) {
21400 * $scope.name = "John Smith";
21401 * $scope.contacts = [
21402 * {type:'phone', value:'408 555 1212'},
21403 * {type:'email', value:'john.smith@example.org'} ];
21405 * $scope.greet = function() {
21406 * alert($scope.name);
21409 * $scope.addContact = function() {
21410 * $scope.contacts.push({type:'email', value:'yourname@example.org'});
21413 * $scope.removeContact = function(contactToRemove) {
21414 * var index = $scope.contacts.indexOf(contactToRemove);
21415 * $scope.contacts.splice(index, 1);
21418 * $scope.clearContact = function(contact) {
21419 * contact.type = 'phone';
21420 * contact.value = '';
21424 * <file name="protractor.js" type="protractor">
21425 * it('should check controller', function() {
21426 * var container = element(by.id('ctrl-exmpl'));
21428 * expect(container.element(by.model('name'))
21429 * .getAttribute('value')).toBe('John Smith');
21431 * var firstRepeat =
21432 * container.element(by.repeater('contact in contacts').row(0));
21433 * var secondRepeat =
21434 * container.element(by.repeater('contact in contacts').row(1));
21436 * expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
21437 * .toBe('408 555 1212');
21438 * expect(secondRepeat.element(by.model('contact.value')).getAttribute('value'))
21439 * .toBe('john.smith@example.org');
21441 * firstRepeat.element(by.linkText('clear')).click();
21443 * expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
21446 * container.element(by.linkText('add')).click();
21448 * expect(container.element(by.repeater('contact in contacts').row(2))
21449 * .element(by.model('contact.value'))
21450 * .getAttribute('value'))
21451 * .toBe('yourname@example.org');
21457 var ngControllerDirective = [function() {
21472 * Enables [CSP (Content Security Policy)](https://developer.mozilla.org/en/Security/CSP) support.
21474 * This is necessary when developing things like Google Chrome Extensions or Universal Windows Apps.
21476 * CSP forbids apps to use `eval` or `Function(string)` generated functions (among other things).
21477 * For Angular to be CSP compatible there are only two things that we need to do differently:
21479 * - don't use `Function` constructor to generate optimized value getters
21480 * - don't inject custom stylesheet into the document
21482 * AngularJS uses `Function(string)` generated functions as a speed optimization. Applying the `ngCsp`
21483 * directive will cause Angular to use CSP compatibility mode. When this mode is on AngularJS will
21484 * evaluate all expressions up to 30% slower than in non-CSP mode, but no security violations will
21487 * CSP forbids JavaScript to inline stylesheet rules. In non CSP mode Angular automatically
21488 * includes some CSS rules (e.g. {@link ng.directive:ngCloak ngCloak}).
21489 * To make those directives work in CSP mode, include the `angular-csp.css` manually.
21491 * Angular tries to autodetect if CSP is active and automatically turn on the CSP-safe mode. This
21492 * autodetection however triggers a CSP error to be logged in the console:
21495 * Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of
21496 * script in the following Content Security Policy directive: "default-src 'self'". Note that
21497 * 'script-src' was not explicitly set, so 'default-src' is used as a fallback.
21500 * This error is harmless but annoying. To prevent the error from showing up, put the `ngCsp`
21501 * directive on the root element of the application or on the `angular.js` script tag, whichever
21502 * appears first in the html document.
21504 * *Note: This directive is only available in the `ng-csp` and `data-ng-csp` attribute form.*
21507 * This example shows how to apply the `ngCsp` directive to the `html` tag.
21510 <html ng-app ng-csp>
21516 // Note: the suffix `.csp` in the example name triggers
21517 // csp mode in our http server!
21518 <example name="example.csp" module="cspExample" ng-csp="true">
21519 <file name="index.html">
21520 <div ng-controller="MainController as ctrl">
21522 <button ng-click="ctrl.inc()" id="inc">Increment</button>
21523 <span id="counter">
21529 <button ng-click="ctrl.evil()" id="evil">Evil</button>
21530 <span id="evilError">
21536 <file name="script.js">
21537 angular.module('cspExample', [])
21538 .controller('MainController', function() {
21540 this.inc = function() {
21543 this.evil = function() {
21544 // jshint evil:true
21548 this.evilError = e.message;
21553 <file name="protractor.js" type="protractor">
21554 var util, webdriver;
21556 var incBtn = element(by.id('inc'));
21557 var counter = element(by.id('counter'));
21558 var evilBtn = element(by.id('evil'));
21559 var evilError = element(by.id('evilError'));
21561 function getAndClearSevereErrors() {
21562 return browser.manage().logs().get('browser').then(function(browserLog) {
21563 return browserLog.filter(function(logEntry) {
21564 return logEntry.level.value > webdriver.logging.Level.WARNING.value;
21569 function clearErrors() {
21570 getAndClearSevereErrors();
21573 function expectNoErrors() {
21574 getAndClearSevereErrors().then(function(filteredLog) {
21575 expect(filteredLog.length).toEqual(0);
21576 if (filteredLog.length) {
21577 console.log('browser console errors: ' + util.inspect(filteredLog));
21582 function expectError(regex) {
21583 getAndClearSevereErrors().then(function(filteredLog) {
21585 filteredLog.forEach(function(log) {
21586 if (log.message.match(regex)) {
21591 throw new Error('expected an error that matches ' + regex);
21596 beforeEach(function() {
21597 util = require('util');
21598 webdriver = require('protractor/node_modules/selenium-webdriver');
21601 // For now, we only test on Chrome,
21602 // as Safari does not load the page with Protractor's injected scripts,
21603 // and Firefox webdriver always disables content security policy (#6358)
21604 if (browser.params.browser !== 'chrome') {
21608 it('should not report errors when the page is loaded', function() {
21609 // clear errors so we are not dependent on previous tests
21611 // Need to reload the page as the page is already loaded when
21613 browser.driver.getCurrentUrl().then(function(url) {
21619 it('should evaluate expressions', function() {
21620 expect(counter.getText()).toEqual('0');
21622 expect(counter.getText()).toEqual('1');
21626 it('should throw and report an error when using "eval"', function() {
21628 expect(evilError.getText()).toMatch(/Content Security Policy/);
21629 expectError(/Content Security Policy/);
21635 // ngCsp is not implemented as a proper directive any more, because we need it be processed while we
21636 // bootstrap the system (before $parse is instantiated), for this reason we just have
21637 // the csp.isActive() fn that looks for ng-csp attribute anywhere in the current doc
21644 * The ngClick directive allows you to specify custom behavior when
21645 * an element is clicked.
21649 * @param {expression} ngClick {@link guide/expression Expression} to evaluate upon
21650 * click. ({@link guide/expression#-event- Event object is available as `$event`})
21654 <file name="index.html">
21655 <button ng-click="count = count + 1" ng-init="count=0">
21662 <file name="protractor.js" type="protractor">
21663 it('should check ng-click', function() {
21664 expect(element(by.binding('count')).getText()).toMatch('0');
21665 element(by.css('button')).click();
21666 expect(element(by.binding('count')).getText()).toMatch('1');
21672 * A collection of directives that allows creation of custom event handlers that are defined as
21673 * angular expressions and are compiled and executed within the current scope.
21675 var ngEventDirectives = {};
21677 // For events that might fire synchronously during DOM manipulation
21678 // we need to execute their event handlers asynchronously using $evalAsync,
21679 // so that they are not executed in an inconsistent state.
21680 var forceAsyncEvents = {
21685 'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste'.split(' '),
21686 function(eventName) {
21687 var directiveName = directiveNormalize('ng-' + eventName);
21688 ngEventDirectives[directiveName] = ['$parse', '$rootScope', function($parse, $rootScope) {
21691 compile: function($element, attr) {
21692 // We expose the powerful $event object on the scope that provides access to the Window,
21693 // etc. that isn't protected by the fast paths in $parse. We explicitly request better
21694 // checks at the cost of speed since event handler expressions are not executed as
21695 // frequently as regular change detection.
21696 var fn = $parse(attr[directiveName], /* interceptorFn */ null, /* expensiveChecks */ true);
21697 return function ngEventHandler(scope, element) {
21698 element.on(eventName, function(event) {
21699 var callback = function() {
21700 fn(scope, {$event:event});
21702 if (forceAsyncEvents[eventName] && $rootScope.$$phase) {
21703 scope.$evalAsync(callback);
21705 scope.$apply(callback);
21720 * The `ngDblclick` directive allows you to specify custom behavior on a dblclick event.
21724 * @param {expression} ngDblclick {@link guide/expression Expression} to evaluate upon
21725 * a dblclick. (The Event object is available as `$event`)
21729 <file name="index.html">
21730 <button ng-dblclick="count = count + 1" ng-init="count=0">
21731 Increment (on double click)
21741 * @name ngMousedown
21744 * The ngMousedown directive allows you to specify custom behavior on mousedown event.
21748 * @param {expression} ngMousedown {@link guide/expression Expression} to evaluate upon
21749 * mousedown. ({@link guide/expression#-event- Event object is available as `$event`})
21753 <file name="index.html">
21754 <button ng-mousedown="count = count + 1" ng-init="count=0">
21755 Increment (on mouse down)
21768 * Specify custom behavior on mouseup event.
21772 * @param {expression} ngMouseup {@link guide/expression Expression} to evaluate upon
21773 * mouseup. ({@link guide/expression#-event- Event object is available as `$event`})
21777 <file name="index.html">
21778 <button ng-mouseup="count = count + 1" ng-init="count=0">
21779 Increment (on mouse up)
21788 * @name ngMouseover
21791 * Specify custom behavior on mouseover event.
21795 * @param {expression} ngMouseover {@link guide/expression Expression} to evaluate upon
21796 * mouseover. ({@link guide/expression#-event- Event object is available as `$event`})
21800 <file name="index.html">
21801 <button ng-mouseover="count = count + 1" ng-init="count=0">
21802 Increment (when mouse is over)
21812 * @name ngMouseenter
21815 * Specify custom behavior on mouseenter event.
21819 * @param {expression} ngMouseenter {@link guide/expression Expression} to evaluate upon
21820 * mouseenter. ({@link guide/expression#-event- Event object is available as `$event`})
21824 <file name="index.html">
21825 <button ng-mouseenter="count = count + 1" ng-init="count=0">
21826 Increment (when mouse enters)
21836 * @name ngMouseleave
21839 * Specify custom behavior on mouseleave event.
21843 * @param {expression} ngMouseleave {@link guide/expression Expression} to evaluate upon
21844 * mouseleave. ({@link guide/expression#-event- Event object is available as `$event`})
21848 <file name="index.html">
21849 <button ng-mouseleave="count = count + 1" ng-init="count=0">
21850 Increment (when mouse leaves)
21860 * @name ngMousemove
21863 * Specify custom behavior on mousemove event.
21867 * @param {expression} ngMousemove {@link guide/expression Expression} to evaluate upon
21868 * mousemove. ({@link guide/expression#-event- Event object is available as `$event`})
21872 <file name="index.html">
21873 <button ng-mousemove="count = count + 1" ng-init="count=0">
21874 Increment (when mouse moves)
21887 * Specify custom behavior on keydown event.
21891 * @param {expression} ngKeydown {@link guide/expression Expression} to evaluate upon
21892 * keydown. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
21896 <file name="index.html">
21897 <input ng-keydown="count = count + 1" ng-init="count=0">
21898 key down count: {{count}}
21909 * Specify custom behavior on keyup event.
21913 * @param {expression} ngKeyup {@link guide/expression Expression} to evaluate upon
21914 * keyup. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
21918 <file name="index.html">
21919 <p>Typing in the input box below updates the key count</p>
21920 <input ng-keyup="count = count + 1" ng-init="count=0"> key up count: {{count}}
21922 <p>Typing in the input box below updates the keycode</p>
21923 <input ng-keyup="event=$event">
21924 <p>event keyCode: {{ event.keyCode }}</p>
21925 <p>event altKey: {{ event.altKey }}</p>
21936 * Specify custom behavior on keypress event.
21939 * @param {expression} ngKeypress {@link guide/expression Expression} to evaluate upon
21940 * keypress. ({@link guide/expression#-event- Event object is available as `$event`}
21941 * and can be interrogated for keyCode, altKey, etc.)
21945 <file name="index.html">
21946 <input ng-keypress="count = count + 1" ng-init="count=0">
21947 key press count: {{count}}
21958 * Enables binding angular expressions to onsubmit events.
21960 * Additionally it prevents the default action (which for form means sending the request to the
21961 * server and reloading the current page), but only if the form does not contain `action`,
21962 * `data-action`, or `x-action` attributes.
21964 * <div class="alert alert-warning">
21965 * **Warning:** Be careful not to cause "double-submission" by using both the `ngClick` and
21966 * `ngSubmit` handlers together. See the
21967 * {@link form#submitting-a-form-and-preventing-the-default-action `form` directive documentation}
21968 * for a detailed discussion of when `ngSubmit` may be triggered.
21973 * @param {expression} ngSubmit {@link guide/expression Expression} to eval.
21974 * ({@link guide/expression#-event- Event object is available as `$event`})
21977 <example module="submitExample">
21978 <file name="index.html">
21980 angular.module('submitExample', [])
21981 .controller('ExampleController', ['$scope', function($scope) {
21983 $scope.text = 'hello';
21984 $scope.submit = function() {
21986 $scope.list.push(this.text);
21992 <form ng-submit="submit()" ng-controller="ExampleController">
21993 Enter text and hit enter:
21994 <input type="text" ng-model="text" name="text" />
21995 <input type="submit" id="submit" value="Submit" />
21996 <pre>list={{list}}</pre>
21999 <file name="protractor.js" type="protractor">
22000 it('should check ng-submit', function() {
22001 expect(element(by.binding('list')).getText()).toBe('list=[]');
22002 element(by.css('#submit')).click();
22003 expect(element(by.binding('list')).getText()).toContain('hello');
22004 expect(element(by.model('text')).getAttribute('value')).toBe('');
22006 it('should ignore empty strings', function() {
22007 expect(element(by.binding('list')).getText()).toBe('list=[]');
22008 element(by.css('#submit')).click();
22009 element(by.css('#submit')).click();
22010 expect(element(by.binding('list')).getText()).toContain('hello');
22021 * Specify custom behavior on focus event.
22023 * Note: As the `focus` event is executed synchronously when calling `input.focus()`
22024 * AngularJS executes the expression using `scope.$evalAsync` if the event is fired
22025 * during an `$apply` to ensure a consistent state.
22027 * @element window, input, select, textarea, a
22029 * @param {expression} ngFocus {@link guide/expression Expression} to evaluate upon
22030 * focus. ({@link guide/expression#-event- Event object is available as `$event`})
22033 * See {@link ng.directive:ngClick ngClick}
22041 * Specify custom behavior on blur event.
22043 * A [blur event](https://developer.mozilla.org/en-US/docs/Web/Events/blur) fires when
22044 * an element has lost focus.
22046 * Note: As the `blur` event is executed synchronously also during DOM manipulations
22047 * (e.g. removing a focussed input),
22048 * AngularJS executes the expression using `scope.$evalAsync` if the event is fired
22049 * during an `$apply` to ensure a consistent state.
22051 * @element window, input, select, textarea, a
22053 * @param {expression} ngBlur {@link guide/expression Expression} to evaluate upon
22054 * blur. ({@link guide/expression#-event- Event object is available as `$event`})
22057 * See {@link ng.directive:ngClick ngClick}
22065 * Specify custom behavior on copy event.
22067 * @element window, input, select, textarea, a
22069 * @param {expression} ngCopy {@link guide/expression Expression} to evaluate upon
22070 * copy. ({@link guide/expression#-event- Event object is available as `$event`})
22074 <file name="index.html">
22075 <input ng-copy="copied=true" ng-init="copied=false; value='copy me'" ng-model="value">
22086 * Specify custom behavior on cut event.
22088 * @element window, input, select, textarea, a
22090 * @param {expression} ngCut {@link guide/expression Expression} to evaluate upon
22091 * cut. ({@link guide/expression#-event- Event object is available as `$event`})
22095 <file name="index.html">
22096 <input ng-cut="cut=true" ng-init="cut=false; value='cut me'" ng-model="value">
22107 * Specify custom behavior on paste event.
22109 * @element window, input, select, textarea, a
22111 * @param {expression} ngPaste {@link guide/expression Expression} to evaluate upon
22112 * paste. ({@link guide/expression#-event- Event object is available as `$event`})
22116 <file name="index.html">
22117 <input ng-paste="paste=true" ng-init="paste=false" placeholder='paste here'>
22129 * The `ngIf` directive removes or recreates a portion of the DOM tree based on an
22130 * {expression}. If the expression assigned to `ngIf` evaluates to a false
22131 * value then the element is removed from the DOM, otherwise a clone of the
22132 * element is reinserted into the DOM.
22134 * `ngIf` differs from `ngShow` and `ngHide` in that `ngIf` completely removes and recreates the
22135 * element in the DOM rather than changing its visibility via the `display` css property. A common
22136 * case when this difference is significant is when using css selectors that rely on an element's
22137 * position within the DOM, such as the `:first-child` or `:last-child` pseudo-classes.
22139 * Note that when an element is removed using `ngIf` its scope is destroyed and a new scope
22140 * is created when the element is restored. The scope created within `ngIf` inherits from
22141 * its parent scope using
22142 * [prototypal inheritance](https://github.com/angular/angular.js/wiki/Understanding-Scopes#javascript-prototypal-inheritance).
22143 * An important implication of this is if `ngModel` is used within `ngIf` to bind to
22144 * a javascript primitive defined in the parent scope. In this case any modifications made to the
22145 * variable within the child scope will override (hide) the value in the parent scope.
22147 * Also, `ngIf` recreates elements using their compiled state. An example of this behavior
22148 * is if an element's class attribute is directly modified after it's compiled, using something like
22149 * jQuery's `.addClass()` method, and the element is later removed. When `ngIf` recreates the element
22150 * the added class will be lost because the original compiled state is used to regenerate the element.
22152 * Additionally, you can provide animations via the `ngAnimate` module to animate the `enter`
22153 * and `leave` effects.
22156 * enter - happens just after the `ngIf` contents change and a new DOM element is created and injected into the `ngIf` container
22157 * leave - happens just before the `ngIf` contents are removed from the DOM
22162 * @param {expression} ngIf If the {@link guide/expression expression} is falsy then
22163 * the element is removed from the DOM tree. If it is truthy a copy of the compiled
22164 * element is added to the DOM tree.
22167 <example module="ngAnimate" deps="angular-animate.js" animations="true">
22168 <file name="index.html">
22169 Click me: <input type="checkbox" ng-model="checked" ng-init="checked=true" /><br/>
22171 <span ng-if="checked" class="animate-if">
22172 This is removed when the checkbox is unchecked.
22175 <file name="animations.css">
22178 border:1px solid black;
22182 .animate-if.ng-enter, .animate-if.ng-leave {
22183 -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
22184 transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
22187 .animate-if.ng-enter,
22188 .animate-if.ng-leave.ng-leave-active {
22192 .animate-if.ng-leave,
22193 .animate-if.ng-enter.ng-enter-active {
22199 var ngIfDirective = ['$animate', function($animate) {
22201 multiElement: true,
22202 transclude: 'element',
22207 link: function($scope, $element, $attr, ctrl, $transclude) {
22208 var block, childScope, previousElements;
22209 $scope.$watch($attr.ngIf, function ngIfWatchAction(value) {
22213 $transclude(function(clone, newScope) {
22214 childScope = newScope;
22215 clone[clone.length++] = document.createComment(' end ngIf: ' + $attr.ngIf + ' ');
22216 // Note: We only need the first/last node of the cloned nodes.
22217 // However, we need to keep the reference to the jqlite wrapper as it might be changed later
22218 // by a directive with templateUrl when its template arrives.
22222 $animate.enter(clone, $element.parent(), $element);
22226 if (previousElements) {
22227 previousElements.remove();
22228 previousElements = null;
22231 childScope.$destroy();
22235 previousElements = getBlockNodes(block.clone);
22236 $animate.leave(previousElements).then(function() {
22237 previousElements = null;
22253 * Fetches, compiles and includes an external HTML fragment.
22255 * By default, the template URL is restricted to the same domain and protocol as the
22256 * application document. This is done by calling {@link $sce#getTrustedResourceUrl
22257 * $sce.getTrustedResourceUrl} on it. To load templates from other domains or protocols
22258 * you may either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist them} or
22259 * {@link $sce#trustAsResourceUrl wrap them} as trusted values. Refer to Angular's {@link
22260 * ng.$sce Strict Contextual Escaping}.
22262 * In addition, the browser's
22263 * [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest)
22264 * and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/)
22265 * policy may further restrict whether the template is successfully loaded.
22266 * For example, `ngInclude` won't work for cross-domain requests on all browsers and for `file://`
22267 * access on some browsers.
22270 * enter - animation is used to bring new content into the browser.
22271 * leave - animation is used to animate existing content away.
22273 * The enter and leave animation occur concurrently.
22278 * @param {string} ngInclude|src angular expression evaluating to URL. If the source is a string constant,
22279 * make sure you wrap it in **single** quotes, e.g. `src="'myPartialTemplate.html'"`.
22280 * @param {string=} onload Expression to evaluate when a new partial is loaded.
22282 * @param {string=} autoscroll Whether `ngInclude` should call {@link ng.$anchorScroll
22283 * $anchorScroll} to scroll the viewport after the content is loaded.
22285 * - If the attribute is not set, disable scrolling.
22286 * - If the attribute is set without value, enable scrolling.
22287 * - Otherwise enable scrolling only if the expression evaluates to truthy value.
22290 <example module="includeExample" deps="angular-animate.js" animations="true">
22291 <file name="index.html">
22292 <div ng-controller="ExampleController">
22293 <select ng-model="template" ng-options="t.name for t in templates">
22294 <option value="">(blank)</option>
22296 url of the template: <code>{{template.url}}</code>
22298 <div class="slide-animate-container">
22299 <div class="slide-animate" ng-include="template.url"></div>
22303 <file name="script.js">
22304 angular.module('includeExample', ['ngAnimate'])
22305 .controller('ExampleController', ['$scope', function($scope) {
22307 [ { name: 'template1.html', url: 'template1.html'},
22308 { name: 'template2.html', url: 'template2.html'} ];
22309 $scope.template = $scope.templates[0];
22312 <file name="template1.html">
22313 Content of template1.html
22315 <file name="template2.html">
22316 Content of template2.html
22318 <file name="animations.css">
22319 .slide-animate-container {
22322 border:1px solid black;
22331 .slide-animate.ng-enter, .slide-animate.ng-leave {
22332 -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
22333 transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
22344 .slide-animate.ng-enter {
22347 .slide-animate.ng-enter.ng-enter-active {
22351 .slide-animate.ng-leave {
22354 .slide-animate.ng-leave.ng-leave-active {
22358 <file name="protractor.js" type="protractor">
22359 var templateSelect = element(by.model('template'));
22360 var includeElem = element(by.css('[ng-include]'));
22362 it('should load template1.html', function() {
22363 expect(includeElem.getText()).toMatch(/Content of template1.html/);
22366 it('should load template2.html', function() {
22367 if (browser.params.browser == 'firefox') {
22368 // Firefox can't handle using selects
22369 // See https://github.com/angular/protractor/issues/480
22372 templateSelect.click();
22373 templateSelect.all(by.css('option')).get(2).click();
22374 expect(includeElem.getText()).toMatch(/Content of template2.html/);
22377 it('should change to blank', function() {
22378 if (browser.params.browser == 'firefox') {
22379 // Firefox can't handle using selects
22382 templateSelect.click();
22383 templateSelect.all(by.css('option')).get(0).click();
22384 expect(includeElem.isPresent()).toBe(false);
22393 * @name ngInclude#$includeContentRequested
22394 * @eventType emit on the scope ngInclude was declared in
22396 * Emitted every time the ngInclude content is requested.
22398 * @param {Object} angularEvent Synthetic event object.
22399 * @param {String} src URL of content to load.
22405 * @name ngInclude#$includeContentLoaded
22406 * @eventType emit on the current ngInclude scope
22408 * Emitted every time the ngInclude content is reloaded.
22410 * @param {Object} angularEvent Synthetic event object.
22411 * @param {String} src URL of content to load.
22417 * @name ngInclude#$includeContentError
22418 * @eventType emit on the scope ngInclude was declared in
22420 * Emitted when a template HTTP request yields an erroneous response (status < 200 || status > 299)
22422 * @param {Object} angularEvent Synthetic event object.
22423 * @param {String} src URL of content to load.
22425 var ngIncludeDirective = ['$templateRequest', '$anchorScroll', '$animate',
22426 function($templateRequest, $anchorScroll, $animate) {
22431 transclude: 'element',
22432 controller: angular.noop,
22433 compile: function(element, attr) {
22434 var srcExp = attr.ngInclude || attr.src,
22435 onloadExp = attr.onload || '',
22436 autoScrollExp = attr.autoscroll;
22438 return function(scope, $element, $attr, ctrl, $transclude) {
22439 var changeCounter = 0,
22444 var cleanupLastIncludeContent = function() {
22445 if (previousElement) {
22446 previousElement.remove();
22447 previousElement = null;
22449 if (currentScope) {
22450 currentScope.$destroy();
22451 currentScope = null;
22453 if (currentElement) {
22454 $animate.leave(currentElement).then(function() {
22455 previousElement = null;
22457 previousElement = currentElement;
22458 currentElement = null;
22462 scope.$watch(srcExp, function ngIncludeWatchAction(src) {
22463 var afterAnimation = function() {
22464 if (isDefined(autoScrollExp) && (!autoScrollExp || scope.$eval(autoScrollExp))) {
22468 var thisChangeId = ++changeCounter;
22471 //set the 2nd param to true to ignore the template request error so that the inner
22472 //contents and scope can be cleaned up.
22473 $templateRequest(src, true).then(function(response) {
22474 if (thisChangeId !== changeCounter) return;
22475 var newScope = scope.$new();
22476 ctrl.template = response;
22478 // Note: This will also link all children of ng-include that were contained in the original
22479 // html. If that content contains controllers, ... they could pollute/change the scope.
22480 // However, using ng-include on an element with additional content does not make sense...
22481 // Note: We can't remove them in the cloneAttchFn of $transclude as that
22482 // function is called before linking the content, which would apply child
22483 // directives to non existing elements.
22484 var clone = $transclude(newScope, function(clone) {
22485 cleanupLastIncludeContent();
22486 $animate.enter(clone, null, $element).then(afterAnimation);
22489 currentScope = newScope;
22490 currentElement = clone;
22492 currentScope.$emit('$includeContentLoaded', src);
22493 scope.$eval(onloadExp);
22495 if (thisChangeId === changeCounter) {
22496 cleanupLastIncludeContent();
22497 scope.$emit('$includeContentError', src);
22500 scope.$emit('$includeContentRequested', src);
22502 cleanupLastIncludeContent();
22503 ctrl.template = null;
22511 // This directive is called during the $transclude call of the first `ngInclude` directive.
22512 // It will replace and compile the content of the element with the loaded template.
22513 // We need this directive so that the element content is already filled when
22514 // the link function of another directive on the same element as ngInclude
22516 var ngIncludeFillContentDirective = ['$compile',
22517 function($compile) {
22521 require: 'ngInclude',
22522 link: function(scope, $element, $attr, ctrl) {
22523 if (/SVG/.test($element[0].toString())) {
22524 // WebKit: https://bugs.webkit.org/show_bug.cgi?id=135698 --- SVG elements do not
22525 // support innerHTML, so detect this here and try to generate the contents
22528 $compile(jqLiteBuildFragment(ctrl.template, document).childNodes)(scope,
22529 function namespaceAdaptedClone(clone) {
22530 $element.append(clone);
22531 }, {futureParentElement: $element});
22535 $element.html(ctrl.template);
22536 $compile($element.contents())(scope);
22547 * The `ngInit` directive allows you to evaluate an expression in the
22550 * <div class="alert alert-error">
22551 * The only appropriate use of `ngInit` is for aliasing special properties of
22552 * {@link ng.directive:ngRepeat `ngRepeat`}, as seen in the demo below. Besides this case, you
22553 * should use {@link guide/controller controllers} rather than `ngInit`
22554 * to initialize values on a scope.
22556 * <div class="alert alert-warning">
22557 * **Note**: If you have assignment in `ngInit` along with {@link ng.$filter `$filter`}, make
22558 * sure you have parenthesis for correct precedence:
22559 * <pre class="prettyprint">
22560 * `<div ng-init="test1 = (data | orderBy:'name')"></div>`
22567 * @param {expression} ngInit {@link guide/expression Expression} to eval.
22570 <example module="initExample">
22571 <file name="index.html">
22573 angular.module('initExample', [])
22574 .controller('ExampleController', ['$scope', function($scope) {
22575 $scope.list = [['a', 'b'], ['c', 'd']];
22578 <div ng-controller="ExampleController">
22579 <div ng-repeat="innerList in list" ng-init="outerIndex = $index">
22580 <div ng-repeat="value in innerList" ng-init="innerIndex = $index">
22581 <span class="example-init">list[ {{outerIndex}} ][ {{innerIndex}} ] = {{value}};</span>
22586 <file name="protractor.js" type="protractor">
22587 it('should alias index positions', function() {
22588 var elements = element.all(by.css('.example-init'));
22589 expect(elements.get(0).getText()).toBe('list[ 0 ][ 0 ] = a;');
22590 expect(elements.get(1).getText()).toBe('list[ 0 ][ 1 ] = b;');
22591 expect(elements.get(2).getText()).toBe('list[ 1 ][ 0 ] = c;');
22592 expect(elements.get(3).getText()).toBe('list[ 1 ][ 1 ] = d;');
22597 var ngInitDirective = ngDirective({
22599 compile: function() {
22601 pre: function(scope, element, attrs) {
22602 scope.$eval(attrs.ngInit);
22613 * Text input that converts between a delimited string and an array of strings. The default
22614 * delimiter is a comma followed by a space - equivalent to `ng-list=", "`. You can specify a custom
22615 * delimiter as the value of the `ngList` attribute - for example, `ng-list=" | "`.
22617 * The behaviour of the directive is affected by the use of the `ngTrim` attribute.
22618 * * If `ngTrim` is set to `"false"` then whitespace around both the separator and each
22619 * list item is respected. This implies that the user of the directive is responsible for
22620 * dealing with whitespace but also allows you to use whitespace as a delimiter, such as a
22621 * tab or newline character.
22622 * * Otherwise whitespace around the delimiter is ignored when splitting (although it is respected
22623 * when joining the list items back together) and whitespace around each list item is stripped
22624 * before it is added to the model.
22626 * ### Example with Validation
22628 * <example name="ngList-directive" module="listExample">
22629 * <file name="app.js">
22630 * angular.module('listExample', [])
22631 * .controller('ExampleController', ['$scope', function($scope) {
22632 * $scope.names = ['morpheus', 'neo', 'trinity'];
22635 * <file name="index.html">
22636 * <form name="myForm" ng-controller="ExampleController">
22637 * List: <input name="namesInput" ng-model="names" ng-list required>
22638 * <span class="error" ng-show="myForm.namesInput.$error.required">
22641 * <tt>names = {{names}}</tt><br/>
22642 * <tt>myForm.namesInput.$valid = {{myForm.namesInput.$valid}}</tt><br/>
22643 * <tt>myForm.namesInput.$error = {{myForm.namesInput.$error}}</tt><br/>
22644 * <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
22645 * <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
22648 * <file name="protractor.js" type="protractor">
22649 * var listInput = element(by.model('names'));
22650 * var names = element(by.exactBinding('names'));
22651 * var valid = element(by.binding('myForm.namesInput.$valid'));
22652 * var error = element(by.css('span.error'));
22654 * it('should initialize to model', function() {
22655 * expect(names.getText()).toContain('["morpheus","neo","trinity"]');
22656 * expect(valid.getText()).toContain('true');
22657 * expect(error.getCssValue('display')).toBe('none');
22660 * it('should be invalid if empty', function() {
22661 * listInput.clear();
22662 * listInput.sendKeys('');
22664 * expect(names.getText()).toContain('');
22665 * expect(valid.getText()).toContain('false');
22666 * expect(error.getCssValue('display')).not.toBe('none');
22671 * ### Example - splitting on whitespace
22672 * <example name="ngList-directive-newlines">
22673 * <file name="index.html">
22674 * <textarea ng-model="list" ng-list=" " ng-trim="false"></textarea>
22675 * <pre>{{ list | json }}</pre>
22677 * <file name="protractor.js" type="protractor">
22678 * it("should split the text by newlines", function() {
22679 * var listInput = element(by.model('list'));
22680 * var output = element(by.binding('list | json'));
22681 * listInput.sendKeys('abc\ndef\nghi');
22682 * expect(output.getText()).toContain('[\n "abc",\n "def",\n "ghi"\n]');
22688 * @param {string=} ngList optional delimiter that should be used to split the value.
22690 var ngListDirective = function() {
22694 require: 'ngModel',
22695 link: function(scope, element, attr, ctrl) {
22696 // We want to control whitespace trimming so we use this convoluted approach
22697 // to access the ngList attribute, which doesn't pre-trim the attribute
22698 var ngList = element.attr(attr.$attr.ngList) || ', ';
22699 var trimValues = attr.ngTrim !== 'false';
22700 var separator = trimValues ? trim(ngList) : ngList;
22702 var parse = function(viewValue) {
22703 // If the viewValue is invalid (say required but empty) it will be `undefined`
22704 if (isUndefined(viewValue)) return;
22709 forEach(viewValue.split(separator), function(value) {
22710 if (value) list.push(trimValues ? trim(value) : value);
22717 ctrl.$parsers.push(parse);
22718 ctrl.$formatters.push(function(value) {
22719 if (isArray(value)) {
22720 return value.join(ngList);
22726 // Override the standard $isEmpty because an empty array means the input is empty.
22727 ctrl.$isEmpty = function(value) {
22728 return !value || !value.length;
22734 /* global VALID_CLASS: true,
22735 INVALID_CLASS: true,
22736 PRISTINE_CLASS: true,
22738 UNTOUCHED_CLASS: true,
22739 TOUCHED_CLASS: true,
22742 var VALID_CLASS = 'ng-valid',
22743 INVALID_CLASS = 'ng-invalid',
22744 PRISTINE_CLASS = 'ng-pristine',
22745 DIRTY_CLASS = 'ng-dirty',
22746 UNTOUCHED_CLASS = 'ng-untouched',
22747 TOUCHED_CLASS = 'ng-touched',
22748 PENDING_CLASS = 'ng-pending';
22750 var ngModelMinErr = minErr('ngModel');
22754 * @name ngModel.NgModelController
22756 * @property {string} $viewValue Actual string value in the view.
22757 * @property {*} $modelValue The value in the model that the control is bound to.
22758 * @property {Array.<Function>} $parsers Array of functions to execute, as a pipeline, whenever
22759 the control reads value from the DOM. The functions are called in array order, each passing
22760 its return value through to the next. The last return value is forwarded to the
22761 {@link ngModel.NgModelController#$validators `$validators`} collection.
22763 Parsers are used to sanitize / convert the {@link ngModel.NgModelController#$viewValue
22766 Returning `undefined` from a parser means a parse error occurred. In that case,
22767 no {@link ngModel.NgModelController#$validators `$validators`} will run and the `ngModel`
22768 will be set to `undefined` unless {@link ngModelOptions `ngModelOptions.allowInvalid`}
22769 is set to `true`. The parse error is stored in `ngModel.$error.parse`.
22772 * @property {Array.<Function>} $formatters Array of functions to execute, as a pipeline, whenever
22773 the model value changes. The functions are called in reverse array order, each passing the value through to the
22774 next. The last return value is used as the actual DOM value.
22775 Used to format / convert values for display in the control.
22777 * function formatter(value) {
22779 * return value.toUpperCase();
22782 * ngModel.$formatters.push(formatter);
22785 * @property {Object.<string, function>} $validators A collection of validators that are applied
22786 * whenever the model value changes. The key value within the object refers to the name of the
22787 * validator while the function refers to the validation operation. The validation operation is
22788 * provided with the model value as an argument and must return a true or false value depending
22789 * on the response of that validation.
22792 * ngModel.$validators.validCharacters = function(modelValue, viewValue) {
22793 * var value = modelValue || viewValue;
22794 * return /[0-9]+/.test(value) &&
22795 * /[a-z]+/.test(value) &&
22796 * /[A-Z]+/.test(value) &&
22797 * /\W+/.test(value);
22801 * @property {Object.<string, function>} $asyncValidators A collection of validations that are expected to
22802 * perform an asynchronous validation (e.g. a HTTP request). The validation function that is provided
22803 * is expected to return a promise when it is run during the model validation process. Once the promise
22804 * is delivered then the validation status will be set to true when fulfilled and false when rejected.
22805 * When the asynchronous validators are triggered, each of the validators will run in parallel and the model
22806 * value will only be updated once all validators have been fulfilled. As long as an asynchronous validator
22807 * is unfulfilled, its key will be added to the controllers `$pending` property. Also, all asynchronous validators
22808 * will only run once all synchronous validators have passed.
22810 * Please note that if $http is used then it is important that the server returns a success HTTP response code
22811 * in order to fulfill the validation and a status level of `4xx` in order to reject the validation.
22814 * ngModel.$asyncValidators.uniqueUsername = function(modelValue, viewValue) {
22815 * var value = modelValue || viewValue;
22817 * // Lookup user by username
22818 * return $http.get('/api/users/' + value).
22819 * then(function resolved() {
22820 * //username exists, this means validation fails
22821 * return $q.reject('exists');
22822 * }, function rejected() {
22823 * //username does not exist, therefore this validation passes
22829 * @property {Array.<Function>} $viewChangeListeners Array of functions to execute whenever the
22830 * view value has changed. It is called with no arguments, and its return value is ignored.
22831 * This can be used in place of additional $watches against the model value.
22833 * @property {Object} $error An object hash with all failing validator ids as keys.
22834 * @property {Object} $pending An object hash with all pending validator ids as keys.
22836 * @property {boolean} $untouched True if control has not lost focus yet.
22837 * @property {boolean} $touched True if control has lost focus.
22838 * @property {boolean} $pristine True if user has not interacted with the control yet.
22839 * @property {boolean} $dirty True if user has already interacted with the control.
22840 * @property {boolean} $valid True if there is no error.
22841 * @property {boolean} $invalid True if at least one error on the control.
22842 * @property {string} $name The name attribute of the control.
22846 * `NgModelController` provides API for the {@link ngModel `ngModel`} directive.
22847 * The controller contains services for data-binding, validation, CSS updates, and value formatting
22848 * and parsing. It purposefully does not contain any logic which deals with DOM rendering or
22849 * listening to DOM events.
22850 * Such DOM related logic should be provided by other directives which make use of
22851 * `NgModelController` for data-binding to control elements.
22852 * Angular provides this DOM logic for most {@link input `input`} elements.
22853 * At the end of this page you can find a {@link ngModel.NgModelController#custom-control-example
22854 * custom control example} that uses `ngModelController` to bind to `contenteditable` elements.
22857 * ### Custom Control Example
22858 * This example shows how to use `NgModelController` with a custom control to achieve
22859 * data-binding. Notice how different directives (`contenteditable`, `ng-model`, and `required`)
22860 * collaborate together to achieve the desired result.
22862 * `contenteditable` is an HTML5 attribute, which tells the browser to let the element
22863 * contents be edited in place by the user.
22865 * We are using the {@link ng.service:$sce $sce} service here and include the {@link ngSanitize $sanitize}
22866 * module to automatically remove "bad" content like inline event listener (e.g. `<span onclick="...">`).
22867 * However, as we are using `$sce` the model can still decide to provide unsafe content if it marks
22868 * that content using the `$sce` service.
22870 * <example name="NgModelController" module="customControl" deps="angular-sanitize.js">
22871 <file name="style.css">
22872 [contenteditable] {
22873 border: 1px solid black;
22874 background-color: white;
22879 border: 1px solid red;
22883 <file name="script.js">
22884 angular.module('customControl', ['ngSanitize']).
22885 directive('contenteditable', ['$sce', function($sce) {
22887 restrict: 'A', // only activate on element attribute
22888 require: '?ngModel', // get a hold of NgModelController
22889 link: function(scope, element, attrs, ngModel) {
22890 if (!ngModel) return; // do nothing if no ng-model
22892 // Specify how UI should be updated
22893 ngModel.$render = function() {
22894 element.html($sce.getTrustedHtml(ngModel.$viewValue || ''));
22897 // Listen for change events to enable binding
22898 element.on('blur keyup change', function() {
22899 scope.$evalAsync(read);
22901 read(); // initialize
22903 // Write data to the model
22905 var html = element.html();
22906 // When we clear the content editable the browser leaves a <br> behind
22907 // If strip-br attribute is provided then we strip this out
22908 if ( attrs.stripBr && html == '<br>' ) {
22911 ngModel.$setViewValue(html);
22917 <file name="index.html">
22918 <form name="myForm">
22919 <div contenteditable
22920 name="myWidget" ng-model="userContent"
22922 required>Change me!</div>
22923 <span ng-show="myForm.myWidget.$error.required">Required!</span>
22925 <textarea ng-model="userContent"></textarea>
22928 <file name="protractor.js" type="protractor">
22929 it('should data-bind and become invalid', function() {
22930 if (browser.params.browser == 'safari' || browser.params.browser == 'firefox') {
22931 // SafariDriver can't handle contenteditable
22932 // and Firefox driver can't clear contenteditables very well
22935 var contentEditable = element(by.css('[contenteditable]'));
22936 var content = 'Change me!';
22938 expect(contentEditable.getText()).toEqual(content);
22940 contentEditable.clear();
22941 contentEditable.sendKeys(protractor.Key.BACK_SPACE);
22942 expect(contentEditable.getText()).toEqual('');
22943 expect(contentEditable.getAttribute('class')).toMatch(/ng-invalid-required/);
22950 var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$parse', '$animate', '$timeout', '$rootScope', '$q', '$interpolate',
22951 function($scope, $exceptionHandler, $attr, $element, $parse, $animate, $timeout, $rootScope, $q, $interpolate) {
22952 this.$viewValue = Number.NaN;
22953 this.$modelValue = Number.NaN;
22954 this.$$rawModelValue = undefined; // stores the parsed modelValue / model set from scope regardless of validity.
22955 this.$validators = {};
22956 this.$asyncValidators = {};
22957 this.$parsers = [];
22958 this.$formatters = [];
22959 this.$viewChangeListeners = [];
22960 this.$untouched = true;
22961 this.$touched = false;
22962 this.$pristine = true;
22963 this.$dirty = false;
22964 this.$valid = true;
22965 this.$invalid = false;
22966 this.$error = {}; // keep invalid keys here
22967 this.$$success = {}; // keep valid keys here
22968 this.$pending = undefined; // keep pending keys here
22969 this.$name = $interpolate($attr.name || '', false)($scope);
22972 var parsedNgModel = $parse($attr.ngModel),
22973 parsedNgModelAssign = parsedNgModel.assign,
22974 ngModelGet = parsedNgModel,
22975 ngModelSet = parsedNgModelAssign,
22976 pendingDebounce = null,
22980 this.$$setOptions = function(options) {
22981 ctrl.$options = options;
22982 if (options && options.getterSetter) {
22983 var invokeModelGetter = $parse($attr.ngModel + '()'),
22984 invokeModelSetter = $parse($attr.ngModel + '($$$p)');
22986 ngModelGet = function($scope) {
22987 var modelValue = parsedNgModel($scope);
22988 if (isFunction(modelValue)) {
22989 modelValue = invokeModelGetter($scope);
22993 ngModelSet = function($scope, newValue) {
22994 if (isFunction(parsedNgModel($scope))) {
22995 invokeModelSetter($scope, {$$$p: ctrl.$modelValue});
22997 parsedNgModelAssign($scope, ctrl.$modelValue);
23000 } else if (!parsedNgModel.assign) {
23001 throw ngModelMinErr('nonassign', "Expression '{0}' is non-assignable. Element: {1}",
23002 $attr.ngModel, startingTag($element));
23008 * @name ngModel.NgModelController#$render
23011 * Called when the view needs to be updated. It is expected that the user of the ng-model
23012 * directive will implement this method.
23014 * The `$render()` method is invoked in the following situations:
23016 * * `$rollbackViewValue()` is called. If we are rolling back the view value to the last
23017 * committed value then `$render()` is called to update the input control.
23018 * * The value referenced by `ng-model` is changed programmatically and both the `$modelValue` and
23019 * the `$viewValue` are different to last time.
23021 * Since `ng-model` does not do a deep watch, `$render()` is only invoked if the values of
23022 * `$modelValue` and `$viewValue` are actually different to their previous value. If `$modelValue`
23023 * or `$viewValue` are objects (rather than a string or number) then `$render()` will not be
23024 * invoked if you only change a property on the objects.
23026 this.$render = noop;
23030 * @name ngModel.NgModelController#$isEmpty
23033 * This is called when we need to determine if the value of an input is empty.
23035 * For instance, the required directive does this to work out if the input has data or not.
23037 * The default `$isEmpty` function checks whether the value is `undefined`, `''`, `null` or `NaN`.
23039 * You can override this for input directives whose concept of being empty is different to the
23040 * default. The `checkboxInputType` directive does this because in its case a value of `false`
23043 * @param {*} value The value of the input to check for emptiness.
23044 * @returns {boolean} True if `value` is "empty".
23046 this.$isEmpty = function(value) {
23047 return isUndefined(value) || value === '' || value === null || value !== value;
23050 var parentForm = $element.inheritedData('$formController') || nullFormCtrl,
23051 currentValidationRunId = 0;
23055 * @name ngModel.NgModelController#$setValidity
23058 * Change the validity state, and notify the form.
23060 * This method can be called within $parsers/$formatters or a custom validation implementation.
23061 * However, in most cases it should be sufficient to use the `ngModel.$validators` and
23062 * `ngModel.$asyncValidators` collections which will call `$setValidity` automatically.
23064 * @param {string} validationErrorKey Name of the validator. The `validationErrorKey` will be assigned
23065 * to either `$error[validationErrorKey]` or `$pending[validationErrorKey]`
23066 * (for unfulfilled `$asyncValidators`), so that it is available for data-binding.
23067 * The `validationErrorKey` should be in camelCase and will get converted into dash-case
23068 * for class name. Example: `myError` will result in `ng-valid-my-error` and `ng-invalid-my-error`
23069 * class and can be bound to as `{{someForm.someControl.$error.myError}}` .
23070 * @param {boolean} isValid Whether the current state is valid (true), invalid (false), pending (undefined),
23071 * or skipped (null). Pending is used for unfulfilled `$asyncValidators`.
23072 * Skipped is used by Angular when validators do not run because of parse errors and
23073 * when `$asyncValidators` do not run because any of the `$validators` failed.
23075 addSetValidityMethod({
23077 $element: $element,
23078 set: function(object, property) {
23079 object[property] = true;
23081 unset: function(object, property) {
23082 delete object[property];
23084 parentForm: parentForm,
23090 * @name ngModel.NgModelController#$setPristine
23093 * Sets the control to its pristine state.
23095 * This method can be called to remove the `ng-dirty` class and set the control to its pristine
23096 * state (`ng-pristine` class). A model is considered to be pristine when the control
23097 * has not been changed from when first compiled.
23099 this.$setPristine = function() {
23100 ctrl.$dirty = false;
23101 ctrl.$pristine = true;
23102 $animate.removeClass($element, DIRTY_CLASS);
23103 $animate.addClass($element, PRISTINE_CLASS);
23108 * @name ngModel.NgModelController#$setDirty
23111 * Sets the control to its dirty state.
23113 * This method can be called to remove the `ng-pristine` class and set the control to its dirty
23114 * state (`ng-dirty` class). A model is considered to be dirty when the control has been changed
23115 * from when first compiled.
23117 this.$setDirty = function() {
23118 ctrl.$dirty = true;
23119 ctrl.$pristine = false;
23120 $animate.removeClass($element, PRISTINE_CLASS);
23121 $animate.addClass($element, DIRTY_CLASS);
23122 parentForm.$setDirty();
23127 * @name ngModel.NgModelController#$setUntouched
23130 * Sets the control to its untouched state.
23132 * This method can be called to remove the `ng-touched` class and set the control to its
23133 * untouched state (`ng-untouched` class). Upon compilation, a model is set as untouched
23134 * by default, however this function can be used to restore that state if the model has
23135 * already been touched by the user.
23137 this.$setUntouched = function() {
23138 ctrl.$touched = false;
23139 ctrl.$untouched = true;
23140 $animate.setClass($element, UNTOUCHED_CLASS, TOUCHED_CLASS);
23145 * @name ngModel.NgModelController#$setTouched
23148 * Sets the control to its touched state.
23150 * This method can be called to remove the `ng-untouched` class and set the control to its
23151 * touched state (`ng-touched` class). A model is considered to be touched when the user has
23152 * first focused the control element and then shifted focus away from the control (blur event).
23154 this.$setTouched = function() {
23155 ctrl.$touched = true;
23156 ctrl.$untouched = false;
23157 $animate.setClass($element, TOUCHED_CLASS, UNTOUCHED_CLASS);
23162 * @name ngModel.NgModelController#$rollbackViewValue
23165 * Cancel an update and reset the input element's value to prevent an update to the `$modelValue`,
23166 * which may be caused by a pending debounced event or because the input is waiting for a some
23169 * If you have an input that uses `ng-model-options` to set up debounced events or events such
23170 * as blur you can have a situation where there is a period when the `$viewValue`
23171 * is out of synch with the ngModel's `$modelValue`.
23173 * In this case, you can run into difficulties if you try to update the ngModel's `$modelValue`
23174 * programmatically before these debounced/future events have resolved/occurred, because Angular's
23175 * dirty checking mechanism is not able to tell whether the model has actually changed or not.
23177 * The `$rollbackViewValue()` method should be called before programmatically changing the model of an
23178 * input which may have such events pending. This is important in order to make sure that the
23179 * input field will be updated with the new model value and any pending operations are cancelled.
23181 * <example name="ng-model-cancel-update" module="cancel-update-example">
23182 * <file name="app.js">
23183 * angular.module('cancel-update-example', [])
23185 * .controller('CancelUpdateController', ['$scope', function($scope) {
23186 * $scope.resetWithCancel = function(e) {
23187 * if (e.keyCode == 27) {
23188 * $scope.myForm.myInput1.$rollbackViewValue();
23189 * $scope.myValue = '';
23192 * $scope.resetWithoutCancel = function(e) {
23193 * if (e.keyCode == 27) {
23194 * $scope.myValue = '';
23199 * <file name="index.html">
23200 * <div ng-controller="CancelUpdateController">
23201 * <p>Try typing something in each input. See that the model only updates when you
23202 * blur off the input.
23204 * <p>Now see what happens if you start typing then press the Escape key</p>
23206 * <form name="myForm" ng-model-options="{ updateOn: 'blur' }">
23207 * <p>With $rollbackViewValue()</p>
23208 * <input name="myInput1" ng-model="myValue" ng-keydown="resetWithCancel($event)"><br/>
23209 * myValue: "{{ myValue }}"
23211 * <p>Without $rollbackViewValue()</p>
23212 * <input name="myInput2" ng-model="myValue" ng-keydown="resetWithoutCancel($event)"><br/>
23213 * myValue: "{{ myValue }}"
23219 this.$rollbackViewValue = function() {
23220 $timeout.cancel(pendingDebounce);
23221 ctrl.$viewValue = ctrl.$$lastCommittedViewValue;
23227 * @name ngModel.NgModelController#$validate
23230 * Runs each of the registered validators (first synchronous validators and then
23231 * asynchronous validators).
23232 * If the validity changes to invalid, the model will be set to `undefined`,
23233 * unless {@link ngModelOptions `ngModelOptions.allowInvalid`} is `true`.
23234 * If the validity changes to valid, it will set the model to the last available valid
23235 * `$modelValue`, i.e. either the last parsed value or the last value set from the scope.
23237 this.$validate = function() {
23238 // ignore $validate before model is initialized
23239 if (isNumber(ctrl.$modelValue) && isNaN(ctrl.$modelValue)) {
23243 var viewValue = ctrl.$$lastCommittedViewValue;
23244 // Note: we use the $$rawModelValue as $modelValue might have been
23245 // set to undefined during a view -> model update that found validation
23246 // errors. We can't parse the view here, since that could change
23247 // the model although neither viewValue nor the model on the scope changed
23248 var modelValue = ctrl.$$rawModelValue;
23250 var prevValid = ctrl.$valid;
23251 var prevModelValue = ctrl.$modelValue;
23253 var allowInvalid = ctrl.$options && ctrl.$options.allowInvalid;
23255 ctrl.$$runValidators(modelValue, viewValue, function(allValid) {
23256 // If there was no change in validity, don't update the model
23257 // This prevents changing an invalid modelValue to undefined
23258 if (!allowInvalid && prevValid !== allValid) {
23259 // Note: Don't check ctrl.$valid here, as we could have
23260 // external validators (e.g. calculated on the server),
23261 // that just call $setValidity and need the model value
23262 // to calculate their validity.
23263 ctrl.$modelValue = allValid ? modelValue : undefined;
23265 if (ctrl.$modelValue !== prevModelValue) {
23266 ctrl.$$writeModelToScope();
23273 this.$$runValidators = function(modelValue, viewValue, doneCallback) {
23274 currentValidationRunId++;
23275 var localValidationRunId = currentValidationRunId;
23277 // check parser error
23278 if (!processParseErrors()) {
23279 validationDone(false);
23282 if (!processSyncValidators()) {
23283 validationDone(false);
23286 processAsyncValidators();
23288 function processParseErrors() {
23289 var errorKey = ctrl.$$parserName || 'parse';
23290 if (parserValid === undefined) {
23291 setValidity(errorKey, null);
23293 if (!parserValid) {
23294 forEach(ctrl.$validators, function(v, name) {
23295 setValidity(name, null);
23297 forEach(ctrl.$asyncValidators, function(v, name) {
23298 setValidity(name, null);
23301 // Set the parse error last, to prevent unsetting it, should a $validators key == parserName
23302 setValidity(errorKey, parserValid);
23303 return parserValid;
23308 function processSyncValidators() {
23309 var syncValidatorsValid = true;
23310 forEach(ctrl.$validators, function(validator, name) {
23311 var result = validator(modelValue, viewValue);
23312 syncValidatorsValid = syncValidatorsValid && result;
23313 setValidity(name, result);
23315 if (!syncValidatorsValid) {
23316 forEach(ctrl.$asyncValidators, function(v, name) {
23317 setValidity(name, null);
23324 function processAsyncValidators() {
23325 var validatorPromises = [];
23326 var allValid = true;
23327 forEach(ctrl.$asyncValidators, function(validator, name) {
23328 var promise = validator(modelValue, viewValue);
23329 if (!isPromiseLike(promise)) {
23330 throw ngModelMinErr("$asyncValidators",
23331 "Expected asynchronous validator to return a promise but got '{0}' instead.", promise);
23333 setValidity(name, undefined);
23334 validatorPromises.push(promise.then(function() {
23335 setValidity(name, true);
23336 }, function(error) {
23338 setValidity(name, false);
23341 if (!validatorPromises.length) {
23342 validationDone(true);
23344 $q.all(validatorPromises).then(function() {
23345 validationDone(allValid);
23350 function setValidity(name, isValid) {
23351 if (localValidationRunId === currentValidationRunId) {
23352 ctrl.$setValidity(name, isValid);
23356 function validationDone(allValid) {
23357 if (localValidationRunId === currentValidationRunId) {
23359 doneCallback(allValid);
23366 * @name ngModel.NgModelController#$commitViewValue
23369 * Commit a pending update to the `$modelValue`.
23371 * Updates may be pending by a debounced event or because the input is waiting for a some future
23372 * event defined in `ng-model-options`. this method is rarely needed as `NgModelController`
23373 * usually handles calling this in response to input events.
23375 this.$commitViewValue = function() {
23376 var viewValue = ctrl.$viewValue;
23378 $timeout.cancel(pendingDebounce);
23380 // If the view value has not changed then we should just exit, except in the case where there is
23381 // a native validator on the element. In this case the validation state may have changed even though
23382 // the viewValue has stayed empty.
23383 if (ctrl.$$lastCommittedViewValue === viewValue && (viewValue !== '' || !ctrl.$$hasNativeValidators)) {
23386 ctrl.$$lastCommittedViewValue = viewValue;
23389 if (ctrl.$pristine) {
23392 this.$$parseAndValidate();
23395 this.$$parseAndValidate = function() {
23396 var viewValue = ctrl.$$lastCommittedViewValue;
23397 var modelValue = viewValue;
23398 parserValid = isUndefined(modelValue) ? undefined : true;
23401 for (var i = 0; i < ctrl.$parsers.length; i++) {
23402 modelValue = ctrl.$parsers[i](modelValue);
23403 if (isUndefined(modelValue)) {
23404 parserValid = false;
23409 if (isNumber(ctrl.$modelValue) && isNaN(ctrl.$modelValue)) {
23410 // ctrl.$modelValue has not been touched yet...
23411 ctrl.$modelValue = ngModelGet($scope);
23413 var prevModelValue = ctrl.$modelValue;
23414 var allowInvalid = ctrl.$options && ctrl.$options.allowInvalid;
23415 ctrl.$$rawModelValue = modelValue;
23417 if (allowInvalid) {
23418 ctrl.$modelValue = modelValue;
23419 writeToModelIfNeeded();
23422 // Pass the $$lastCommittedViewValue here, because the cached viewValue might be out of date.
23423 // This can happen if e.g. $setViewValue is called from inside a parser
23424 ctrl.$$runValidators(modelValue, ctrl.$$lastCommittedViewValue, function(allValid) {
23425 if (!allowInvalid) {
23426 // Note: Don't check ctrl.$valid here, as we could have
23427 // external validators (e.g. calculated on the server),
23428 // that just call $setValidity and need the model value
23429 // to calculate their validity.
23430 ctrl.$modelValue = allValid ? modelValue : undefined;
23431 writeToModelIfNeeded();
23435 function writeToModelIfNeeded() {
23436 if (ctrl.$modelValue !== prevModelValue) {
23437 ctrl.$$writeModelToScope();
23442 this.$$writeModelToScope = function() {
23443 ngModelSet($scope, ctrl.$modelValue);
23444 forEach(ctrl.$viewChangeListeners, function(listener) {
23448 $exceptionHandler(e);
23455 * @name ngModel.NgModelController#$setViewValue
23458 * Update the view value.
23460 * This method should be called when an input directive want to change the view value; typically,
23461 * this is done from within a DOM event handler.
23463 * For example {@link ng.directive:input input} calls it when the value of the input changes and
23464 * {@link ng.directive:select select} calls it when an option is selected.
23466 * If the new `value` is an object (rather than a string or a number), we should make a copy of the
23467 * object before passing it to `$setViewValue`. This is because `ngModel` does not perform a deep
23468 * watch of objects, it only looks for a change of identity. If you only change the property of
23469 * the object then ngModel will not realise that the object has changed and will not invoke the
23470 * `$parsers` and `$validators` pipelines.
23472 * For this reason, you should not change properties of the copy once it has been passed to
23473 * `$setViewValue`. Otherwise you may cause the model value on the scope to change incorrectly.
23475 * When this method is called, the new `value` will be staged for committing through the `$parsers`
23476 * and `$validators` pipelines. If there are no special {@link ngModelOptions} specified then the staged
23477 * value sent directly for processing, finally to be applied to `$modelValue` and then the
23478 * **expression** specified in the `ng-model` attribute.
23480 * Lastly, all the registered change listeners, in the `$viewChangeListeners` list, are called.
23482 * In case the {@link ng.directive:ngModelOptions ngModelOptions} directive is used with `updateOn`
23483 * and the `default` trigger is not listed, all those actions will remain pending until one of the
23484 * `updateOn` events is triggered on the DOM element.
23485 * All these actions will be debounced if the {@link ng.directive:ngModelOptions ngModelOptions}
23486 * directive is used with a custom debounce for this particular event.
23488 * Note that calling this function does not trigger a `$digest`.
23490 * @param {string} value Value from the view.
23491 * @param {string} trigger Event that triggered the update.
23493 this.$setViewValue = function(value, trigger) {
23494 ctrl.$viewValue = value;
23495 if (!ctrl.$options || ctrl.$options.updateOnDefault) {
23496 ctrl.$$debounceViewValueCommit(trigger);
23500 this.$$debounceViewValueCommit = function(trigger) {
23501 var debounceDelay = 0,
23502 options = ctrl.$options,
23505 if (options && isDefined(options.debounce)) {
23506 debounce = options.debounce;
23507 if (isNumber(debounce)) {
23508 debounceDelay = debounce;
23509 } else if (isNumber(debounce[trigger])) {
23510 debounceDelay = debounce[trigger];
23511 } else if (isNumber(debounce['default'])) {
23512 debounceDelay = debounce['default'];
23516 $timeout.cancel(pendingDebounce);
23517 if (debounceDelay) {
23518 pendingDebounce = $timeout(function() {
23519 ctrl.$commitViewValue();
23521 } else if ($rootScope.$$phase) {
23522 ctrl.$commitViewValue();
23524 $scope.$apply(function() {
23525 ctrl.$commitViewValue();
23531 // Note: we cannot use a normal scope.$watch as we want to detect the following:
23532 // 1. scope value is 'a'
23533 // 2. user enters 'b'
23534 // 3. ng-change kicks in and reverts scope value to 'a'
23535 // -> scope value did not change since the last digest as
23536 // ng-change executes in apply phase
23537 // 4. view should be changed back to 'a'
23538 $scope.$watch(function ngModelWatch() {
23539 var modelValue = ngModelGet($scope);
23541 // if scope model value and ngModel value are out of sync
23542 // TODO(perf): why not move this to the action fn?
23543 if (modelValue !== ctrl.$modelValue &&
23544 // checks for NaN is needed to allow setting the model to NaN when there's an asyncValidator
23545 (ctrl.$modelValue === ctrl.$modelValue || modelValue === modelValue)
23547 ctrl.$modelValue = ctrl.$$rawModelValue = modelValue;
23548 parserValid = undefined;
23550 var formatters = ctrl.$formatters,
23551 idx = formatters.length;
23553 var viewValue = modelValue;
23555 viewValue = formatters[idx](viewValue);
23557 if (ctrl.$viewValue !== viewValue) {
23558 ctrl.$viewValue = ctrl.$$lastCommittedViewValue = viewValue;
23561 ctrl.$$runValidators(modelValue, viewValue, noop);
23578 * The `ngModel` directive binds an `input`,`select`, `textarea` (or custom form control) to a
23579 * property on the scope using {@link ngModel.NgModelController NgModelController},
23580 * which is created and exposed by this directive.
23582 * `ngModel` is responsible for:
23584 * - Binding the view into the model, which other directives such as `input`, `textarea` or `select`
23586 * - Providing validation behavior (i.e. required, number, email, url).
23587 * - Keeping the state of the control (valid/invalid, dirty/pristine, touched/untouched, validation errors).
23588 * - Setting related css classes on the element (`ng-valid`, `ng-invalid`, `ng-dirty`, `ng-pristine`, `ng-touched`, `ng-untouched`) including animations.
23589 * - Registering the control with its parent {@link ng.directive:form form}.
23591 * Note: `ngModel` will try to bind to the property given by evaluating the expression on the
23592 * current scope. If the property doesn't already exist on this scope, it will be created
23593 * implicitly and added to the scope.
23595 * For best practices on using `ngModel`, see:
23597 * - [Understanding Scopes](https://github.com/angular/angular.js/wiki/Understanding-Scopes)
23599 * For basic examples, how to use `ngModel`, see:
23601 * - {@link ng.directive:input input}
23602 * - {@link input[text] text}
23603 * - {@link input[checkbox] checkbox}
23604 * - {@link input[radio] radio}
23605 * - {@link input[number] number}
23606 * - {@link input[email] email}
23607 * - {@link input[url] url}
23608 * - {@link input[date] date}
23609 * - {@link input[datetime-local] datetime-local}
23610 * - {@link input[time] time}
23611 * - {@link input[month] month}
23612 * - {@link input[week] week}
23613 * - {@link ng.directive:select select}
23614 * - {@link ng.directive:textarea textarea}
23617 * The following CSS classes are added and removed on the associated input/select/textarea element
23618 * depending on the validity of the model.
23620 * - `ng-valid`: the model is valid
23621 * - `ng-invalid`: the model is invalid
23622 * - `ng-valid-[key]`: for each valid key added by `$setValidity`
23623 * - `ng-invalid-[key]`: for each invalid key added by `$setValidity`
23624 * - `ng-pristine`: the control hasn't been interacted with yet
23625 * - `ng-dirty`: the control has been interacted with
23626 * - `ng-touched`: the control has been blurred
23627 * - `ng-untouched`: the control hasn't been blurred
23628 * - `ng-pending`: any `$asyncValidators` are unfulfilled
23630 * Keep in mind that ngAnimate can detect each of these classes when added and removed.
23632 * ## Animation Hooks
23634 * Animations within models are triggered when any of the associated CSS classes are added and removed
23635 * on the input element which is attached to the model. These classes are: `.ng-pristine`, `.ng-dirty`,
23636 * `.ng-invalid` and `.ng-valid` as well as any other validations that are performed on the model itself.
23637 * The animations that are triggered within ngModel are similar to how they work in ngClass and
23638 * animations can be hooked into using CSS transitions, keyframes as well as JS animations.
23640 * The following example shows a simple way to utilize CSS transitions to style an input element
23641 * that has been rendered as invalid after it has been validated:
23644 * //be sure to include ngAnimate as a module to hook into more
23645 * //advanced animations
23647 * transition:0.5s linear all;
23648 * background: white;
23650 * .my-input.ng-invalid {
23657 * <example deps="angular-animate.js" animations="true" fixBase="true" module="inputExample">
23658 <file name="index.html">
23660 angular.module('inputExample', [])
23661 .controller('ExampleController', ['$scope', function($scope) {
23667 -webkit-transition:all linear 0.5s;
23668 transition:all linear 0.5s;
23669 background: transparent;
23671 .my-input.ng-invalid {
23676 Update input to see transitions when valid/invalid.
23677 Integer is a valid value.
23678 <form name="testForm" ng-controller="ExampleController">
23679 <input ng-model="val" ng-pattern="/^\d+$/" name="anim" class="my-input" />
23684 * ## Binding to a getter/setter
23686 * Sometimes it's helpful to bind `ngModel` to a getter/setter function. A getter/setter is a
23687 * function that returns a representation of the model when called with zero arguments, and sets
23688 * the internal state of a model when called with an argument. It's sometimes useful to use this
23689 * for models that have an internal representation that's different than what the model exposes
23692 * <div class="alert alert-success">
23693 * **Best Practice:** It's best to keep getters fast because Angular is likely to call them more
23694 * frequently than other parts of your code.
23697 * You use this behavior by adding `ng-model-options="{ getterSetter: true }"` to an element that
23698 * has `ng-model` attached to it. You can also add `ng-model-options="{ getterSetter: true }"` to
23699 * a `<form>`, which will enable this behavior for all `<input>`s within it. See
23700 * {@link ng.directive:ngModelOptions `ngModelOptions`} for more.
23702 * The following example shows how to use `ngModel` with a getter/setter:
23705 * <example name="ngModel-getter-setter" module="getterSetterExample">
23706 <file name="index.html">
23707 <div ng-controller="ExampleController">
23708 <form name="userForm">
23710 <input type="text" name="userName"
23711 ng-model="user.name"
23712 ng-model-options="{ getterSetter: true }" />
23714 <pre>user.name = <span ng-bind="user.name()"></span></pre>
23717 <file name="app.js">
23718 angular.module('getterSetterExample', [])
23719 .controller('ExampleController', ['$scope', function($scope) {
23720 var _name = 'Brian';
23722 name: function(newName) {
23723 // Note that newName can be undefined for two reasons:
23724 // 1. Because it is called as a getter and thus called with no arguments
23725 // 2. Because the property should actually be set to undefined. This happens e.g. if the
23726 // input is invalid
23727 return arguments.length ? (_name = newName) : _name;
23734 var ngModelDirective = ['$rootScope', function($rootScope) {
23737 require: ['ngModel', '^?form', '^?ngModelOptions'],
23738 controller: NgModelController,
23739 // Prelink needs to run before any input directive
23740 // so that we can set the NgModelOptions in NgModelController
23741 // before anyone else uses it.
23743 compile: function ngModelCompile(element) {
23744 // Setup initial state of the control
23745 element.addClass(PRISTINE_CLASS).addClass(UNTOUCHED_CLASS).addClass(VALID_CLASS);
23748 pre: function ngModelPreLink(scope, element, attr, ctrls) {
23749 var modelCtrl = ctrls[0],
23750 formCtrl = ctrls[1] || nullFormCtrl;
23752 modelCtrl.$$setOptions(ctrls[2] && ctrls[2].$options);
23754 // notify others, especially parent forms
23755 formCtrl.$addControl(modelCtrl);
23757 attr.$observe('name', function(newValue) {
23758 if (modelCtrl.$name !== newValue) {
23759 formCtrl.$$renameControl(modelCtrl, newValue);
23763 scope.$on('$destroy', function() {
23764 formCtrl.$removeControl(modelCtrl);
23767 post: function ngModelPostLink(scope, element, attr, ctrls) {
23768 var modelCtrl = ctrls[0];
23769 if (modelCtrl.$options && modelCtrl.$options.updateOn) {
23770 element.on(modelCtrl.$options.updateOn, function(ev) {
23771 modelCtrl.$$debounceViewValueCommit(ev && ev.type);
23775 element.on('blur', function(ev) {
23776 if (modelCtrl.$touched) return;
23778 if ($rootScope.$$phase) {
23779 scope.$evalAsync(modelCtrl.$setTouched);
23781 scope.$apply(modelCtrl.$setTouched);
23790 var DEFAULT_REGEXP = /(\s+|^)default(\s+|$)/;
23794 * @name ngModelOptions
23797 * Allows tuning how model updates are done. Using `ngModelOptions` you can specify a custom list of
23798 * events that will trigger a model update and/or a debouncing delay so that the actual update only
23799 * takes place when a timer expires; this timer will be reset after another change takes place.
23801 * Given the nature of `ngModelOptions`, the value displayed inside input fields in the view might
23802 * be different than the value in the actual model. This means that if you update the model you
23803 * should also invoke {@link ngModel.NgModelController `$rollbackViewValue`} on the relevant input field in
23804 * order to make sure it is synchronized with the model and that any debounced action is canceled.
23806 * The easiest way to reference the control's {@link ngModel.NgModelController `$rollbackViewValue`}
23807 * method is by making sure the input is placed inside a form that has a `name` attribute. This is
23808 * important because `form` controllers are published to the related scope under the name in their
23809 * `name` attribute.
23811 * Any pending changes will take place immediately when an enclosing form is submitted via the
23812 * `submit` event. Note that `ngClick` events will occur before the model is updated. Use `ngSubmit`
23813 * to have access to the updated model.
23815 * `ngModelOptions` has an effect on the element it's declared on and its descendants.
23817 * @param {Object} ngModelOptions options to apply to the current model. Valid keys are:
23818 * - `updateOn`: string specifying which event should the input be bound to. You can set several
23819 * events using an space delimited list. There is a special event called `default` that
23820 * matches the default events belonging of the control.
23821 * - `debounce`: integer value which contains the debounce model update value in milliseconds. A
23822 * value of 0 triggers an immediate update. If an object is supplied instead, you can specify a
23823 * custom value for each event. For example:
23824 * `ng-model-options="{ updateOn: 'default blur', debounce: {'default': 500, 'blur': 0} }"`
23825 * - `allowInvalid`: boolean value which indicates that the model can be set with values that did
23826 * not validate correctly instead of the default behavior of setting the model to undefined.
23827 * - `getterSetter`: boolean value which determines whether or not to treat functions bound to
23828 `ngModel` as getters/setters.
23829 * - `timezone`: Defines the timezone to be used to read/write the `Date` instance in the model for
23830 * `<input type="date">`, `<input type="time">`, ... . Right now, the only supported value is `'UTC'`,
23831 * otherwise the default timezone of the browser will be used.
23835 The following example shows how to override immediate updates. Changes on the inputs within the
23836 form will update the model only when the control loses focus (blur event). If `escape` key is
23837 pressed while the input field is focused, the value is reset to the value in the current model.
23839 <example name="ngModelOptions-directive-blur" module="optionsExample">
23840 <file name="index.html">
23841 <div ng-controller="ExampleController">
23842 <form name="userForm">
23844 <input type="text" name="userName"
23845 ng-model="user.name"
23846 ng-model-options="{ updateOn: 'blur' }"
23847 ng-keyup="cancel($event)" /><br />
23850 <input type="text" ng-model="user.data" /><br />
23852 <pre>user.name = <span ng-bind="user.name"></span></pre>
23855 <file name="app.js">
23856 angular.module('optionsExample', [])
23857 .controller('ExampleController', ['$scope', function($scope) {
23858 $scope.user = { name: 'say', data: '' };
23860 $scope.cancel = function(e) {
23861 if (e.keyCode == 27) {
23862 $scope.userForm.userName.$rollbackViewValue();
23867 <file name="protractor.js" type="protractor">
23868 var model = element(by.binding('user.name'));
23869 var input = element(by.model('user.name'));
23870 var other = element(by.model('user.data'));
23872 it('should allow custom events', function() {
23873 input.sendKeys(' hello');
23875 expect(model.getText()).toEqual('say');
23877 expect(model.getText()).toEqual('say hello');
23880 it('should $rollbackViewValue when model changes', function() {
23881 input.sendKeys(' hello');
23882 expect(input.getAttribute('value')).toEqual('say hello');
23883 input.sendKeys(protractor.Key.ESCAPE);
23884 expect(input.getAttribute('value')).toEqual('say');
23886 expect(model.getText()).toEqual('say');
23891 This one shows how to debounce model changes. Model will be updated only 1 sec after last change.
23892 If the `Clear` button is pressed, any debounced action is canceled and the value becomes empty.
23894 <example name="ngModelOptions-directive-debounce" module="optionsExample">
23895 <file name="index.html">
23896 <div ng-controller="ExampleController">
23897 <form name="userForm">
23899 <input type="text" name="userName"
23900 ng-model="user.name"
23901 ng-model-options="{ debounce: 1000 }" />
23902 <button ng-click="userForm.userName.$rollbackViewValue(); user.name=''">Clear</button><br />
23904 <pre>user.name = <span ng-bind="user.name"></span></pre>
23907 <file name="app.js">
23908 angular.module('optionsExample', [])
23909 .controller('ExampleController', ['$scope', function($scope) {
23910 $scope.user = { name: 'say' };
23915 This one shows how to bind to getter/setters:
23917 <example name="ngModelOptions-directive-getter-setter" module="getterSetterExample">
23918 <file name="index.html">
23919 <div ng-controller="ExampleController">
23920 <form name="userForm">
23922 <input type="text" name="userName"
23923 ng-model="user.name"
23924 ng-model-options="{ getterSetter: true }" />
23926 <pre>user.name = <span ng-bind="user.name()"></span></pre>
23929 <file name="app.js">
23930 angular.module('getterSetterExample', [])
23931 .controller('ExampleController', ['$scope', function($scope) {
23932 var _name = 'Brian';
23934 name: function(newName) {
23935 // Note that newName can be undefined for two reasons:
23936 // 1. Because it is called as a getter and thus called with no arguments
23937 // 2. Because the property should actually be set to undefined. This happens e.g. if the
23938 // input is invalid
23939 return arguments.length ? (_name = newName) : _name;
23946 var ngModelOptionsDirective = function() {
23949 controller: ['$scope', '$attrs', function($scope, $attrs) {
23951 this.$options = $scope.$eval($attrs.ngModelOptions);
23952 // Allow adding/overriding bound events
23953 if (this.$options.updateOn !== undefined) {
23954 this.$options.updateOnDefault = false;
23955 // extract "default" pseudo-event from list of events that can trigger a model update
23956 this.$options.updateOn = trim(this.$options.updateOn.replace(DEFAULT_REGEXP, function() {
23957 that.$options.updateOnDefault = true;
23961 this.$options.updateOnDefault = true;
23970 function addSetValidityMethod(context) {
23971 var ctrl = context.ctrl,
23972 $element = context.$element,
23975 unset = context.unset,
23976 parentForm = context.parentForm,
23977 $animate = context.$animate;
23979 classCache[INVALID_CLASS] = !(classCache[VALID_CLASS] = $element.hasClass(VALID_CLASS));
23981 ctrl.$setValidity = setValidity;
23983 function setValidity(validationErrorKey, state, controller) {
23984 if (state === undefined) {
23985 createAndSet('$pending', validationErrorKey, controller);
23987 unsetAndCleanup('$pending', validationErrorKey, controller);
23989 if (!isBoolean(state)) {
23990 unset(ctrl.$error, validationErrorKey, controller);
23991 unset(ctrl.$$success, validationErrorKey, controller);
23994 unset(ctrl.$error, validationErrorKey, controller);
23995 set(ctrl.$$success, validationErrorKey, controller);
23997 set(ctrl.$error, validationErrorKey, controller);
23998 unset(ctrl.$$success, validationErrorKey, controller);
24001 if (ctrl.$pending) {
24002 cachedToggleClass(PENDING_CLASS, true);
24003 ctrl.$valid = ctrl.$invalid = undefined;
24004 toggleValidationCss('', null);
24006 cachedToggleClass(PENDING_CLASS, false);
24007 ctrl.$valid = isObjectEmpty(ctrl.$error);
24008 ctrl.$invalid = !ctrl.$valid;
24009 toggleValidationCss('', ctrl.$valid);
24012 // re-read the state as the set/unset methods could have
24013 // combined state in ctrl.$error[validationError] (used for forms),
24014 // where setting/unsetting only increments/decrements the value,
24015 // and does not replace it.
24017 if (ctrl.$pending && ctrl.$pending[validationErrorKey]) {
24018 combinedState = undefined;
24019 } else if (ctrl.$error[validationErrorKey]) {
24020 combinedState = false;
24021 } else if (ctrl.$$success[validationErrorKey]) {
24022 combinedState = true;
24024 combinedState = null;
24027 toggleValidationCss(validationErrorKey, combinedState);
24028 parentForm.$setValidity(validationErrorKey, combinedState, ctrl);
24031 function createAndSet(name, value, controller) {
24035 set(ctrl[name], value, controller);
24038 function unsetAndCleanup(name, value, controller) {
24040 unset(ctrl[name], value, controller);
24042 if (isObjectEmpty(ctrl[name])) {
24043 ctrl[name] = undefined;
24047 function cachedToggleClass(className, switchValue) {
24048 if (switchValue && !classCache[className]) {
24049 $animate.addClass($element, className);
24050 classCache[className] = true;
24051 } else if (!switchValue && classCache[className]) {
24052 $animate.removeClass($element, className);
24053 classCache[className] = false;
24057 function toggleValidationCss(validationErrorKey, isValid) {
24058 validationErrorKey = validationErrorKey ? '-' + snake_case(validationErrorKey, '-') : '';
24060 cachedToggleClass(VALID_CLASS + validationErrorKey, isValid === true);
24061 cachedToggleClass(INVALID_CLASS + validationErrorKey, isValid === false);
24065 function isObjectEmpty(obj) {
24067 for (var prop in obj) {
24076 * @name ngNonBindable
24081 * The `ngNonBindable` directive tells Angular not to compile or bind the contents of the current
24082 * DOM element. This is useful if the element contains what appears to be Angular directives and
24083 * bindings but which should be ignored by Angular. This could be the case if you have a site that
24084 * displays snippets of code, for instance.
24089 * In this example there are two locations where a simple interpolation binding (`{{}}`) is present,
24090 * but the one wrapped in `ngNonBindable` is left alone.
24094 <file name="index.html">
24095 <div>Normal: {{1 + 2}}</div>
24096 <div ng-non-bindable>Ignored: {{1 + 2}}</div>
24098 <file name="protractor.js" type="protractor">
24099 it('should check ng-non-bindable', function() {
24100 expect(element(by.binding('1 + 2')).getText()).toContain('3');
24101 expect(element.all(by.css('div')).last().getText()).toMatch(/1 \+ 2/);
24106 var ngNonBindableDirective = ngDirective({ terminal: true, priority: 1000 });
24110 * @name ngPluralize
24114 * `ngPluralize` is a directive that displays messages according to en-US localization rules.
24115 * These rules are bundled with angular.js, but can be overridden
24116 * (see {@link guide/i18n Angular i18n} dev guide). You configure ngPluralize directive
24117 * by specifying the mappings between
24118 * [plural categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html)
24119 * and the strings to be displayed.
24121 * # Plural categories and explicit number rules
24123 * [plural categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html)
24124 * in Angular's default en-US locale: "one" and "other".
24126 * While a plural category may match many numbers (for example, in en-US locale, "other" can match
24127 * any number that is not 1), an explicit number rule can only match one number. For example, the
24128 * explicit number rule for "3" matches the number 3. There are examples of plural categories
24129 * and explicit number rules throughout the rest of this documentation.
24131 * # Configuring ngPluralize
24132 * You configure ngPluralize by providing 2 attributes: `count` and `when`.
24133 * You can also provide an optional attribute, `offset`.
24135 * The value of the `count` attribute can be either a string or an {@link guide/expression
24136 * Angular expression}; these are evaluated on the current scope for its bound value.
24138 * The `when` attribute specifies the mappings between plural categories and the actual
24139 * string to be displayed. The value of the attribute should be a JSON object.
24141 * The following example shows how to configure ngPluralize:
24144 * <ng-pluralize count="personCount"
24145 when="{'0': 'Nobody is viewing.',
24146 * 'one': '1 person is viewing.',
24147 * 'other': '{} people are viewing.'}">
24151 * In the example, `"0: Nobody is viewing."` is an explicit number rule. If you did not
24152 * specify this rule, 0 would be matched to the "other" category and "0 people are viewing"
24153 * would be shown instead of "Nobody is viewing". You can specify an explicit number rule for
24154 * other numbers, for example 12, so that instead of showing "12 people are viewing", you can
24155 * show "a dozen people are viewing".
24157 * You can use a set of closed braces (`{}`) as a placeholder for the number that you want substituted
24158 * into pluralized strings. In the previous example, Angular will replace `{}` with
24159 * <span ng-non-bindable>`{{personCount}}`</span>. The closed braces `{}` is a placeholder
24160 * for <span ng-non-bindable>{{numberExpression}}</span>.
24162 * # Configuring ngPluralize with offset
24163 * The `offset` attribute allows further customization of pluralized text, which can result in
24164 * a better user experience. For example, instead of the message "4 people are viewing this document",
24165 * you might display "John, Kate and 2 others are viewing this document".
24166 * The offset attribute allows you to offset a number by any desired value.
24167 * Let's take a look at an example:
24170 * <ng-pluralize count="personCount" offset=2
24171 * when="{'0': 'Nobody is viewing.',
24172 * '1': '{{person1}} is viewing.',
24173 * '2': '{{person1}} and {{person2}} are viewing.',
24174 * 'one': '{{person1}}, {{person2}} and one other person are viewing.',
24175 * 'other': '{{person1}}, {{person2}} and {} other people are viewing.'}">
24179 * Notice that we are still using two plural categories(one, other), but we added
24180 * three explicit number rules 0, 1 and 2.
24181 * When one person, perhaps John, views the document, "John is viewing" will be shown.
24182 * When three people view the document, no explicit number rule is found, so
24183 * an offset of 2 is taken off 3, and Angular uses 1 to decide the plural category.
24184 * In this case, plural category 'one' is matched and "John, Mary and one other person are viewing"
24187 * Note that when you specify offsets, you must provide explicit number rules for
24188 * numbers from 0 up to and including the offset. If you use an offset of 3, for example,
24189 * you must provide explicit number rules for 0, 1, 2 and 3. You must also provide plural strings for
24190 * plural categories "one" and "other".
24192 * @param {string|expression} count The variable to be bound to.
24193 * @param {string} when The mapping between plural category to its corresponding strings.
24194 * @param {number=} offset Offset to deduct from the total number.
24197 <example module="pluralizeExample">
24198 <file name="index.html">
24200 angular.module('pluralizeExample', [])
24201 .controller('ExampleController', ['$scope', function($scope) {
24202 $scope.person1 = 'Igor';
24203 $scope.person2 = 'Misko';
24204 $scope.personCount = 1;
24207 <div ng-controller="ExampleController">
24208 Person 1:<input type="text" ng-model="person1" value="Igor" /><br/>
24209 Person 2:<input type="text" ng-model="person2" value="Misko" /><br/>
24210 Number of People:<input type="text" ng-model="personCount" value="1" /><br/>
24212 <!--- Example with simple pluralization rules for en locale --->
24214 <ng-pluralize count="personCount"
24215 when="{'0': 'Nobody is viewing.',
24216 'one': '1 person is viewing.',
24217 'other': '{} people are viewing.'}">
24218 </ng-pluralize><br>
24220 <!--- Example with offset --->
24222 <ng-pluralize count="personCount" offset=2
24223 when="{'0': 'Nobody is viewing.',
24224 '1': '{{person1}} is viewing.',
24225 '2': '{{person1}} and {{person2}} are viewing.',
24226 'one': '{{person1}}, {{person2}} and one other person are viewing.',
24227 'other': '{{person1}}, {{person2}} and {} other people are viewing.'}">
24231 <file name="protractor.js" type="protractor">
24232 it('should show correct pluralized string', function() {
24233 var withoutOffset = element.all(by.css('ng-pluralize')).get(0);
24234 var withOffset = element.all(by.css('ng-pluralize')).get(1);
24235 var countInput = element(by.model('personCount'));
24237 expect(withoutOffset.getText()).toEqual('1 person is viewing.');
24238 expect(withOffset.getText()).toEqual('Igor is viewing.');
24240 countInput.clear();
24241 countInput.sendKeys('0');
24243 expect(withoutOffset.getText()).toEqual('Nobody is viewing.');
24244 expect(withOffset.getText()).toEqual('Nobody is viewing.');
24246 countInput.clear();
24247 countInput.sendKeys('2');
24249 expect(withoutOffset.getText()).toEqual('2 people are viewing.');
24250 expect(withOffset.getText()).toEqual('Igor and Misko are viewing.');
24252 countInput.clear();
24253 countInput.sendKeys('3');
24255 expect(withoutOffset.getText()).toEqual('3 people are viewing.');
24256 expect(withOffset.getText()).toEqual('Igor, Misko and one other person are viewing.');
24258 countInput.clear();
24259 countInput.sendKeys('4');
24261 expect(withoutOffset.getText()).toEqual('4 people are viewing.');
24262 expect(withOffset.getText()).toEqual('Igor, Misko and 2 other people are viewing.');
24264 it('should show data-bound names', function() {
24265 var withOffset = element.all(by.css('ng-pluralize')).get(1);
24266 var personCount = element(by.model('personCount'));
24267 var person1 = element(by.model('person1'));
24268 var person2 = element(by.model('person2'));
24269 personCount.clear();
24270 personCount.sendKeys('4');
24272 person1.sendKeys('Di');
24274 person2.sendKeys('Vojta');
24275 expect(withOffset.getText()).toEqual('Di, Vojta and 2 other people are viewing.');
24280 var ngPluralizeDirective = ['$locale', '$interpolate', function($locale, $interpolate) {
24282 IS_WHEN = /^when(Minus)?(.+)$/;
24286 link: function(scope, element, attr) {
24287 var numberExp = attr.count,
24288 whenExp = attr.$attr.when && element.attr(attr.$attr.when), // we have {{}} in attrs
24289 offset = attr.offset || 0,
24290 whens = scope.$eval(whenExp) || {},
24292 startSymbol = $interpolate.startSymbol(),
24293 endSymbol = $interpolate.endSymbol(),
24294 braceReplacement = startSymbol + numberExp + '-' + offset + endSymbol,
24295 watchRemover = angular.noop,
24298 forEach(attr, function(expression, attributeName) {
24299 var tmpMatch = IS_WHEN.exec(attributeName);
24301 var whenKey = (tmpMatch[1] ? '-' : '') + lowercase(tmpMatch[2]);
24302 whens[whenKey] = element.attr(attr.$attr[attributeName]);
24305 forEach(whens, function(expression, key) {
24306 whensExpFns[key] = $interpolate(expression.replace(BRACE, braceReplacement));
24310 scope.$watch(numberExp, function ngPluralizeWatchAction(newVal) {
24311 var count = parseFloat(newVal);
24312 var countIsNaN = isNaN(count);
24314 if (!countIsNaN && !(count in whens)) {
24315 // If an explicit number rule such as 1, 2, 3... is defined, just use it.
24316 // Otherwise, check it against pluralization rules in $locale service.
24317 count = $locale.pluralCat(count - offset);
24320 // If both `count` and `lastCount` are NaN, we don't need to re-register a watch.
24321 // In JS `NaN !== NaN`, so we have to exlicitly check.
24322 if ((count !== lastCount) && !(countIsNaN && isNaN(lastCount))) {
24324 watchRemover = scope.$watch(whensExpFns[count], updateElementText);
24329 function updateElementText(newText) {
24330 element.text(newText || '');
24341 * The `ngRepeat` directive instantiates a template once per item from a collection. Each template
24342 * instance gets its own scope, where the given loop variable is set to the current collection item,
24343 * and `$index` is set to the item index or key.
24345 * Special properties are exposed on the local scope of each template instance, including:
24347 * | Variable | Type | Details |
24348 * |-----------|-----------------|-----------------------------------------------------------------------------|
24349 * | `$index` | {@type number} | iterator offset of the repeated element (0..length-1) |
24350 * | `$first` | {@type boolean} | true if the repeated element is first in the iterator. |
24351 * | `$middle` | {@type boolean} | true if the repeated element is between the first and last in the iterator. |
24352 * | `$last` | {@type boolean} | true if the repeated element is last in the iterator. |
24353 * | `$even` | {@type boolean} | true if the iterator position `$index` is even (otherwise false). |
24354 * | `$odd` | {@type boolean} | true if the iterator position `$index` is odd (otherwise false). |
24356 * Creating aliases for these properties is possible with {@link ng.directive:ngInit `ngInit`}.
24357 * This may be useful when, for instance, nesting ngRepeats.
24359 * # Iterating over object properties
24361 * It is possible to get `ngRepeat` to iterate over the properties of an object using the following
24365 * <div ng-repeat="(key, value) in myObj"> ... </div>
24368 * You need to be aware that the JavaScript specification does not define what order
24369 * it will return the keys for an object. In order to have a guaranteed deterministic order
24370 * for the keys, Angular versions up to and including 1.3 **sort the keys alphabetically**.
24372 * If this is not desired, the recommended workaround is to convert your object into an array
24373 * that is sorted into the order that you prefer before providing it to `ngRepeat`. You could
24374 * do this with a filter such as [toArrayFilter](http://ngmodules.org/modules/angular-toArrayFilter)
24375 * or implement a `$watch` on the object yourself.
24377 * In version 1.4 we will remove the sorting, since it seems that browsers generally follow the
24378 * strategy of providing keys in the order in which they were defined, although there are exceptions
24379 * when keys are deleted and reinstated.
24382 * # Tracking and Duplicates
24384 * When the contents of the collection change, `ngRepeat` makes the corresponding changes to the DOM:
24386 * * When an item is added, a new instance of the template is added to the DOM.
24387 * * When an item is removed, its template instance is removed from the DOM.
24388 * * When items are reordered, their respective templates are reordered in the DOM.
24390 * By default, `ngRepeat` does not allow duplicate items in arrays. This is because when
24391 * there are duplicates, it is not possible to maintain a one-to-one mapping between collection
24392 * items and DOM elements.
24394 * If you do need to repeat duplicate items, you can substitute the default tracking behavior
24395 * with your own using the `track by` expression.
24397 * For example, you may track items by the index of each item in the collection, using the
24398 * special scope property `$index`:
24400 * <div ng-repeat="n in [42, 42, 43, 43] track by $index">
24405 * You may use arbitrary expressions in `track by`, including references to custom functions
24408 * <div ng-repeat="n in [42, 42, 43, 43] track by myTrackingFunction(n)">
24413 * If you are working with objects that have an identifier property, you can track
24414 * by the identifier instead of the whole object. Should you reload your data later, `ngRepeat`
24415 * will not have to rebuild the DOM elements for items it has already rendered, even if the
24416 * JavaScript objects in the collection have been substituted for new ones:
24418 * <div ng-repeat="model in collection track by model.id">
24423 * When no `track by` expression is provided, it is equivalent to tracking by the built-in
24424 * `$id` function, which tracks items by their identity:
24426 * <div ng-repeat="obj in collection track by $id(obj)">
24431 * # Special repeat start and end points
24432 * To repeat a series of elements instead of just one parent element, ngRepeat (as well as other ng directives) supports extending
24433 * the range of the repeater by defining explicit start and end points by using **ng-repeat-start** and **ng-repeat-end** respectively.
24434 * 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)
24435 * up to and including the ending HTML tag where **ng-repeat-end** is placed.
24437 * The example below makes use of this feature:
24439 * <header ng-repeat-start="item in items">
24440 * Header {{ item }}
24442 * <div class="body">
24445 * <footer ng-repeat-end>
24446 * Footer {{ item }}
24450 * And with an input of {@type ['A','B']} for the items variable in the example above, the output will evaluate to:
24455 * <div class="body">
24464 * <div class="body">
24472 * The custom start and end points for ngRepeat also support all other HTML directive syntax flavors provided in AngularJS (such
24473 * as **data-ng-repeat-start**, **x-ng-repeat-start** and **ng:repeat-start**).
24476 * **.enter** - when a new item is added to the list or when an item is revealed after a filter
24478 * **.leave** - when an item is removed from the list or when an item is filtered out
24480 * **.move** - when an adjacent item is filtered out causing a reorder or when the item contents are reordered
24485 * @param {repeat_expression} ngRepeat The expression indicating how to enumerate a collection. These
24486 * formats are currently supported:
24488 * * `variable in expression` – where variable is the user defined loop variable and `expression`
24489 * is a scope expression giving the collection to enumerate.
24491 * For example: `album in artist.albums`.
24493 * * `(key, value) in expression` – where `key` and `value` can be any user defined identifiers,
24494 * and `expression` is the scope expression giving the collection to enumerate.
24496 * For example: `(name, age) in {'adam':10, 'amalie':12}`.
24498 * * `variable in expression track by tracking_expression` – You can also provide an optional tracking expression
24499 * which can be used to associate the objects in the collection with the DOM elements. If no tracking expression
24500 * is specified, ng-repeat associates elements by identity. It is an error to have
24501 * more than one tracking expression value resolve to the same key. (This would mean that two distinct objects are
24502 * mapped to the same DOM element, which is not possible.) If filters are used in the expression, they should be
24503 * applied before the tracking expression.
24505 * For example: `item in items` is equivalent to `item in items track by $id(item)`. This implies that the DOM elements
24506 * will be associated by item identity in the array.
24508 * For example: `item in items track by $id(item)`. A built in `$id()` function can be used to assign a unique
24509 * `$$hashKey` property to each item in the array. This property is then used as a key to associated DOM elements
24510 * with the corresponding item in the array by identity. Moving the same object in array would move the DOM
24511 * element in the same way in the DOM.
24513 * For example: `item in items track by item.id` is a typical pattern when the items come from the database. In this
24514 * case the object identity does not matter. Two objects are considered equivalent as long as their `id`
24515 * property is same.
24517 * For example: `item in items | filter:searchText track by item.id` is a pattern that might be used to apply a filter
24518 * to items in conjunction with a tracking expression.
24520 * * `variable in expression as alias_expression` – You can also provide an optional alias expression which will then store the
24521 * intermediate results of the repeater after the filters have been applied. Typically this is used to render a special message
24522 * when a filter is active on the repeater, but the filtered result set is empty.
24524 * For example: `item in items | filter:x as results` will store the fragment of the repeated items as `results`, but only after
24525 * the items have been processed through the filter.
24528 * This example initializes the scope to a list of names and
24529 * then uses `ngRepeat` to display every person:
24530 <example module="ngAnimate" deps="angular-animate.js" animations="true">
24531 <file name="index.html">
24532 <div ng-init="friends = [
24533 {name:'John', age:25, gender:'boy'},
24534 {name:'Jessie', age:30, gender:'girl'},
24535 {name:'Johanna', age:28, gender:'girl'},
24536 {name:'Joy', age:15, gender:'girl'},
24537 {name:'Mary', age:28, gender:'girl'},
24538 {name:'Peter', age:95, gender:'boy'},
24539 {name:'Sebastian', age:50, gender:'boy'},
24540 {name:'Erika', age:27, gender:'girl'},
24541 {name:'Patrick', age:40, gender:'boy'},
24542 {name:'Samantha', age:60, gender:'girl'}
24544 I have {{friends.length}} friends. They are:
24545 <input type="search" ng-model="q" placeholder="filter friends..." />
24546 <ul class="example-animate-container">
24547 <li class="animate-repeat" ng-repeat="friend in friends | filter:q as results">
24548 [{{$index + 1}}] {{friend.name}} who is {{friend.age}} years old.
24550 <li class="animate-repeat" ng-if="results.length == 0">
24551 <strong>No results found...</strong>
24556 <file name="animations.css">
24557 .example-animate-container {
24559 border:1px solid black;
24568 box-sizing:border-box;
24571 .animate-repeat.ng-move,
24572 .animate-repeat.ng-enter,
24573 .animate-repeat.ng-leave {
24574 -webkit-transition:all linear 0.5s;
24575 transition:all linear 0.5s;
24578 .animate-repeat.ng-leave.ng-leave-active,
24579 .animate-repeat.ng-move,
24580 .animate-repeat.ng-enter {
24585 .animate-repeat.ng-leave,
24586 .animate-repeat.ng-move.ng-move-active,
24587 .animate-repeat.ng-enter.ng-enter-active {
24592 <file name="protractor.js" type="protractor">
24593 var friends = element.all(by.repeater('friend in friends'));
24595 it('should render initial data set', function() {
24596 expect(friends.count()).toBe(10);
24597 expect(friends.get(0).getText()).toEqual('[1] John who is 25 years old.');
24598 expect(friends.get(1).getText()).toEqual('[2] Jessie who is 30 years old.');
24599 expect(friends.last().getText()).toEqual('[10] Samantha who is 60 years old.');
24600 expect(element(by.binding('friends.length')).getText())
24601 .toMatch("I have 10 friends. They are:");
24604 it('should update repeater when filter predicate changes', function() {
24605 expect(friends.count()).toBe(10);
24607 element(by.model('q')).sendKeys('ma');
24609 expect(friends.count()).toBe(2);
24610 expect(friends.get(0).getText()).toEqual('[1] Mary who is 28 years old.');
24611 expect(friends.last().getText()).toEqual('[2] Samantha who is 60 years old.');
24616 var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
24617 var NG_REMOVED = '$$NG_REMOVED';
24618 var ngRepeatMinErr = minErr('ngRepeat');
24620 var updateScope = function(scope, index, valueIdentifier, value, keyIdentifier, key, arrayLength) {
24621 // TODO(perf): generate setters to shave off ~40ms or 1-1.5%
24622 scope[valueIdentifier] = value;
24623 if (keyIdentifier) scope[keyIdentifier] = key;
24624 scope.$index = index;
24625 scope.$first = (index === 0);
24626 scope.$last = (index === (arrayLength - 1));
24627 scope.$middle = !(scope.$first || scope.$last);
24628 // jshint bitwise: false
24629 scope.$odd = !(scope.$even = (index&1) === 0);
24630 // jshint bitwise: true
24633 var getBlockStart = function(block) {
24634 return block.clone[0];
24637 var getBlockEnd = function(block) {
24638 return block.clone[block.clone.length - 1];
24644 multiElement: true,
24645 transclude: 'element',
24649 compile: function ngRepeatCompile($element, $attr) {
24650 var expression = $attr.ngRepeat;
24651 var ngRepeatEndComment = document.createComment(' end ngRepeat: ' + expression + ' ');
24653 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*$/);
24656 throw ngRepeatMinErr('iexp', "Expected expression in form of '_item_ in _collection_[ track by _id_]' but got '{0}'.",
24660 var lhs = match[1];
24661 var rhs = match[2];
24662 var aliasAs = match[3];
24663 var trackByExp = match[4];
24665 match = lhs.match(/^(?:(\s*[\$\w]+)|\(\s*([\$\w]+)\s*,\s*([\$\w]+)\s*\))$/);
24668 throw ngRepeatMinErr('iidexp', "'_item_' in '_item_ in _collection_' should be an identifier or '(_key_, _value_)' expression, but got '{0}'.",
24671 var valueIdentifier = match[3] || match[1];
24672 var keyIdentifier = match[2];
24674 if (aliasAs && (!/^[$a-zA-Z_][$a-zA-Z0-9_]*$/.test(aliasAs) ||
24675 /^(null|undefined|this|\$index|\$first|\$middle|\$last|\$even|\$odd|\$parent|\$root|\$id)$/.test(aliasAs))) {
24676 throw ngRepeatMinErr('badident', "alias '{0}' is invalid --- must be a valid JS identifier which is not a reserved name.",
24680 var trackByExpGetter, trackByIdExpFn, trackByIdArrayFn, trackByIdObjFn;
24681 var hashFnLocals = {$id: hashKey};
24684 trackByExpGetter = $parse(trackByExp);
24686 trackByIdArrayFn = function(key, value) {
24687 return hashKey(value);
24689 trackByIdObjFn = function(key) {
24694 return function ngRepeatLink($scope, $element, $attr, ctrl, $transclude) {
24696 if (trackByExpGetter) {
24697 trackByIdExpFn = function(key, value, index) {
24698 // assign key, value, and $index to the locals so that they can be used in hash functions
24699 if (keyIdentifier) hashFnLocals[keyIdentifier] = key;
24700 hashFnLocals[valueIdentifier] = value;
24701 hashFnLocals.$index = index;
24702 return trackByExpGetter($scope, hashFnLocals);
24706 // Store a list of elements from previous run. This is a hash where key is the item from the
24707 // iterator, and the value is objects with following properties.
24708 // - scope: bound scope
24709 // - element: previous element.
24710 // - index: position
24712 // We are using no-proto object so that we don't need to guard against inherited props via
24714 var lastBlockMap = createMap();
24717 $scope.$watchCollection(rhs, function ngRepeatAction(collection) {
24719 previousNode = $element[0], // node that cloned nodes should be inserted after
24720 // initialized to the comment node anchor
24722 // Same as lastBlockMap but it has the current state. It will become the
24723 // lastBlockMap on the next iteration.
24724 nextBlockMap = createMap(),
24726 key, value, // key/value of iteration
24730 block, // last object information {scope, element, id}
24735 $scope[aliasAs] = collection;
24738 if (isArrayLike(collection)) {
24739 collectionKeys = collection;
24740 trackByIdFn = trackByIdExpFn || trackByIdArrayFn;
24742 trackByIdFn = trackByIdExpFn || trackByIdObjFn;
24743 // if object, extract keys, sort them and use to determine order of iteration over obj props
24744 collectionKeys = [];
24745 for (var itemKey in collection) {
24746 if (collection.hasOwnProperty(itemKey) && itemKey.charAt(0) != '$') {
24747 collectionKeys.push(itemKey);
24750 collectionKeys.sort();
24753 collectionLength = collectionKeys.length;
24754 nextBlockOrder = new Array(collectionLength);
24756 // locate existing items
24757 for (index = 0; index < collectionLength; index++) {
24758 key = (collection === collectionKeys) ? index : collectionKeys[index];
24759 value = collection[key];
24760 trackById = trackByIdFn(key, value, index);
24761 if (lastBlockMap[trackById]) {
24762 // found previously seen block
24763 block = lastBlockMap[trackById];
24764 delete lastBlockMap[trackById];
24765 nextBlockMap[trackById] = block;
24766 nextBlockOrder[index] = block;
24767 } else if (nextBlockMap[trackById]) {
24768 // if collision detected. restore lastBlockMap and throw an error
24769 forEach(nextBlockOrder, function(block) {
24770 if (block && block.scope) lastBlockMap[block.id] = block;
24772 throw ngRepeatMinErr('dupes',
24773 "Duplicates in a repeater are not allowed. Use 'track by' expression to specify unique keys. Repeater: {0}, Duplicate key: {1}, Duplicate value: {2}",
24774 expression, trackById, value);
24776 // new never before seen block
24777 nextBlockOrder[index] = {id: trackById, scope: undefined, clone: undefined};
24778 nextBlockMap[trackById] = true;
24782 // remove leftover items
24783 for (var blockKey in lastBlockMap) {
24784 block = lastBlockMap[blockKey];
24785 elementsToRemove = getBlockNodes(block.clone);
24786 $animate.leave(elementsToRemove);
24787 if (elementsToRemove[0].parentNode) {
24788 // if the element was not removed yet because of pending animation, mark it as deleted
24789 // so that we can ignore it later
24790 for (index = 0, length = elementsToRemove.length; index < length; index++) {
24791 elementsToRemove[index][NG_REMOVED] = true;
24794 block.scope.$destroy();
24797 // we are not using forEach for perf reasons (trying to avoid #call)
24798 for (index = 0; index < collectionLength; index++) {
24799 key = (collection === collectionKeys) ? index : collectionKeys[index];
24800 value = collection[key];
24801 block = nextBlockOrder[index];
24804 // if we have already seen this object, then we need to reuse the
24805 // associated scope/element
24807 nextNode = previousNode;
24809 // skip nodes that are already pending removal via leave animation
24811 nextNode = nextNode.nextSibling;
24812 } while (nextNode && nextNode[NG_REMOVED]);
24814 if (getBlockStart(block) != nextNode) {
24815 // existing item which got moved
24816 $animate.move(getBlockNodes(block.clone), null, jqLite(previousNode));
24818 previousNode = getBlockEnd(block);
24819 updateScope(block.scope, index, valueIdentifier, value, keyIdentifier, key, collectionLength);
24821 // new item which we don't know about
24822 $transclude(function ngRepeatTransclude(clone, scope) {
24823 block.scope = scope;
24824 // http://jsperf.com/clone-vs-createcomment
24825 var endNode = ngRepeatEndComment.cloneNode(false);
24826 clone[clone.length++] = endNode;
24828 // TODO(perf): support naked previousNode in `enter` to avoid creation of jqLite wrapper?
24829 $animate.enter(clone, null, jqLite(previousNode));
24830 previousNode = endNode;
24831 // Note: We only need the first/last node of the cloned nodes.
24832 // However, we need to keep the reference to the jqlite wrapper as it might be changed later
24833 // by a directive with templateUrl when its template arrives.
24834 block.clone = clone;
24835 nextBlockMap[block.id] = block;
24836 updateScope(block.scope, index, valueIdentifier, value, keyIdentifier, key, collectionLength);
24840 lastBlockMap = nextBlockMap;
24847 var NG_HIDE_CLASS = 'ng-hide';
24848 var NG_HIDE_IN_PROGRESS_CLASS = 'ng-hide-animate';
24854 * The `ngShow` directive shows or hides the given HTML element based on the expression
24855 * provided to the `ngShow` attribute. The element is shown or hidden by removing or adding
24856 * the `.ng-hide` CSS class onto the element. The `.ng-hide` CSS class is predefined
24857 * in AngularJS and sets the display style to none (using an !important flag).
24858 * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
24861 * <!-- when $scope.myValue is truthy (element is visible) -->
24862 * <div ng-show="myValue"></div>
24864 * <!-- when $scope.myValue is falsy (element is hidden) -->
24865 * <div ng-show="myValue" class="ng-hide"></div>
24868 * When the `ngShow` expression evaluates to a falsy value then the `.ng-hide` CSS class is added to the class
24869 * attribute on the element causing it to become hidden. When truthy, the `.ng-hide` CSS class is removed
24870 * from the element causing the element not to appear hidden.
24872 * ## Why is !important used?
24874 * You may be wondering why !important is used for the `.ng-hide` CSS class. This is because the `.ng-hide` selector
24875 * can be easily overridden by heavier selectors. For example, something as simple
24876 * as changing the display style on a HTML list item would make hidden elements appear visible.
24877 * This also becomes a bigger issue when dealing with CSS frameworks.
24879 * By using !important, the show and hide behavior will work as expected despite any clash between CSS selector
24880 * specificity (when !important isn't used with any conflicting styles). If a developer chooses to override the
24881 * styling to change how to hide an element then it is just a matter of using !important in their own CSS code.
24883 * ### Overriding `.ng-hide`
24885 * By default, the `.ng-hide` class will style the element with `display: none!important`. If you wish to change
24886 * the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide`
24887 * class CSS. Note that the selector that needs to be used is actually `.ng-hide:not(.ng-hide-animate)` to cope
24888 * with extra animation classes that can be added.
24891 * .ng-hide:not(.ng-hide-animate) {
24892 * /* this is just another form of hiding an element */
24893 * display: block!important;
24894 * position: absolute;
24900 * By default you don't need to override in CSS anything and the animations will work around the display style.
24902 * ## A note about animations with `ngShow`
24904 * Animations in ngShow/ngHide work with the show and hide events that are triggered when the directive expression
24905 * is true and false. This system works like the animation system present with ngClass except that
24906 * you must also include the !important flag to override the display property
24907 * so that you can perform an animation when the element is hidden during the time of the animation.
24911 * //a working example can be found at the bottom of this page
24913 * .my-element.ng-hide-add, .my-element.ng-hide-remove {
24914 * /* this is required as of 1.3x to properly
24915 * apply all styling in a show/hide animation */
24916 * transition: 0s linear all;
24919 * .my-element.ng-hide-add-active,
24920 * .my-element.ng-hide-remove-active {
24921 * /* the transition is defined in the active class */
24922 * transition: 1s linear all;
24925 * .my-element.ng-hide-add { ... }
24926 * .my-element.ng-hide-add.ng-hide-add-active { ... }
24927 * .my-element.ng-hide-remove { ... }
24928 * .my-element.ng-hide-remove.ng-hide-remove-active { ... }
24931 * Keep in mind that, as of AngularJS version 1.3.0-beta.11, there is no need to change the display
24932 * property to block during animation states--ngAnimate will handle the style toggling automatically for you.
24935 * addClass: `.ng-hide` - happens after the `ngShow` expression evaluates to a truthy value and the just before contents are set to visible
24936 * removeClass: `.ng-hide` - happens after the `ngShow` expression evaluates to a non truthy value and just before the contents are set to hidden
24939 * @param {expression} ngShow If the {@link guide/expression expression} is truthy
24940 * then the element is shown or hidden respectively.
24943 <example module="ngAnimate" deps="angular-animate.js" animations="true">
24944 <file name="index.html">
24945 Click me: <input type="checkbox" ng-model="checked"><br/>
24948 <div class="check-element animate-show" ng-show="checked">
24949 <span class="glyphicon glyphicon-thumbs-up"></span> I show up when your checkbox is checked.
24954 <div class="check-element animate-show" ng-hide="checked">
24955 <span class="glyphicon glyphicon-thumbs-down"></span> I hide when your checkbox is checked.
24959 <file name="glyphicons.css">
24960 @import url(../../components/bootstrap-3.1.1/css/bootstrap.css);
24962 <file name="animations.css">
24967 border: 1px solid black;
24971 .animate-show.ng-hide-add.ng-hide-add-active,
24972 .animate-show.ng-hide-remove.ng-hide-remove-active {
24973 -webkit-transition: all linear 0.5s;
24974 transition: all linear 0.5s;
24977 .animate-show.ng-hide {
24985 border: 1px solid black;
24989 <file name="protractor.js" type="protractor">
24990 var thumbsUp = element(by.css('span.glyphicon-thumbs-up'));
24991 var thumbsDown = element(by.css('span.glyphicon-thumbs-down'));
24993 it('should check ng-show / ng-hide', function() {
24994 expect(thumbsUp.isDisplayed()).toBeFalsy();
24995 expect(thumbsDown.isDisplayed()).toBeTruthy();
24997 element(by.model('checked')).click();
24999 expect(thumbsUp.isDisplayed()).toBeTruthy();
25000 expect(thumbsDown.isDisplayed()).toBeFalsy();
25005 var ngShowDirective = ['$animate', function($animate) {
25008 multiElement: true,
25009 link: function(scope, element, attr) {
25010 scope.$watch(attr.ngShow, function ngShowWatchAction(value) {
25011 // we're adding a temporary, animation-specific class for ng-hide since this way
25012 // we can control when the element is actually displayed on screen without having
25013 // to have a global/greedy CSS selector that breaks when other animations are run.
25014 // Read: https://github.com/angular/angular.js/issues/9103#issuecomment-58335845
25015 $animate[value ? 'removeClass' : 'addClass'](element, NG_HIDE_CLASS, {
25016 tempClasses: NG_HIDE_IN_PROGRESS_CLASS
25029 * The `ngHide` directive shows or hides the given HTML element based on the expression
25030 * provided to the `ngHide` attribute. The element is shown or hidden by removing or adding
25031 * the `ng-hide` CSS class onto the element. The `.ng-hide` CSS class is predefined
25032 * in AngularJS and sets the display style to none (using an !important flag).
25033 * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
25036 * <!-- when $scope.myValue is truthy (element is hidden) -->
25037 * <div ng-hide="myValue" class="ng-hide"></div>
25039 * <!-- when $scope.myValue is falsy (element is visible) -->
25040 * <div ng-hide="myValue"></div>
25043 * When the `ngHide` expression evaluates to a truthy value then the `.ng-hide` CSS class is added to the class
25044 * attribute on the element causing it to become hidden. When falsy, the `.ng-hide` CSS class is removed
25045 * from the element causing the element not to appear hidden.
25047 * ## Why is !important used?
25049 * You may be wondering why !important is used for the `.ng-hide` CSS class. This is because the `.ng-hide` selector
25050 * can be easily overridden by heavier selectors. For example, something as simple
25051 * as changing the display style on a HTML list item would make hidden elements appear visible.
25052 * This also becomes a bigger issue when dealing with CSS frameworks.
25054 * By using !important, the show and hide behavior will work as expected despite any clash between CSS selector
25055 * specificity (when !important isn't used with any conflicting styles). If a developer chooses to override the
25056 * styling to change how to hide an element then it is just a matter of using !important in their own CSS code.
25058 * ### Overriding `.ng-hide`
25060 * By default, the `.ng-hide` class will style the element with `display: none!important`. If you wish to change
25061 * the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide`
25066 * /* this is just another form of hiding an element */
25067 * display: block!important;
25068 * position: absolute;
25074 * By default you don't need to override in CSS anything and the animations will work around the display style.
25076 * ## A note about animations with `ngHide`
25078 * Animations in ngShow/ngHide work with the show and hide events that are triggered when the directive expression
25079 * is true and false. This system works like the animation system present with ngClass, except that the `.ng-hide`
25080 * CSS class is added and removed for you instead of your own CSS class.
25084 * //a working example can be found at the bottom of this page
25086 * .my-element.ng-hide-add, .my-element.ng-hide-remove {
25087 * transition: 0.5s linear all;
25090 * .my-element.ng-hide-add { ... }
25091 * .my-element.ng-hide-add.ng-hide-add-active { ... }
25092 * .my-element.ng-hide-remove { ... }
25093 * .my-element.ng-hide-remove.ng-hide-remove-active { ... }
25096 * Keep in mind that, as of AngularJS version 1.3.0-beta.11, there is no need to change the display
25097 * property to block during animation states--ngAnimate will handle the style toggling automatically for you.
25100 * removeClass: `.ng-hide` - happens after the `ngHide` expression evaluates to a truthy value and just before the contents are set to hidden
25101 * addClass: `.ng-hide` - happens after the `ngHide` expression evaluates to a non truthy value and just before the contents are set to visible
25104 * @param {expression} ngHide If the {@link guide/expression expression} is truthy then
25105 * the element is shown or hidden respectively.
25108 <example module="ngAnimate" deps="angular-animate.js" animations="true">
25109 <file name="index.html">
25110 Click me: <input type="checkbox" ng-model="checked"><br/>
25113 <div class="check-element animate-hide" ng-show="checked">
25114 <span class="glyphicon glyphicon-thumbs-up"></span> I show up when your checkbox is checked.
25119 <div class="check-element animate-hide" ng-hide="checked">
25120 <span class="glyphicon glyphicon-thumbs-down"></span> I hide when your checkbox is checked.
25124 <file name="glyphicons.css">
25125 @import url(../../components/bootstrap-3.1.1/css/bootstrap.css);
25127 <file name="animations.css">
25129 -webkit-transition: all linear 0.5s;
25130 transition: all linear 0.5s;
25134 border: 1px solid black;
25138 .animate-hide.ng-hide {
25146 border: 1px solid black;
25150 <file name="protractor.js" type="protractor">
25151 var thumbsUp = element(by.css('span.glyphicon-thumbs-up'));
25152 var thumbsDown = element(by.css('span.glyphicon-thumbs-down'));
25154 it('should check ng-show / ng-hide', function() {
25155 expect(thumbsUp.isDisplayed()).toBeFalsy();
25156 expect(thumbsDown.isDisplayed()).toBeTruthy();
25158 element(by.model('checked')).click();
25160 expect(thumbsUp.isDisplayed()).toBeTruthy();
25161 expect(thumbsDown.isDisplayed()).toBeFalsy();
25166 var ngHideDirective = ['$animate', function($animate) {
25169 multiElement: true,
25170 link: function(scope, element, attr) {
25171 scope.$watch(attr.ngHide, function ngHideWatchAction(value) {
25172 // The comment inside of the ngShowDirective explains why we add and
25173 // remove a temporary class for the show/hide animation
25174 $animate[value ? 'addClass' : 'removeClass'](element,NG_HIDE_CLASS, {
25175 tempClasses: NG_HIDE_IN_PROGRESS_CLASS
25188 * The `ngStyle` directive allows you to set CSS style on an HTML element conditionally.
25191 * @param {expression} ngStyle
25193 * {@link guide/expression Expression} which evals to an
25194 * object whose keys are CSS style names and values are corresponding values for those CSS
25197 * Since some CSS style names are not valid keys for an object, they must be quoted.
25198 * See the 'background-color' style in the example below.
25202 <file name="index.html">
25203 <input type="button" value="set color" ng-click="myStyle={color:'red'}">
25204 <input type="button" value="set background" ng-click="myStyle={'background-color':'blue'}">
25205 <input type="button" value="clear" ng-click="myStyle={}">
25207 <span ng-style="myStyle">Sample Text</span>
25208 <pre>myStyle={{myStyle}}</pre>
25210 <file name="style.css">
25215 <file name="protractor.js" type="protractor">
25216 var colorSpan = element(by.css('span'));
25218 it('should check ng-style', function() {
25219 expect(colorSpan.getCssValue('color')).toBe('rgba(0, 0, 0, 1)');
25220 element(by.css('input[value=\'set color\']')).click();
25221 expect(colorSpan.getCssValue('color')).toBe('rgba(255, 0, 0, 1)');
25222 element(by.css('input[value=clear]')).click();
25223 expect(colorSpan.getCssValue('color')).toBe('rgba(0, 0, 0, 1)');
25228 var ngStyleDirective = ngDirective(function(scope, element, attr) {
25229 scope.$watch(attr.ngStyle, function ngStyleWatchAction(newStyles, oldStyles) {
25230 if (oldStyles && (newStyles !== oldStyles)) {
25231 forEach(oldStyles, function(val, style) { element.css(style, '');});
25233 if (newStyles) element.css(newStyles);
25243 * The `ngSwitch` directive is used to conditionally swap DOM structure on your template based on a scope expression.
25244 * Elements within `ngSwitch` but without `ngSwitchWhen` or `ngSwitchDefault` directives will be preserved at the location
25245 * as specified in the template.
25247 * The directive itself works similar to ngInclude, however, instead of downloading template code (or loading it
25248 * from the template cache), `ngSwitch` simply chooses one of the nested elements and makes it visible based on which element
25249 * matches the value obtained from the evaluated expression. In other words, you define a container element
25250 * (where you place the directive), place an expression on the **`on="..."` attribute**
25251 * (or the **`ng-switch="..."` attribute**), define any inner elements inside of the directive and place
25252 * a when attribute per element. The when attribute is used to inform ngSwitch which element to display when the on
25253 * expression is evaluated. If a matching expression is not found via a when attribute then an element with the default
25254 * attribute is displayed.
25256 * <div class="alert alert-info">
25257 * Be aware that the attribute values to match against cannot be expressions. They are interpreted
25258 * as literal string values to match against.
25259 * For example, **`ng-switch-when="someVal"`** will match against the string `"someVal"` not against the
25260 * value of the expression `$scope.someVal`.
25264 * enter - happens after the ngSwitch contents change and the matched child element is placed inside the container
25265 * leave - happens just after the ngSwitch contents change and just before the former contents are removed from the DOM
25270 * <ANY ng-switch="expression">
25271 * <ANY ng-switch-when="matchValue1">...</ANY>
25272 * <ANY ng-switch-when="matchValue2">...</ANY>
25273 * <ANY ng-switch-default>...</ANY>
25280 * @param {*} ngSwitch|on expression to match against <code>ng-switch-when</code>.
25281 * On child elements add:
25283 * * `ngSwitchWhen`: the case statement to match against. If match then this
25284 * case will be displayed. If the same match appears multiple times, all the
25285 * elements will be displayed.
25286 * * `ngSwitchDefault`: the default case when no other case match. If there
25287 * are multiple default cases, all of them will be displayed when no other
25292 <example module="switchExample" deps="angular-animate.js" animations="true">
25293 <file name="index.html">
25294 <div ng-controller="ExampleController">
25295 <select ng-model="selection" ng-options="item for item in items">
25297 <code>selection={{selection}}</code>
25299 <div class="animate-switch-container"
25300 ng-switch on="selection">
25301 <div class="animate-switch" ng-switch-when="settings">Settings Div</div>
25302 <div class="animate-switch" ng-switch-when="home">Home Span</div>
25303 <div class="animate-switch" ng-switch-default>default</div>
25307 <file name="script.js">
25308 angular.module('switchExample', ['ngAnimate'])
25309 .controller('ExampleController', ['$scope', function($scope) {
25310 $scope.items = ['settings', 'home', 'other'];
25311 $scope.selection = $scope.items[0];
25314 <file name="animations.css">
25315 .animate-switch-container {
25318 border:1px solid black;
25327 .animate-switch.ng-animate {
25328 -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
25329 transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
25338 .animate-switch.ng-leave.ng-leave-active,
25339 .animate-switch.ng-enter {
25342 .animate-switch.ng-leave,
25343 .animate-switch.ng-enter.ng-enter-active {
25347 <file name="protractor.js" type="protractor">
25348 var switchElem = element(by.css('[ng-switch]'));
25349 var select = element(by.model('selection'));
25351 it('should start in settings', function() {
25352 expect(switchElem.getText()).toMatch(/Settings Div/);
25354 it('should change to home', function() {
25355 select.all(by.css('option')).get(1).click();
25356 expect(switchElem.getText()).toMatch(/Home Span/);
25358 it('should select default', function() {
25359 select.all(by.css('option')).get(2).click();
25360 expect(switchElem.getText()).toMatch(/default/);
25365 var ngSwitchDirective = ['$animate', function($animate) {
25368 require: 'ngSwitch',
25370 // asks for $scope to fool the BC controller module
25371 controller: ['$scope', function ngSwitchController() {
25374 link: function(scope, element, attr, ngSwitchController) {
25375 var watchExpr = attr.ngSwitch || attr.on,
25376 selectedTranscludes = [],
25377 selectedElements = [],
25378 previousLeaveAnimations = [],
25379 selectedScopes = [];
25381 var spliceFactory = function(array, index) {
25382 return function() { array.splice(index, 1); };
25385 scope.$watch(watchExpr, function ngSwitchWatchAction(value) {
25387 for (i = 0, ii = previousLeaveAnimations.length; i < ii; ++i) {
25388 $animate.cancel(previousLeaveAnimations[i]);
25390 previousLeaveAnimations.length = 0;
25392 for (i = 0, ii = selectedScopes.length; i < ii; ++i) {
25393 var selected = getBlockNodes(selectedElements[i].clone);
25394 selectedScopes[i].$destroy();
25395 var promise = previousLeaveAnimations[i] = $animate.leave(selected);
25396 promise.then(spliceFactory(previousLeaveAnimations, i));
25399 selectedElements.length = 0;
25400 selectedScopes.length = 0;
25402 if ((selectedTranscludes = ngSwitchController.cases['!' + value] || ngSwitchController.cases['?'])) {
25403 forEach(selectedTranscludes, function(selectedTransclude) {
25404 selectedTransclude.transclude(function(caseElement, selectedScope) {
25405 selectedScopes.push(selectedScope);
25406 var anchor = selectedTransclude.element;
25407 caseElement[caseElement.length++] = document.createComment(' end ngSwitchWhen: ');
25408 var block = { clone: caseElement };
25410 selectedElements.push(block);
25411 $animate.enter(caseElement, anchor.parent(), anchor);
25420 var ngSwitchWhenDirective = ngDirective({
25421 transclude: 'element',
25423 require: '^ngSwitch',
25424 multiElement: true,
25425 link: function(scope, element, attrs, ctrl, $transclude) {
25426 ctrl.cases['!' + attrs.ngSwitchWhen] = (ctrl.cases['!' + attrs.ngSwitchWhen] || []);
25427 ctrl.cases['!' + attrs.ngSwitchWhen].push({ transclude: $transclude, element: element });
25431 var ngSwitchDefaultDirective = ngDirective({
25432 transclude: 'element',
25434 require: '^ngSwitch',
25435 multiElement: true,
25436 link: function(scope, element, attr, ctrl, $transclude) {
25437 ctrl.cases['?'] = (ctrl.cases['?'] || []);
25438 ctrl.cases['?'].push({ transclude: $transclude, element: element });
25444 * @name ngTransclude
25448 * Directive that marks the insertion point for the transcluded DOM of the nearest parent directive that uses transclusion.
25450 * Any existing content of the element that this directive is placed on will be removed before the transcluded content is inserted.
25455 <example module="transcludeExample">
25456 <file name="index.html">
25458 angular.module('transcludeExample', [])
25459 .directive('pane', function(){
25463 scope: { title:'@' },
25464 template: '<div style="border: 1px solid black;">' +
25465 '<div style="background-color: gray">{{title}}</div>' +
25466 '<ng-transclude></ng-transclude>' +
25470 .controller('ExampleController', ['$scope', function($scope) {
25471 $scope.title = 'Lorem Ipsum';
25472 $scope.text = 'Neque porro quisquam est qui dolorem ipsum quia dolor...';
25475 <div ng-controller="ExampleController">
25476 <input ng-model="title"> <br/>
25477 <textarea ng-model="text"></textarea> <br/>
25478 <pane title="{{title}}">{{text}}</pane>
25481 <file name="protractor.js" type="protractor">
25482 it('should have transcluded', function() {
25483 var titleElement = element(by.model('title'));
25484 titleElement.clear();
25485 titleElement.sendKeys('TITLE');
25486 var textElement = element(by.model('text'));
25487 textElement.clear();
25488 textElement.sendKeys('TEXT');
25489 expect(element(by.binding('title')).getText()).toEqual('TITLE');
25490 expect(element(by.binding('text')).getText()).toEqual('TEXT');
25496 var ngTranscludeDirective = ngDirective({
25498 link: function($scope, $element, $attrs, controller, $transclude) {
25499 if (!$transclude) {
25500 throw minErr('ngTransclude')('orphan',
25501 'Illegal use of ngTransclude directive in the template! ' +
25502 'No parent directive that requires a transclusion found. ' +
25504 startingTag($element));
25507 $transclude(function(clone) {
25509 $element.append(clone);
25520 * Load the content of a `<script>` element into {@link ng.$templateCache `$templateCache`}, so that the
25521 * template can be used by {@link ng.directive:ngInclude `ngInclude`},
25522 * {@link ngRoute.directive:ngView `ngView`}, or {@link guide/directive directives}. The type of the
25523 * `<script>` element must be specified as `text/ng-template`, and a cache name for the template must be
25524 * assigned through the element's `id`, which can then be used as a directive's `templateUrl`.
25526 * @param {string} type Must be set to `'text/ng-template'`.
25527 * @param {string} id Cache name of the template.
25531 <file name="index.html">
25532 <script type="text/ng-template" id="/tpl.html">
25533 Content of the template.
25536 <a ng-click="currentTpl='/tpl.html'" id="tpl-link">Load inlined template</a>
25537 <div id="tpl-content" ng-include src="currentTpl"></div>
25539 <file name="protractor.js" type="protractor">
25540 it('should load template defined inside script tag', function() {
25541 element(by.css('#tpl-link')).click();
25542 expect(element(by.css('#tpl-content')).getText()).toMatch(/Content of the template/);
25547 var scriptDirective = ['$templateCache', function($templateCache) {
25551 compile: function(element, attr) {
25552 if (attr.type == 'text/ng-template') {
25553 var templateUrl = attr.id,
25554 text = element[0].text;
25556 $templateCache.put(templateUrl, text);
25562 var ngOptionsMinErr = minErr('ngOptions');
25569 * HTML `SELECT` element with angular data-binding.
25573 * The `ngOptions` attribute can be used to dynamically generate a list of `<option>`
25574 * elements for the `<select>` element using the array or object obtained by evaluating the
25575 * `ngOptions` comprehension expression.
25577 * In many cases, `ngRepeat` can be used on `<option>` elements instead of `ngOptions` to achieve a
25578 * similar result. However, `ngOptions` provides some benefits such as reducing memory and
25579 * increasing speed by not creating a new scope for each repeated instance, as well as providing
25580 * more flexibility in how the `<select>`'s model is assigned via the `select` **`as`** part of the
25581 * comprehension expression. `ngOptions` should be used when the `<select>` model needs to be bound
25582 * to a non-string value. This is because an option element can only be bound to string values at
25585 * When an item in the `<select>` menu is selected, the array element or object property
25586 * represented by the selected option will be bound to the model identified by the `ngModel`
25589 * Optionally, a single hard-coded `<option>` element, with the value set to an empty string, can
25590 * be nested into the `<select>` element. This element will then represent the `null` or "not selected"
25591 * option. See example below for demonstration.
25593 * <div class="alert alert-warning">
25594 * **Note:** `ngModel` compares by reference, not value. This is important when binding to an
25595 * array of objects. See an example [in this jsfiddle](http://jsfiddle.net/qWzTb/).
25598 * ## `select` **`as`**
25600 * Using `select` **`as`** will bind the result of the `select` expression to the model, but
25601 * the value of the `<select>` and `<option>` html elements will be either the index (for array data sources)
25602 * or property name (for object data sources) of the value within the collection. If a **`track by`** expression
25603 * is used, the result of that expression will be set as the value of the `option` and `select` elements.
25606 * ### `select` **`as`** and **`track by`**
25608 * <div class="alert alert-warning">
25609 * Do not use `select` **`as`** and **`track by`** in the same expression. They are not designed to work together.
25612 * Consider the following example:
25615 * <select ng-options="item.subItem as item.label for item in values track by item.id" ng-model="selected">
25619 * $scope.values = [{
25622 * subItem: { name: 'aSubItem' }
25626 * subItem: { name: 'bSubItem' }
25629 * $scope.selected = { name: 'aSubItem' };
25632 * With the purpose of preserving the selection, the **`track by`** expression is always applied to the element
25633 * of the data source (to `item` in this example). To calculate whether an element is selected, we do the
25636 * 1. Apply **`track by`** to the elements in the array. In the example: `[1, 2]`
25637 * 2. Apply **`track by`** to the already selected value in `ngModel`.
25638 * In the example: this is not possible as **`track by`** refers to `item.id`, but the selected
25639 * value from `ngModel` is `{name: 'aSubItem'}`, so the **`track by`** expression is applied to
25640 * a wrong object, the selected element can't be found, `<select>` is always reset to the "not
25641 * selected" option.
25644 * @param {string} ngModel Assignable angular expression to data-bind to.
25645 * @param {string=} name Property name of the form under which the control is published.
25646 * @param {string=} required The control is considered valid only if value is entered.
25647 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
25648 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
25649 * `required` when you want to data-bind to the `required` attribute.
25650 * @param {comprehension_expression=} ngOptions in one of the following forms:
25652 * * for array data sources:
25653 * * `label` **`for`** `value` **`in`** `array`
25654 * * `select` **`as`** `label` **`for`** `value` **`in`** `array`
25655 * * `label` **`group by`** `group` **`for`** `value` **`in`** `array`
25656 * * `label` **`group by`** `group` **`for`** `value` **`in`** `array` **`track by`** `trackexpr`
25657 * * `label` **`for`** `value` **`in`** `array` | orderBy:`orderexpr` **`track by`** `trackexpr`
25658 * (for including a filter with `track by`)
25659 * * for object data sources:
25660 * * `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
25661 * * `select` **`as`** `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
25662 * * `label` **`group by`** `group` **`for (`**`key`**`,`** `value`**`) in`** `object`
25663 * * `select` **`as`** `label` **`group by`** `group`
25664 * **`for` `(`**`key`**`,`** `value`**`) in`** `object`
25668 * * `array` / `object`: an expression which evaluates to an array / object to iterate over.
25669 * * `value`: local variable which will refer to each item in the `array` or each property value
25670 * of `object` during iteration.
25671 * * `key`: local variable which will refer to a property name in `object` during iteration.
25672 * * `label`: The result of this expression will be the label for `<option>` element. The
25673 * `expression` will most likely refer to the `value` variable (e.g. `value.propertyName`).
25674 * * `select`: The result of this expression will be bound to the model of the parent `<select>`
25675 * element. If not specified, `select` expression will default to `value`.
25676 * * `group`: The result of this expression will be used to group options using the `<optgroup>`
25678 * * `trackexpr`: Used when working with an array of objects. The result of this expression will be
25679 * used to identify the objects in the array. The `trackexpr` will most likely refer to the
25680 * `value` variable (e.g. `value.propertyName`). With this the selection is preserved
25681 * even when the options are recreated (e.g. reloaded from the server).
25684 <example module="selectExample">
25685 <file name="index.html">
25687 angular.module('selectExample', [])
25688 .controller('ExampleController', ['$scope', function($scope) {
25690 {name:'black', shade:'dark'},
25691 {name:'white', shade:'light'},
25692 {name:'red', shade:'dark'},
25693 {name:'blue', shade:'dark'},
25694 {name:'yellow', shade:'light'}
25696 $scope.myColor = $scope.colors[2]; // red
25699 <div ng-controller="ExampleController">
25701 <li ng-repeat="color in colors">
25702 Name: <input ng-model="color.name">
25703 [<a href ng-click="colors.splice($index, 1)">X</a>]
25706 [<a href ng-click="colors.push({})">add</a>]
25710 Color (null not allowed):
25711 <select ng-model="myColor" ng-options="color.name for color in colors"></select><br>
25713 Color (null allowed):
25714 <span class="nullable">
25715 <select ng-model="myColor" ng-options="color.name for color in colors">
25716 <option value="">-- choose color --</option>
25720 Color grouped by shade:
25721 <select ng-model="myColor" ng-options="color.name group by color.shade for color in colors">
25725 Select <a href ng-click="myColor = { name:'not in list', shade: 'other' }">bogus</a>.<br>
25727 Currently selected: {{ {selected_color:myColor} }}
25728 <div style="border:solid 1px black; height:20px"
25729 ng-style="{'background-color':myColor.name}">
25733 <file name="protractor.js" type="protractor">
25734 it('should check ng-options', function() {
25735 expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('red');
25736 element.all(by.model('myColor')).first().click();
25737 element.all(by.css('select[ng-model="myColor"] option')).first().click();
25738 expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('black');
25739 element(by.css('.nullable select[ng-model="myColor"]')).click();
25740 element.all(by.css('.nullable select[ng-model="myColor"] option')).first().click();
25741 expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('null');
25747 var ngOptionsDirective = valueFn({
25752 // jshint maxlen: false
25753 var selectDirective = ['$compile', '$parse', function($compile, $parse) {
25754 //000011111111110000000000022222222220000000000000000000003333333333000000000000004444444444444440000000005555555555555550000000666666666666666000000000000000777777777700000000000000000008888888888
25755 var NG_OPTIONS_REGEXP = /^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+group\s+by\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]+?))?$/,
25756 nullModelCtrl = {$setViewValue: noop};
25757 // jshint maxlen: 100
25761 require: ['select', '?ngModel'],
25762 controller: ['$element', '$scope', '$attrs', function($element, $scope, $attrs) {
25765 ngModelCtrl = nullModelCtrl,
25770 self.databound = $attrs.ngModel;
25773 self.init = function(ngModelCtrl_, nullOption_, unknownOption_) {
25774 ngModelCtrl = ngModelCtrl_;
25775 nullOption = nullOption_;
25776 unknownOption = unknownOption_;
25780 self.addOption = function(value, element) {
25781 assertNotHasOwnProperty(value, '"option value"');
25782 optionsMap[value] = true;
25784 if (ngModelCtrl.$viewValue == value) {
25785 $element.val(value);
25786 if (unknownOption.parent()) unknownOption.remove();
25788 // Workaround for https://code.google.com/p/chromium/issues/detail?id=381459
25789 // Adding an <option selected="selected"> element to a <select required="required"> should
25790 // automatically select the new element
25791 if (element && element[0].hasAttribute('selected')) {
25792 element[0].selected = true;
25797 self.removeOption = function(value) {
25798 if (this.hasOption(value)) {
25799 delete optionsMap[value];
25800 if (ngModelCtrl.$viewValue === value) {
25801 this.renderUnknownOption(value);
25807 self.renderUnknownOption = function(val) {
25808 var unknownVal = '? ' + hashKey(val) + ' ?';
25809 unknownOption.val(unknownVal);
25810 $element.prepend(unknownOption);
25811 $element.val(unknownVal);
25812 unknownOption.prop('selected', true); // needed for IE
25816 self.hasOption = function(value) {
25817 return optionsMap.hasOwnProperty(value);
25820 $scope.$on('$destroy', function() {
25821 // disable unknown option so that we don't do work when the whole select is being destroyed
25822 self.renderUnknownOption = noop;
25826 link: function(scope, element, attr, ctrls) {
25827 // if ngModel is not defined, we don't need to do anything
25828 if (!ctrls[1]) return;
25830 var selectCtrl = ctrls[0],
25831 ngModelCtrl = ctrls[1],
25832 multiple = attr.multiple,
25833 optionsExp = attr.ngOptions,
25834 nullOption = false, // if false, user will not be able to select it (used by ngOptions)
25836 renderScheduled = false,
25837 // we can't just jqLite('<option>') since jqLite is not smart enough
25838 // to create it in <select> and IE barfs otherwise.
25839 optionTemplate = jqLite(document.createElement('option')),
25840 optGroupTemplate =jqLite(document.createElement('optgroup')),
25841 unknownOption = optionTemplate.clone();
25843 // find "null" option
25844 for (var i = 0, children = element.children(), ii = children.length; i < ii; i++) {
25845 if (children[i].value === '') {
25846 emptyOption = nullOption = children.eq(i);
25851 selectCtrl.init(ngModelCtrl, nullOption, unknownOption);
25853 // required validator
25855 ngModelCtrl.$isEmpty = function(value) {
25856 return !value || value.length === 0;
25860 if (optionsExp) setupAsOptions(scope, element, ngModelCtrl);
25861 else if (multiple) setupAsMultiple(scope, element, ngModelCtrl);
25862 else setupAsSingle(scope, element, ngModelCtrl, selectCtrl);
25865 ////////////////////////////
25869 function setupAsSingle(scope, selectElement, ngModelCtrl, selectCtrl) {
25870 ngModelCtrl.$render = function() {
25871 var viewValue = ngModelCtrl.$viewValue;
25873 if (selectCtrl.hasOption(viewValue)) {
25874 if (unknownOption.parent()) unknownOption.remove();
25875 selectElement.val(viewValue);
25876 if (viewValue === '') emptyOption.prop('selected', true); // to make IE9 happy
25878 if (viewValue == null && emptyOption) {
25879 selectElement.val('');
25881 selectCtrl.renderUnknownOption(viewValue);
25886 selectElement.on('change', function() {
25887 scope.$apply(function() {
25888 if (unknownOption.parent()) unknownOption.remove();
25889 ngModelCtrl.$setViewValue(selectElement.val());
25894 function setupAsMultiple(scope, selectElement, ctrl) {
25896 ctrl.$render = function() {
25897 var items = new HashMap(ctrl.$viewValue);
25898 forEach(selectElement.find('option'), function(option) {
25899 option.selected = isDefined(items.get(option.value));
25903 // we have to do it on each watch since ngModel watches reference, but
25904 // we need to work of an array, so we need to see if anything was inserted/removed
25905 scope.$watch(function selectMultipleWatch() {
25906 if (!equals(lastView, ctrl.$viewValue)) {
25907 lastView = shallowCopy(ctrl.$viewValue);
25912 selectElement.on('change', function() {
25913 scope.$apply(function() {
25915 forEach(selectElement.find('option'), function(option) {
25916 if (option.selected) {
25917 array.push(option.value);
25920 ctrl.$setViewValue(array);
25925 function setupAsOptions(scope, selectElement, ctrl) {
25928 if (!(match = optionsExp.match(NG_OPTIONS_REGEXP))) {
25929 throw ngOptionsMinErr('iexp',
25930 "Expected expression in form of " +
25931 "'_select_ (as _label_)? for (_key_,)?_value_ in _collection_'" +
25932 " but got '{0}'. Element: {1}",
25933 optionsExp, startingTag(selectElement));
25936 var displayFn = $parse(match[2] || match[1]),
25937 valueName = match[4] || match[6],
25938 selectAs = / as /.test(match[0]) && match[1],
25939 selectAsFn = selectAs ? $parse(selectAs) : null,
25940 keyName = match[5],
25941 groupByFn = $parse(match[3] || ''),
25942 valueFn = $parse(match[2] ? match[1] : valueName),
25943 valuesFn = $parse(match[7]),
25945 trackFn = track ? $parse(match[8]) : null,
25946 trackKeysCache = {},
25947 // This is an array of array of existing option groups in DOM.
25948 // We try to reuse these if possible
25949 // - optionGroupsCache[0] is the options with no option group
25950 // - optionGroupsCache[?][0] is the parent: either the SELECT or OPTGROUP element
25951 optionGroupsCache = [[{element: selectElement, label:''}]],
25952 //re-usable object to represent option's locals
25956 // compile the element since there might be bindings in it
25957 $compile(nullOption)(scope);
25959 // remove the class, which is added automatically because we recompile the element and it
25960 // becomes the compilation root
25961 nullOption.removeClass('ng-scope');
25963 // we need to remove it before calling selectElement.empty() because otherwise IE will
25964 // remove the label from the element. wtf?
25965 nullOption.remove();
25968 // clear contents, we'll add what's needed based on the model
25969 selectElement.empty();
25971 selectElement.on('change', selectionChanged);
25973 ctrl.$render = render;
25975 scope.$watchCollection(valuesFn, scheduleRendering);
25976 scope.$watchCollection(getLabels, scheduleRendering);
25979 scope.$watchCollection(function() { return ctrl.$modelValue; }, scheduleRendering);
25982 // ------------------------------------------------------------------ //
25984 function callExpression(exprFn, key, value) {
25985 locals[valueName] = value;
25986 if (keyName) locals[keyName] = key;
25987 return exprFn(scope, locals);
25990 function selectionChanged() {
25991 scope.$apply(function() {
25992 var collection = valuesFn(scope) || [];
25996 forEach(selectElement.val(), function(selectedKey) {
25997 selectedKey = trackFn ? trackKeysCache[selectedKey] : selectedKey;
25998 viewValue.push(getViewValue(selectedKey, collection[selectedKey]));
26001 var selectedKey = trackFn ? trackKeysCache[selectElement.val()] : selectElement.val();
26002 viewValue = getViewValue(selectedKey, collection[selectedKey]);
26004 ctrl.$setViewValue(viewValue);
26009 function getViewValue(key, value) {
26012 } else if (key === '') {
26015 var viewValueFn = selectAsFn ? selectAsFn : valueFn;
26016 return callExpression(viewValueFn, key, value);
26020 function getLabels() {
26021 var values = valuesFn(scope);
26023 if (values && isArray(values)) {
26024 toDisplay = new Array(values.length);
26025 for (var i = 0, ii = values.length; i < ii; i++) {
26026 toDisplay[i] = callExpression(displayFn, i, values[i]);
26029 } else if (values) {
26030 // TODO: Add a test for this case
26032 for (var prop in values) {
26033 if (values.hasOwnProperty(prop)) {
26034 toDisplay[prop] = callExpression(displayFn, prop, values[prop]);
26041 function createIsSelectedFn(viewValue) {
26044 if (trackFn && isArray(viewValue)) {
26046 selectedSet = new HashMap([]);
26047 for (var trackIndex = 0; trackIndex < viewValue.length; trackIndex++) {
26049 selectedSet.put(callExpression(trackFn, null, viewValue[trackIndex]), true);
26052 selectedSet = new HashMap(viewValue);
26054 } else if (trackFn) {
26055 viewValue = callExpression(trackFn, null, viewValue);
26058 return function isSelected(key, value) {
26059 var compareValueFn;
26061 compareValueFn = trackFn;
26062 } else if (selectAsFn) {
26063 compareValueFn = selectAsFn;
26065 compareValueFn = valueFn;
26069 return isDefined(selectedSet.remove(callExpression(compareValueFn, key, value)));
26071 return viewValue === callExpression(compareValueFn, key, value);
26076 function scheduleRendering() {
26077 if (!renderScheduled) {
26078 scope.$$postDigest(render);
26079 renderScheduled = true;
26084 * A new labelMap is created with each render.
26085 * This function is called for each existing option with added=false,
26086 * and each new option with added=true.
26087 * - Labels that are passed to this method twice,
26088 * (once with added=true and once with added=false) will end up with a value of 0, and
26089 * will cause no change to happen to the corresponding option.
26090 * - Labels that are passed to this method only once with added=false will end up with a
26091 * value of -1 and will eventually be passed to selectCtrl.removeOption()
26092 * - Labels that are passed to this method only once with added=true will end up with a
26093 * value of 1 and will eventually be passed to selectCtrl.addOption()
26095 function updateLabelMap(labelMap, label, added) {
26096 labelMap[label] = labelMap[label] || 0;
26097 labelMap[label] += (added ? 1 : -1);
26100 function render() {
26101 renderScheduled = false;
26103 // Temporary location for the option groups before we render them
26104 var optionGroups = {'':[]},
26105 optionGroupNames = [''],
26109 existingParent, existingOptions, existingOption,
26110 viewValue = ctrl.$viewValue,
26111 values = valuesFn(scope) || [],
26112 keys = keyName ? sortedKeys(values) : values,
26115 groupLength, length,
26119 isSelected = createIsSelectedFn(viewValue),
26120 anySelected = false,
26126 trackKeysCache = {};
26128 // We now build up the list of options we need (we merge later)
26129 for (index = 0; length = keys.length, index < length; index++) {
26133 if (key.charAt(0) === '$') continue;
26135 value = values[key];
26137 optionGroupName = callExpression(groupByFn, key, value) || '';
26138 if (!(optionGroup = optionGroups[optionGroupName])) {
26139 optionGroup = optionGroups[optionGroupName] = [];
26140 optionGroupNames.push(optionGroupName);
26143 selected = isSelected(key, value);
26144 anySelected = anySelected || selected;
26146 label = callExpression(displayFn, key, value); // what will be seen by the user
26148 // doing displayFn(scope, locals) || '' overwrites zero values
26149 label = isDefined(label) ? label : '';
26150 optionId = trackFn ? trackFn(scope, locals) : (keyName ? keys[index] : index);
26152 trackKeysCache[optionId] = key;
26156 // either the index into array or key from object
26159 selected: selected // determine if we should be selected
26163 if (nullOption || viewValue === null) {
26164 // insert null option if we have a placeholder, or the model is null
26165 optionGroups[''].unshift({id:'', label:'', selected:!anySelected});
26166 } else if (!anySelected) {
26167 // option could not be found, we have to insert the undefined item
26168 optionGroups[''].unshift({id:'?', label:'', selected:true});
26172 // Now we need to update the list of DOM nodes to match the optionGroups we computed above
26173 for (groupIndex = 0, groupLength = optionGroupNames.length;
26174 groupIndex < groupLength;
26176 // current option group name or '' if no group
26177 optionGroupName = optionGroupNames[groupIndex];
26179 // list of options for that group. (first item has the parent)
26180 optionGroup = optionGroups[optionGroupName];
26182 if (optionGroupsCache.length <= groupIndex) {
26183 // we need to grow the optionGroups
26185 element: optGroupTemplate.clone().attr('label', optionGroupName),
26186 label: optionGroup.label
26188 existingOptions = [existingParent];
26189 optionGroupsCache.push(existingOptions);
26190 selectElement.append(existingParent.element);
26192 existingOptions = optionGroupsCache[groupIndex];
26193 existingParent = existingOptions[0]; // either SELECT (no group) or OPTGROUP element
26195 // update the OPTGROUP label if not the same.
26196 if (existingParent.label != optionGroupName) {
26197 existingParent.element.attr('label', existingParent.label = optionGroupName);
26201 lastElement = null; // start at the beginning
26202 for (index = 0, length = optionGroup.length; index < length; index++) {
26203 option = optionGroup[index];
26204 if ((existingOption = existingOptions[index + 1])) {
26206 lastElement = existingOption.element;
26207 if (existingOption.label !== option.label) {
26208 updateLabelMap(labelMap, existingOption.label, false);
26209 updateLabelMap(labelMap, option.label, true);
26210 lastElement.text(existingOption.label = option.label);
26211 lastElement.prop('label', existingOption.label);
26213 if (existingOption.id !== option.id) {
26214 lastElement.val(existingOption.id = option.id);
26216 // lastElement.prop('selected') provided by jQuery has side-effects
26217 if (lastElement[0].selected !== option.selected) {
26218 lastElement.prop('selected', (existingOption.selected = option.selected));
26221 // The selected item wouldn't visually update on IE without this.
26222 // Tested on Win7: IE9, IE10 and IE11. Future IEs should be tested as well
26223 lastElement.prop('selected', existingOption.selected);
26229 // if it's a null option
26230 if (option.id === '' && nullOption) {
26231 // put back the pre-compiled element
26232 element = nullOption;
26234 // jQuery(v1.4.2) Bug: We should be able to chain the method calls, but
26235 // in this version of jQuery on some browser the .text() returns a string
26236 // rather then the element.
26237 (element = optionTemplate.clone())
26239 .prop('selected', option.selected)
26240 .attr('selected', option.selected)
26241 .prop('label', option.label)
26242 .text(option.label);
26245 existingOptions.push(existingOption = {
26247 label: option.label,
26249 selected: option.selected
26251 updateLabelMap(labelMap, option.label, true);
26253 lastElement.after(element);
26255 existingParent.element.append(element);
26257 lastElement = element;
26260 // remove any excessive OPTIONs in a group
26261 index++; // increment since the existingOptions[0] is parent element not OPTION
26262 while (existingOptions.length > index) {
26263 option = existingOptions.pop();
26264 updateLabelMap(labelMap, option.label, false);
26265 option.element.remove();
26268 // remove any excessive OPTGROUPs from select
26269 while (optionGroupsCache.length > groupIndex) {
26270 // remove all the labels in the option group
26271 optionGroup = optionGroupsCache.pop();
26272 for (index = 1; index < optionGroup.length; ++index) {
26273 updateLabelMap(labelMap, optionGroup[index].label, false);
26275 optionGroup[0].element.remove();
26277 forEach(labelMap, function(count, label) {
26279 selectCtrl.addOption(label);
26280 } else if (count < 0) {
26281 selectCtrl.removeOption(label);
26290 var optionDirective = ['$interpolate', function($interpolate) {
26291 var nullSelectCtrl = {
26299 compile: function(element, attr) {
26300 if (isUndefined(attr.value)) {
26301 var interpolateFn = $interpolate(element.text(), true);
26302 if (!interpolateFn) {
26303 attr.$set('value', element.text());
26307 return function(scope, element, attr) {
26308 var selectCtrlName = '$selectController',
26309 parent = element.parent(),
26310 selectCtrl = parent.data(selectCtrlName) ||
26311 parent.parent().data(selectCtrlName); // in case we are in optgroup
26313 if (!selectCtrl || !selectCtrl.databound) {
26314 selectCtrl = nullSelectCtrl;
26317 if (interpolateFn) {
26318 scope.$watch(interpolateFn, function interpolateWatchAction(newVal, oldVal) {
26319 attr.$set('value', newVal);
26320 if (oldVal !== newVal) {
26321 selectCtrl.removeOption(oldVal);
26323 selectCtrl.addOption(newVal, element);
26326 selectCtrl.addOption(attr.value, element);
26329 element.on('$destroy', function() {
26330 selectCtrl.removeOption(attr.value);
26337 var styleDirective = valueFn({
26342 var requiredDirective = function() {
26345 require: '?ngModel',
26346 link: function(scope, elm, attr, ctrl) {
26348 attr.required = true; // force truthy in case we are on non input element
26350 ctrl.$validators.required = function(modelValue, viewValue) {
26351 return !attr.required || !ctrl.$isEmpty(viewValue);
26354 attr.$observe('required', function() {
26362 var patternDirective = function() {
26365 require: '?ngModel',
26366 link: function(scope, elm, attr, ctrl) {
26369 var regexp, patternExp = attr.ngPattern || attr.pattern;
26370 attr.$observe('pattern', function(regex) {
26371 if (isString(regex) && regex.length > 0) {
26372 regex = new RegExp('^' + regex + '$');
26375 if (regex && !regex.test) {
26376 throw minErr('ngPattern')('noregexp',
26377 'Expected {0} to be a RegExp but was {1}. Element: {2}', patternExp,
26378 regex, startingTag(elm));
26381 regexp = regex || undefined;
26385 ctrl.$validators.pattern = function(modelValue, viewValue) {
26386 // HTML5 pattern constraint validates the input value, so we validate the viewValue
26387 return ctrl.$isEmpty(viewValue) || isUndefined(regexp) || regexp.test(viewValue);
26394 var maxlengthDirective = function() {
26397 require: '?ngModel',
26398 link: function(scope, elm, attr, ctrl) {
26401 var maxlength = -1;
26402 attr.$observe('maxlength', function(value) {
26403 var intVal = int(value);
26404 maxlength = isNaN(intVal) ? -1 : intVal;
26407 ctrl.$validators.maxlength = function(modelValue, viewValue) {
26408 return (maxlength < 0) || ctrl.$isEmpty(viewValue) || (viewValue.length <= maxlength);
26414 var minlengthDirective = function() {
26417 require: '?ngModel',
26418 link: function(scope, elm, attr, ctrl) {
26422 attr.$observe('minlength', function(value) {
26423 minlength = int(value) || 0;
26426 ctrl.$validators.minlength = function(modelValue, viewValue) {
26427 return ctrl.$isEmpty(viewValue) || viewValue.length >= minlength;
26433 if (window.angular.bootstrap) {
26434 //AngularJS is already loaded, so we can return here...
26435 console.log('WARNING: Tried to load angular more than once.');
26439 //try to bind to jquery now so that one can write jqLite(document).ready()
26440 //but we will rebind on bootstrap again.
26443 publishExternalAPI(angular);
26445 jqLite(document).ready(function() {
26446 angularInit(document, bootstrap);
26449 })(window, document);
26451 !window.angular.$$csp() && 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;}</style>');