2 * @license AngularJS v1.3.15
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.15/' +
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_TEXT: true,
153 NODE_TYPE_COMMENT: true,
154 NODE_TYPE_DOCUMENT: true,
155 NODE_TYPE_DOCUMENT_FRAGMENT: true,
158 ////////////////////////////////////
167 * The ng module is loaded by default when an AngularJS application is started. The module itself
168 * contains the essential components for an AngularJS application to function. The table below
169 * lists a high level breakdown of each of the services/factories, filters, directives and testing
170 * components available within this core module.
172 * <div doc-module-components="ng"></div>
175 var REGEX_STRING_REGEXP = /^\/(.+)\/([a-z]*)$/;
177 // The name of a form control's ValidityState property.
178 // This is used so that it's possible for internal tests to create mock ValidityStates.
179 var VALIDITY_STATE_PROPERTY = 'validity';
183 * @name angular.lowercase
187 * @description Converts the specified string to lowercase.
188 * @param {string} string String to be converted to lowercase.
189 * @returns {string} Lowercased string.
191 var lowercase = function(string) {return isString(string) ? string.toLowerCase() : string;};
192 var hasOwnProperty = Object.prototype.hasOwnProperty;
196 * @name angular.uppercase
200 * @description Converts the specified string to uppercase.
201 * @param {string} string String to be converted to uppercase.
202 * @returns {string} Uppercased string.
204 var uppercase = function(string) {return isString(string) ? string.toUpperCase() : string;};
207 var manualLowercase = function(s) {
208 /* jshint bitwise: false */
210 ? s.replace(/[A-Z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) | 32);})
213 var manualUppercase = function(s) {
214 /* jshint bitwise: false */
216 ? s.replace(/[a-z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) & ~32);})
221 // String#toLowerCase and String#toUpperCase don't produce correct results in browsers with Turkish
222 // locale, for this reason we need to detect this case and redefine lowercase/uppercase methods
223 // with correct but slower alternatives.
224 if ('i' !== 'I'.toLowerCase()) {
225 lowercase = manualLowercase;
226 uppercase = manualUppercase;
231 msie, // holds major version number for IE, or NaN if UA is not IE.
232 jqLite, // delay binding since jQuery could be loaded after us.
233 jQuery, // delay binding
237 toString = Object.prototype.toString,
238 ngMinErr = minErr('ng'),
241 angular = window.angular || (window.angular = {}),
246 * documentMode is an IE-only property
247 * http://msdn.microsoft.com/en-us/library/ie/cc196988(v=vs.85).aspx
249 msie = document.documentMode;
255 * @return {boolean} Returns true if `obj` is an array or array-like object (NodeList, Arguments,
258 function isArrayLike(obj) {
259 if (obj == null || isWindow(obj)) {
263 var length = obj.length;
265 if (obj.nodeType === NODE_TYPE_ELEMENT && length) {
269 return isString(obj) || isArray(obj) || length === 0 ||
270 typeof length === 'number' && length > 0 && (length - 1) in obj;
275 * @name angular.forEach
280 * Invokes the `iterator` function once for each item in `obj` collection, which can be either an
281 * object or an array. The `iterator` function is invoked with `iterator(value, key, obj)`, where `value`
282 * is the value of an object property or an array element, `key` is the object property key or
283 * array element index and obj is the `obj` itself. Specifying a `context` for the function is optional.
285 * It is worth noting that `.forEach` does not iterate over inherited properties because it filters
286 * using the `hasOwnProperty` method.
289 * [Array.prototype.forEach](http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.18),
290 * Providing 'undefined' or 'null' values for `obj` will not throw a TypeError, but rather just
291 * return the value provided.
294 var values = {name: 'misko', gender: 'male'};
296 angular.forEach(values, function(value, key) {
297 this.push(key + ': ' + value);
299 expect(log).toEqual(['name: misko', 'gender: male']);
302 * @param {Object|Array} obj Object to iterate over.
303 * @param {Function} iterator Iterator function.
304 * @param {Object=} context Object to become context (`this`) for the iterator function.
305 * @returns {Object|Array} Reference to `obj`.
308 function forEach(obj, iterator, context) {
311 if (isFunction(obj)) {
313 // Need to check if hasOwnProperty exists,
314 // as on IE8 the result of querySelectorAll is an object without a hasOwnProperty function
315 if (key != 'prototype' && key != 'length' && key != 'name' && (!obj.hasOwnProperty || obj.hasOwnProperty(key))) {
316 iterator.call(context, obj[key], key, obj);
319 } else if (isArray(obj) || isArrayLike(obj)) {
320 var isPrimitive = typeof obj !== 'object';
321 for (key = 0, length = obj.length; key < length; key++) {
322 if (isPrimitive || key in obj) {
323 iterator.call(context, obj[key], key, obj);
326 } else if (obj.forEach && obj.forEach !== forEach) {
327 obj.forEach(iterator, context, obj);
330 if (obj.hasOwnProperty(key)) {
331 iterator.call(context, obj[key], key, obj);
339 function sortedKeys(obj) {
340 return Object.keys(obj).sort();
343 function forEachSorted(obj, iterator, context) {
344 var keys = sortedKeys(obj);
345 for (var i = 0; i < keys.length; i++) {
346 iterator.call(context, obj[keys[i]], keys[i]);
353 * when using forEach the params are value, key, but it is often useful to have key, value.
354 * @param {function(string, *)} iteratorFn
355 * @returns {function(*, string)}
357 function reverseParams(iteratorFn) {
358 return function(value, key) { iteratorFn(key, value); };
362 * A consistent way of creating unique IDs in angular.
364 * Using simple numbers allows us to generate 28.6 million unique ids per second for 10 years before
365 * we hit number precision issues in JavaScript.
367 * Math.pow(2,53) / 60 / 60 / 24 / 365 / 10 = 28.6M
369 * @returns {number} an unique alpha-numeric string
377 * Set or clear the hashkey for an object.
379 * @param h the hashkey (!truthy to delete the hashkey)
381 function setHashKey(obj, h) {
385 delete obj.$$hashKey;
391 * @name angular.extend
396 * Extends the destination object `dst` by copying own enumerable properties from the `src` object(s)
397 * to `dst`. You can specify multiple `src` objects. If you want to preserve original objects, you can do so
398 * by passing an empty object as the target: `var object = angular.extend({}, object1, object2)`.
399 * Note: Keep in mind that `angular.extend` does not support recursive merge (deep copy).
401 * @param {Object} dst Destination object.
402 * @param {...Object} src Source object(s).
403 * @returns {Object} Reference to `dst`.
405 function extend(dst) {
406 var h = dst.$$hashKey;
408 for (var i = 1, ii = arguments.length; i < ii; i++) {
409 var obj = arguments[i];
411 var keys = Object.keys(obj);
412 for (var j = 0, jj = keys.length; j < jj; j++) {
424 return parseInt(str, 10);
428 function inherit(parent, extra) {
429 return extend(Object.create(parent), extra);
439 * A function that performs no operations. This function can be useful when writing code in the
442 function foo(callback) {
443 var result = calculateResult();
444 (callback || angular.noop)(result);
454 * @name angular.identity
459 * A function that returns its first argument. This function is useful when writing code in the
463 function transformer(transformationFn, value) {
464 return (transformationFn || angular.identity)(value);
467 * @param {*} value to be returned.
468 * @returns {*} the value passed in.
470 function identity($) {return $;}
471 identity.$inject = [];
474 function valueFn(value) {return function() {return value;};}
478 * @name angular.isUndefined
483 * Determines if a reference is undefined.
485 * @param {*} value Reference to check.
486 * @returns {boolean} True if `value` is undefined.
488 function isUndefined(value) {return typeof value === 'undefined';}
493 * @name angular.isDefined
498 * Determines if a reference is defined.
500 * @param {*} value Reference to check.
501 * @returns {boolean} True if `value` is defined.
503 function isDefined(value) {return typeof value !== 'undefined';}
508 * @name angular.isObject
513 * Determines if a reference is an `Object`. Unlike `typeof` in JavaScript, `null`s are not
514 * considered to be objects. Note that JavaScript arrays are objects.
516 * @param {*} value Reference to check.
517 * @returns {boolean} True if `value` is an `Object` but not `null`.
519 function isObject(value) {
520 // http://jsperf.com/isobject4
521 return value !== null && typeof value === 'object';
527 * @name angular.isString
532 * Determines if a reference is a `String`.
534 * @param {*} value Reference to check.
535 * @returns {boolean} True if `value` is a `String`.
537 function isString(value) {return typeof value === 'string';}
542 * @name angular.isNumber
547 * Determines if a reference is a `Number`.
549 * This includes the "special" numbers `NaN`, `+Infinity` and `-Infinity`.
551 * If you wish to exclude these then you can use the native
552 * [`isFinite'](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/isFinite)
555 * @param {*} value Reference to check.
556 * @returns {boolean} True if `value` is a `Number`.
558 function isNumber(value) {return typeof value === 'number';}
563 * @name angular.isDate
568 * Determines if a value is a date.
570 * @param {*} value Reference to check.
571 * @returns {boolean} True if `value` is a `Date`.
573 function isDate(value) {
574 return toString.call(value) === '[object Date]';
580 * @name angular.isArray
585 * Determines if a reference is an `Array`.
587 * @param {*} value Reference to check.
588 * @returns {boolean} True if `value` is an `Array`.
590 var isArray = Array.isArray;
594 * @name angular.isFunction
599 * Determines if a reference is a `Function`.
601 * @param {*} value Reference to check.
602 * @returns {boolean} True if `value` is a `Function`.
604 function isFunction(value) {return typeof value === 'function';}
608 * Determines if a value is a regular expression object.
611 * @param {*} value Reference to check.
612 * @returns {boolean} True if `value` is a `RegExp`.
614 function isRegExp(value) {
615 return toString.call(value) === '[object RegExp]';
620 * Checks if `obj` is a window object.
623 * @param {*} obj Object to check
624 * @returns {boolean} True if `obj` is a window obj.
626 function isWindow(obj) {
627 return obj && obj.window === obj;
631 function isScope(obj) {
632 return obj && obj.$evalAsync && obj.$watch;
636 function isFile(obj) {
637 return toString.call(obj) === '[object File]';
641 function isFormData(obj) {
642 return toString.call(obj) === '[object FormData]';
646 function isBlob(obj) {
647 return toString.call(obj) === '[object Blob]';
651 function isBoolean(value) {
652 return typeof value === 'boolean';
656 function isPromiseLike(obj) {
657 return obj && isFunction(obj.then);
661 var trim = function(value) {
662 return isString(value) ? value.trim() : value;
666 // http://docs.closure-library.googlecode.com/git/local_closure_goog_string_string.js.source.html#line1021
667 // Prereq: s is a string.
668 var escapeForRegexp = function(s) {
669 return s.replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g, '\\$1').
670 replace(/\x08/g, '\\x08');
676 * @name angular.isElement
681 * Determines if a reference is a DOM element (or wrapped jQuery element).
683 * @param {*} value Reference to check.
684 * @returns {boolean} True if `value` is a DOM element (or wrapped jQuery element).
686 function isElement(node) {
688 (node.nodeName // we are a direct element
689 || (node.prop && node.attr && node.find))); // we have an on and find method part of jQuery API
693 * @param str 'key1,key2,...'
694 * @returns {object} in the form of {key1:true, key2:true, ...}
696 function makeMap(str) {
697 var obj = {}, items = str.split(","), i;
698 for (i = 0; i < items.length; i++)
699 obj[items[i]] = true;
704 function nodeName_(element) {
705 return lowercase(element.nodeName || (element[0] && element[0].nodeName));
708 function includes(array, obj) {
709 return Array.prototype.indexOf.call(array, obj) != -1;
712 function arrayRemove(array, value) {
713 var index = array.indexOf(value);
715 array.splice(index, 1);
726 * Creates a deep copy of `source`, which should be an object or an array.
728 * * If no destination is supplied, a copy of the object or array is created.
729 * * If a destination is provided, all of its elements (for arrays) or properties (for objects)
730 * are deleted and then all elements/properties from the source are copied to it.
731 * * If `source` is not an object or array (inc. `null` and `undefined`), `source` is returned.
732 * * If `source` is identical to 'destination' an exception will be thrown.
734 * @param {*} source The source that will be used to make a copy.
735 * Can be any type, including primitives, `null`, and `undefined`.
736 * @param {(Object|Array)=} destination Destination into which the source is copied. If
737 * provided, must be of the same type as `source`.
738 * @returns {*} The copy or updated `destination`, if `destination` was specified.
741 <example module="copyExample">
742 <file name="index.html">
743 <div ng-controller="ExampleController">
744 <form novalidate class="simple-form">
745 Name: <input type="text" ng-model="user.name" /><br />
746 E-mail: <input type="email" ng-model="user.email" /><br />
747 Gender: <input type="radio" ng-model="user.gender" value="male" />male
748 <input type="radio" ng-model="user.gender" value="female" />female<br />
749 <button ng-click="reset()">RESET</button>
750 <button ng-click="update(user)">SAVE</button>
752 <pre>form = {{user | json}}</pre>
753 <pre>master = {{master | json}}</pre>
757 angular.module('copyExample', [])
758 .controller('ExampleController', ['$scope', function($scope) {
761 $scope.update = function(user) {
762 // Example with 1 argument
763 $scope.master= angular.copy(user);
766 $scope.reset = function() {
767 // Example with 2 arguments
768 angular.copy($scope.master, $scope.user);
777 function copy(source, destination, stackSource, stackDest) {
778 if (isWindow(source) || isScope(source)) {
779 throw ngMinErr('cpws',
780 "Can't copy! Making copies of Window or Scope instances is not supported.");
784 destination = source;
786 if (isArray(source)) {
787 destination = copy(source, [], stackSource, stackDest);
788 } else if (isDate(source)) {
789 destination = new Date(source.getTime());
790 } else if (isRegExp(source)) {
791 destination = new RegExp(source.source, source.toString().match(/[^\/]*$/)[0]);
792 destination.lastIndex = source.lastIndex;
793 } else if (isObject(source)) {
794 var emptyObject = Object.create(Object.getPrototypeOf(source));
795 destination = copy(source, emptyObject, stackSource, stackDest);
799 if (source === destination) throw ngMinErr('cpi',
800 "Can't copy! Source and destination are identical.");
802 stackSource = stackSource || [];
803 stackDest = stackDest || [];
805 if (isObject(source)) {
806 var index = stackSource.indexOf(source);
807 if (index !== -1) return stackDest[index];
809 stackSource.push(source);
810 stackDest.push(destination);
814 if (isArray(source)) {
815 destination.length = 0;
816 for (var i = 0; i < source.length; i++) {
817 result = copy(source[i], null, stackSource, stackDest);
818 if (isObject(source[i])) {
819 stackSource.push(source[i]);
820 stackDest.push(result);
822 destination.push(result);
825 var h = destination.$$hashKey;
826 if (isArray(destination)) {
827 destination.length = 0;
829 forEach(destination, function(value, key) {
830 delete destination[key];
833 for (var key in source) {
834 if (source.hasOwnProperty(key)) {
835 result = copy(source[key], null, stackSource, stackDest);
836 if (isObject(source[key])) {
837 stackSource.push(source[key]);
838 stackDest.push(result);
840 destination[key] = result;
843 setHashKey(destination,h);
851 * Creates a shallow copy of an object, an array or a primitive.
853 * Assumes that there are no proto properties for objects.
855 function shallowCopy(src, dst) {
859 for (var i = 0, ii = src.length; i < ii; i++) {
862 } else if (isObject(src)) {
865 for (var key in src) {
866 if (!(key.charAt(0) === '$' && key.charAt(1) === '$')) {
878 * @name angular.equals
883 * Determines if two objects or two values are equivalent. Supports value types, regular
884 * expressions, arrays and objects.
886 * Two objects or values are considered equivalent if at least one of the following is true:
888 * * Both objects or values pass `===` comparison.
889 * * Both objects or values are of the same type and all of their properties are equal by
890 * comparing them with `angular.equals`.
891 * * Both values are NaN. (In JavaScript, NaN == NaN => false. But we consider two NaN as equal)
892 * * Both values represent the same regular expression (In JavaScript,
893 * /abc/ == /abc/ => false. But we consider two regular expressions as equal when their textual
894 * representation matches).
896 * During a property comparison, properties of `function` type and properties with names
897 * that begin with `$` are ignored.
899 * Scope and DOMWindow objects are being compared only by identify (`===`).
901 * @param {*} o1 Object or value to compare.
902 * @param {*} o2 Object or value to compare.
903 * @returns {boolean} True if arguments are equal.
905 function equals(o1, o2) {
906 if (o1 === o2) return true;
907 if (o1 === null || o2 === null) return false;
908 if (o1 !== o1 && o2 !== o2) return true; // NaN === NaN
909 var t1 = typeof o1, t2 = typeof o2, length, key, keySet;
911 if (t1 == 'object') {
913 if (!isArray(o2)) return false;
914 if ((length = o1.length) == o2.length) {
915 for (key = 0; key < length; key++) {
916 if (!equals(o1[key], o2[key])) return false;
920 } else if (isDate(o1)) {
921 if (!isDate(o2)) return false;
922 return equals(o1.getTime(), o2.getTime());
923 } else if (isRegExp(o1)) {
924 return isRegExp(o2) ? o1.toString() == o2.toString() : false;
926 if (isScope(o1) || isScope(o2) || isWindow(o1) || isWindow(o2) ||
927 isArray(o2) || isDate(o2) || isRegExp(o2)) return false;
930 if (key.charAt(0) === '$' || isFunction(o1[key])) continue;
931 if (!equals(o1[key], o2[key])) return false;
935 if (!keySet.hasOwnProperty(key) &&
936 key.charAt(0) !== '$' &&
937 o2[key] !== undefined &&
938 !isFunction(o2[key])) return false;
947 var csp = function() {
948 if (isDefined(csp.isActive_)) return csp.isActive_;
950 var active = !!(document.querySelector('[ng-csp]') ||
951 document.querySelector('[data-ng-csp]'));
955 /* jshint -W031, -W054 */
957 /* jshint +W031, +W054 */
963 return (csp.isActive_ = active);
968 function concat(array1, array2, index) {
969 return array1.concat(slice.call(array2, index));
972 function sliceArgs(args, startIndex) {
973 return slice.call(args, startIndex || 0);
985 * Returns a function which calls function `fn` bound to `self` (`self` becomes the `this` for
986 * `fn`). You can supply optional `args` that are prebound to the function. This feature is also
987 * known as [partial application](http://en.wikipedia.org/wiki/Partial_application), as
988 * distinguished from [function currying](http://en.wikipedia.org/wiki/Currying#Contrast_with_partial_function_application).
990 * @param {Object} self Context which `fn` should be evaluated in.
991 * @param {function()} fn Function to be bound.
992 * @param {...*} args Optional arguments to be prebound to the `fn` function call.
993 * @returns {function()} Function that wraps the `fn` with all the specified bindings.
996 function bind(self, fn) {
997 var curryArgs = arguments.length > 2 ? sliceArgs(arguments, 2) : [];
998 if (isFunction(fn) && !(fn instanceof RegExp)) {
999 return curryArgs.length
1001 return arguments.length
1002 ? fn.apply(self, concat(curryArgs, arguments, 0))
1003 : fn.apply(self, curryArgs);
1006 return arguments.length
1007 ? fn.apply(self, arguments)
1011 // in IE, native methods are not functions so they cannot be bound (note: they don't need to be)
1017 function toJsonReplacer(key, value) {
1020 if (typeof key === 'string' && key.charAt(0) === '$' && key.charAt(1) === '$') {
1022 } else if (isWindow(value)) {
1024 } else if (value && document === value) {
1026 } else if (isScope(value)) {
1036 * @name angular.toJson
1041 * Serializes input into a JSON-formatted string. Properties with leading $$ characters will be
1042 * stripped since angular uses this notation internally.
1044 * @param {Object|Array|Date|string|number} obj Input to be serialized into JSON.
1045 * @param {boolean|number=} pretty If set to true, the JSON output will contain newlines and whitespace.
1046 * If set to an integer, the JSON output will contain that many spaces per indentation (the default is 2).
1047 * @returns {string|undefined} JSON-ified string representing `obj`.
1049 function toJson(obj, pretty) {
1050 if (typeof obj === 'undefined') return undefined;
1051 if (!isNumber(pretty)) {
1052 pretty = pretty ? 2 : null;
1054 return JSON.stringify(obj, toJsonReplacer, pretty);
1060 * @name angular.fromJson
1065 * Deserializes a JSON string.
1067 * @param {string} json JSON string to deserialize.
1068 * @returns {Object|Array|string|number} Deserialized JSON string.
1070 function fromJson(json) {
1071 return isString(json)
1078 * @returns {string} Returns the string representation of the element.
1080 function startingTag(element) {
1081 element = jqLite(element).clone();
1083 // turns out IE does not let you set .html() on elements which
1084 // are not allowed to have children. So we just ignore it.
1087 var elemHtml = jqLite('<div>').append(element).html();
1089 return element[0].nodeType === NODE_TYPE_TEXT ? lowercase(elemHtml) :
1091 match(/^(<[^>]+>)/)[1].
1092 replace(/^<([\w\-]+)/, function(match, nodeName) { return '<' + lowercase(nodeName); });
1094 return lowercase(elemHtml);
1100 /////////////////////////////////////////////////
1103 * Tries to decode the URI component without throwing an exception.
1106 * @param str value potential URI component to check.
1107 * @returns {boolean} True if `value` can be decoded
1108 * with the decodeURIComponent function.
1110 function tryDecodeURIComponent(value) {
1112 return decodeURIComponent(value);
1114 // Ignore any invalid uri component
1120 * Parses an escaped url query string into key-value pairs.
1121 * @returns {Object.<string,boolean|Array>}
1123 function parseKeyValue(/**string*/keyValue) {
1124 var obj = {}, key_value, key;
1125 forEach((keyValue || "").split('&'), function(keyValue) {
1127 key_value = keyValue.replace(/\+/g,'%20').split('=');
1128 key = tryDecodeURIComponent(key_value[0]);
1129 if (isDefined(key)) {
1130 var val = isDefined(key_value[1]) ? tryDecodeURIComponent(key_value[1]) : true;
1131 if (!hasOwnProperty.call(obj, key)) {
1133 } else if (isArray(obj[key])) {
1136 obj[key] = [obj[key],val];
1144 function toKeyValue(obj) {
1146 forEach(obj, function(value, key) {
1147 if (isArray(value)) {
1148 forEach(value, function(arrayValue) {
1149 parts.push(encodeUriQuery(key, true) +
1150 (arrayValue === true ? '' : '=' + encodeUriQuery(arrayValue, true)));
1153 parts.push(encodeUriQuery(key, true) +
1154 (value === true ? '' : '=' + encodeUriQuery(value, true)));
1157 return parts.length ? parts.join('&') : '';
1162 * We need our custom method because encodeURIComponent is too aggressive and doesn't follow
1163 * http://www.ietf.org/rfc/rfc3986.txt with regards to the character set (pchar) allowed in path
1166 * pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
1167 * pct-encoded = "%" HEXDIG HEXDIG
1168 * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
1169 * sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
1170 * / "*" / "+" / "," / ";" / "="
1172 function encodeUriSegment(val) {
1173 return encodeUriQuery(val, true).
1174 replace(/%26/gi, '&').
1175 replace(/%3D/gi, '=').
1176 replace(/%2B/gi, '+');
1181 * This method is intended for encoding *key* or *value* parts of query component. We need a custom
1182 * method because encodeURIComponent is too aggressive and encodes stuff that doesn't have to be
1183 * encoded per http://tools.ietf.org/html/rfc3986:
1184 * query = *( pchar / "/" / "?" )
1185 * pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
1186 * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
1187 * pct-encoded = "%" HEXDIG HEXDIG
1188 * sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
1189 * / "*" / "+" / "," / ";" / "="
1191 function encodeUriQuery(val, pctEncodeSpaces) {
1192 return encodeURIComponent(val).
1193 replace(/%40/gi, '@').
1194 replace(/%3A/gi, ':').
1195 replace(/%24/g, '$').
1196 replace(/%2C/gi, ',').
1197 replace(/%3B/gi, ';').
1198 replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
1201 var ngAttrPrefixes = ['ng-', 'data-ng-', 'ng:', 'x-ng-'];
1203 function getNgAttribute(element, ngAttr) {
1204 var attr, i, ii = ngAttrPrefixes.length;
1205 element = jqLite(element);
1206 for (i = 0; i < ii; ++i) {
1207 attr = ngAttrPrefixes[i] + ngAttr;
1208 if (isString(attr = element.attr(attr))) {
1221 * @param {angular.Module} ngApp an optional application
1222 * {@link angular.module module} name to load.
1223 * @param {boolean=} ngStrictDi if this attribute is present on the app element, the injector will be
1224 * created in "strict-di" mode. This means that the application will fail to invoke functions which
1225 * do not use explicit function annotation (and are thus unsuitable for minification), as described
1226 * in {@link guide/di the Dependency Injection guide}, and useful debugging info will assist in
1227 * tracking down the root of these bugs.
1231 * Use this directive to **auto-bootstrap** an AngularJS application. The `ngApp` directive
1232 * designates the **root element** of the application and is typically placed near the root element
1233 * of the page - e.g. on the `<body>` or `<html>` tags.
1235 * Only one AngularJS application can be auto-bootstrapped per HTML document. The first `ngApp`
1236 * found in the document will be used to define the root element to auto-bootstrap as an
1237 * application. To run multiple applications in an HTML document you must manually bootstrap them using
1238 * {@link angular.bootstrap} instead. AngularJS applications cannot be nested within each other.
1240 * You can specify an **AngularJS module** to be used as the root module for the application. This
1241 * module will be loaded into the {@link auto.$injector} when the application is bootstrapped. It
1242 * should contain the application code needed or have dependencies on other modules that will
1243 * contain the code. See {@link angular.module} for more information.
1245 * In the example below if the `ngApp` directive were not placed on the `html` element then the
1246 * document would not be compiled, the `AppController` would not be instantiated and the `{{ a+b }}`
1247 * would not be resolved to `3`.
1249 * `ngApp` is the easiest, and most common way to bootstrap an application.
1251 <example module="ngAppDemo">
1252 <file name="index.html">
1253 <div ng-controller="ngAppDemoController">
1254 I can add: {{a}} + {{b}} = {{ a+b }}
1257 <file name="script.js">
1258 angular.module('ngAppDemo', []).controller('ngAppDemoController', function($scope) {
1265 * Using `ngStrictDi`, you would see something like this:
1267 <example ng-app-included="true">
1268 <file name="index.html">
1269 <div ng-app="ngAppStrictDemo" ng-strict-di>
1270 <div ng-controller="GoodController1">
1271 I can add: {{a}} + {{b}} = {{ a+b }}
1273 <p>This renders because the controller does not fail to
1274 instantiate, by using explicit annotation style (see
1275 script.js for details)
1279 <div ng-controller="GoodController2">
1280 Name: <input ng-model="name"><br />
1283 <p>This renders because the controller does not fail to
1284 instantiate, by using explicit annotation style
1285 (see script.js for details)
1289 <div ng-controller="BadController">
1290 I can add: {{a}} + {{b}} = {{ a+b }}
1292 <p>The controller could not be instantiated, due to relying
1293 on automatic function annotations (which are disabled in
1294 strict mode). As such, the content of this section is not
1295 interpolated, and there should be an error in your web console.
1300 <file name="script.js">
1301 angular.module('ngAppStrictDemo', [])
1302 // BadController will fail to instantiate, due to relying on automatic function annotation,
1303 // rather than an explicit annotation
1304 .controller('BadController', function($scope) {
1308 // Unlike BadController, GoodController1 and GoodController2 will not fail to be instantiated,
1309 // due to using explicit annotations using the array style and $inject property, respectively.
1310 .controller('GoodController1', ['$scope', function($scope) {
1314 .controller('GoodController2', GoodController2);
1315 function GoodController2($scope) {
1316 $scope.name = "World";
1318 GoodController2.$inject = ['$scope'];
1320 <file name="style.css">
1321 div[ng-controller] {
1323 -webkit-border-radius: 4px;
1328 div[ng-controller^=Good] {
1329 border-color: #d6e9c6;
1330 background-color: #dff0d8;
1333 div[ng-controller^=Bad] {
1334 border-color: #ebccd1;
1335 background-color: #f2dede;
1342 function angularInit(element, bootstrap) {
1347 // The element `element` has priority over any other element
1348 forEach(ngAttrPrefixes, function(prefix) {
1349 var name = prefix + 'app';
1351 if (!appElement && element.hasAttribute && element.hasAttribute(name)) {
1352 appElement = element;
1353 module = element.getAttribute(name);
1356 forEach(ngAttrPrefixes, function(prefix) {
1357 var name = prefix + 'app';
1360 if (!appElement && (candidate = element.querySelector('[' + name.replace(':', '\\:') + ']'))) {
1361 appElement = candidate;
1362 module = candidate.getAttribute(name);
1366 config.strictDi = getNgAttribute(appElement, "strict-di") !== null;
1367 bootstrap(appElement, module ? [module] : [], config);
1373 * @name angular.bootstrap
1376 * Use this function to manually start up angular application.
1378 * See: {@link guide/bootstrap Bootstrap}
1380 * Note that Protractor based end-to-end tests cannot use this function to bootstrap manually.
1381 * They must use {@link ng.directive:ngApp ngApp}.
1383 * Angular will detect if it has been loaded into the browser more than once and only allow the
1384 * first loaded script to be bootstrapped and will report a warning to the browser console for
1385 * each of the subsequent scripts. This prevents strange results in applications, where otherwise
1386 * multiple instances of Angular try to work on the DOM.
1392 * <div ng-controller="WelcomeController">
1396 * <script src="angular.js"></script>
1398 * var app = angular.module('demo', [])
1399 * .controller('WelcomeController', function($scope) {
1400 * $scope.greeting = 'Welcome!';
1402 * angular.bootstrap(document, ['demo']);
1408 * @param {DOMElement} element DOM element which is the root of angular application.
1409 * @param {Array<String|Function|Array>=} modules an array of modules to load into the application.
1410 * Each item in the array should be the name of a predefined module or a (DI annotated)
1411 * function that will be invoked by the injector as a `config` block.
1412 * See: {@link angular.module modules}
1413 * @param {Object=} config an object for defining configuration options for the application. The
1414 * following keys are supported:
1416 * * `strictDi` - disable automatic function annotation for the application. This is meant to
1417 * assist in finding bugs which break minified code. Defaults to `false`.
1419 * @returns {auto.$injector} Returns the newly created injector for this app.
1421 function bootstrap(element, modules, config) {
1422 if (!isObject(config)) config = {};
1423 var defaultConfig = {
1426 config = extend(defaultConfig, config);
1427 var doBootstrap = function() {
1428 element = jqLite(element);
1430 if (element.injector()) {
1431 var tag = (element[0] === document) ? 'document' : startingTag(element);
1432 //Encode angle brackets to prevent input from being sanitized to empty string #8683
1435 "App Already Bootstrapped with this Element '{0}'",
1436 tag.replace(/</,'<').replace(/>/,'>'));
1439 modules = modules || [];
1440 modules.unshift(['$provide', function($provide) {
1441 $provide.value('$rootElement', element);
1444 if (config.debugInfoEnabled) {
1445 // Pushing so that this overrides `debugInfoEnabled` setting defined in user's `modules`.
1446 modules.push(['$compileProvider', function($compileProvider) {
1447 $compileProvider.debugInfoEnabled(true);
1451 modules.unshift('ng');
1452 var injector = createInjector(modules, config.strictDi);
1453 injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector',
1454 function bootstrapApply(scope, element, compile, injector) {
1455 scope.$apply(function() {
1456 element.data('$injector', injector);
1457 compile(element)(scope);
1464 var NG_ENABLE_DEBUG_INFO = /^NG_ENABLE_DEBUG_INFO!/;
1465 var NG_DEFER_BOOTSTRAP = /^NG_DEFER_BOOTSTRAP!/;
1467 if (window && NG_ENABLE_DEBUG_INFO.test(window.name)) {
1468 config.debugInfoEnabled = true;
1469 window.name = window.name.replace(NG_ENABLE_DEBUG_INFO, '');
1472 if (window && !NG_DEFER_BOOTSTRAP.test(window.name)) {
1473 return doBootstrap();
1476 window.name = window.name.replace(NG_DEFER_BOOTSTRAP, '');
1477 angular.resumeBootstrap = function(extraModules) {
1478 forEach(extraModules, function(module) {
1479 modules.push(module);
1481 return doBootstrap();
1484 if (isFunction(angular.resumeDeferredBootstrap)) {
1485 angular.resumeDeferredBootstrap();
1491 * @name angular.reloadWithDebugInfo
1494 * Use this function to reload the current application with debug information turned on.
1495 * This takes precedence over a call to `$compileProvider.debugInfoEnabled(false)`.
1497 * See {@link ng.$compileProvider#debugInfoEnabled} for more.
1499 function reloadWithDebugInfo() {
1500 window.name = 'NG_ENABLE_DEBUG_INFO!' + window.name;
1501 window.location.reload();
1505 * @name angular.getTestability
1508 * Get the testability service for the instance of Angular on the given
1510 * @param {DOMElement} element DOM element which is the root of angular application.
1512 function getTestability(rootElement) {
1513 var injector = angular.element(rootElement).injector();
1515 throw ngMinErr('test',
1516 'no injector found for element argument to getTestability');
1518 return injector.get('$$testability');
1521 var SNAKE_CASE_REGEXP = /[A-Z]/g;
1522 function snake_case(name, separator) {
1523 separator = separator || '_';
1524 return name.replace(SNAKE_CASE_REGEXP, function(letter, pos) {
1525 return (pos ? separator : '') + letter.toLowerCase();
1529 var bindJQueryFired = false;
1530 var skipDestroyOnNextJQueryCleanData;
1531 function bindJQuery() {
1532 var originalCleanData;
1534 if (bindJQueryFired) {
1538 // bind to jQuery if present;
1539 jQuery = window.jQuery;
1540 // Use jQuery if it exists with proper functionality, otherwise default to us.
1541 // Angular 1.2+ requires jQuery 1.7+ for on()/off() support.
1542 // Angular 1.3+ technically requires at least jQuery 2.1+ but it may work with older
1543 // versions. It will not work for sure with jQuery <1.7, though.
1544 if (jQuery && jQuery.fn.on) {
1547 scope: JQLitePrototype.scope,
1548 isolateScope: JQLitePrototype.isolateScope,
1549 controller: JQLitePrototype.controller,
1550 injector: JQLitePrototype.injector,
1551 inheritedData: JQLitePrototype.inheritedData
1554 // All nodes removed from the DOM via various jQuery APIs like .remove()
1555 // are passed through jQuery.cleanData. Monkey-patch this method to fire
1556 // the $destroy event on all removed nodes.
1557 originalCleanData = jQuery.cleanData;
1558 jQuery.cleanData = function(elems) {
1560 if (!skipDestroyOnNextJQueryCleanData) {
1561 for (var i = 0, elem; (elem = elems[i]) != null; i++) {
1562 events = jQuery._data(elem, "events");
1563 if (events && events.$destroy) {
1564 jQuery(elem).triggerHandler('$destroy');
1568 skipDestroyOnNextJQueryCleanData = false;
1570 originalCleanData(elems);
1576 angular.element = jqLite;
1578 // Prevent double-proxying.
1579 bindJQueryFired = true;
1583 * throw error if the argument is falsy.
1585 function assertArg(arg, name, reason) {
1587 throw ngMinErr('areq', "Argument '{0}' is {1}", (name || '?'), (reason || "required"));
1592 function assertArgFn(arg, name, acceptArrayAnnotation) {
1593 if (acceptArrayAnnotation && isArray(arg)) {
1594 arg = arg[arg.length - 1];
1597 assertArg(isFunction(arg), name, 'not a function, got ' +
1598 (arg && typeof arg === 'object' ? arg.constructor.name || 'Object' : typeof arg));
1603 * throw error if the name given is hasOwnProperty
1604 * @param {String} name the name to test
1605 * @param {String} context the context in which the name is used, such as module or directive
1607 function assertNotHasOwnProperty(name, context) {
1608 if (name === 'hasOwnProperty') {
1609 throw ngMinErr('badname', "hasOwnProperty is not a valid {0} name", context);
1614 * Return the value accessible from the object by path. Any undefined traversals are ignored
1615 * @param {Object} obj starting object
1616 * @param {String} path path to traverse
1617 * @param {boolean} [bindFnToScope=true]
1618 * @returns {Object} value as accessible by path
1620 //TODO(misko): this function needs to be removed
1621 function getter(obj, path, bindFnToScope) {
1622 if (!path) return obj;
1623 var keys = path.split('.');
1625 var lastInstance = obj;
1626 var len = keys.length;
1628 for (var i = 0; i < len; i++) {
1631 obj = (lastInstance = obj)[key];
1634 if (!bindFnToScope && isFunction(obj)) {
1635 return bind(lastInstance, obj);
1641 * Return the DOM siblings between the first and last node in the given array.
1642 * @param {Array} array like object
1643 * @returns {jqLite} jqLite collection containing the nodes
1645 function getBlockNodes(nodes) {
1646 // TODO(perf): just check if all items in `nodes` are siblings and if they are return the original
1647 // collection, otherwise update the original collection.
1648 var node = nodes[0];
1649 var endNode = nodes[nodes.length - 1];
1650 var blockNodes = [node];
1653 node = node.nextSibling;
1655 blockNodes.push(node);
1656 } while (node !== endNode);
1658 return jqLite(blockNodes);
1663 * Creates a new object without a prototype. This object is useful for lookup without having to
1664 * guard against prototypically inherited properties via hasOwnProperty.
1666 * Related micro-benchmarks:
1667 * - http://jsperf.com/object-create2
1668 * - http://jsperf.com/proto-map-lookup/2
1669 * - http://jsperf.com/for-in-vs-object-keys2
1673 function createMap() {
1674 return Object.create(null);
1677 var NODE_TYPE_ELEMENT = 1;
1678 var NODE_TYPE_TEXT = 3;
1679 var NODE_TYPE_COMMENT = 8;
1680 var NODE_TYPE_DOCUMENT = 9;
1681 var NODE_TYPE_DOCUMENT_FRAGMENT = 11;
1685 * @name angular.Module
1689 * Interface for configuring angular {@link angular.module modules}.
1692 function setupModuleLoader(window) {
1694 var $injectorMinErr = minErr('$injector');
1695 var ngMinErr = minErr('ng');
1697 function ensure(obj, name, factory) {
1698 return obj[name] || (obj[name] = factory());
1701 var angular = ensure(window, 'angular', Object);
1703 // We need to expose `angular.$$minErr` to modules such as `ngResource` that reference it during bootstrap
1704 angular.$$minErr = angular.$$minErr || minErr;
1706 return ensure(angular, 'module', function() {
1707 /** @type {Object.<string, angular.Module>} */
1712 * @name angular.module
1716 * The `angular.module` is a global place for creating, registering and retrieving Angular
1718 * All modules (angular core or 3rd party) that should be available to an application must be
1719 * registered using this mechanism.
1721 * When passed two or more arguments, a new module is created. If passed only one argument, an
1722 * existing module (the name passed as the first argument to `module`) is retrieved.
1727 * A module is a collection of services, directives, controllers, filters, and configuration information.
1728 * `angular.module` is used to configure the {@link auto.$injector $injector}.
1731 * // Create a new module
1732 * var myModule = angular.module('myModule', []);
1734 * // register a new service
1735 * myModule.value('appName', 'MyCoolApp');
1737 * // configure existing services inside initialization blocks.
1738 * myModule.config(['$locationProvider', function($locationProvider) {
1739 * // Configure existing providers
1740 * $locationProvider.hashPrefix('!');
1744 * Then you can create an injector and load your modules like this:
1747 * var injector = angular.injector(['ng', 'myModule'])
1750 * However it's more likely that you'll just use
1751 * {@link ng.directive:ngApp ngApp} or
1752 * {@link angular.bootstrap} to simplify this process for you.
1754 * @param {!string} name The name of the module to create or retrieve.
1755 * @param {!Array.<string>=} requires If specified then new module is being created. If
1756 * unspecified then the module is being retrieved for further configuration.
1757 * @param {Function=} configFn Optional configuration function for the module. Same as
1758 * {@link angular.Module#config Module#config()}.
1759 * @returns {module} new module with the {@link angular.Module} api.
1761 return function module(name, requires, configFn) {
1762 var assertNotHasOwnProperty = function(name, context) {
1763 if (name === 'hasOwnProperty') {
1764 throw ngMinErr('badname', 'hasOwnProperty is not a valid {0} name', context);
1768 assertNotHasOwnProperty(name, 'module');
1769 if (requires && modules.hasOwnProperty(name)) {
1770 modules[name] = null;
1772 return ensure(modules, name, function() {
1774 throw $injectorMinErr('nomod', "Module '{0}' is not available! You either misspelled " +
1775 "the module name or forgot to load it. If registering a module ensure that you " +
1776 "specify the dependencies as the second argument.", name);
1779 /** @type {!Array.<Array.<*>>} */
1780 var invokeQueue = [];
1782 /** @type {!Array.<Function>} */
1783 var configBlocks = [];
1785 /** @type {!Array.<Function>} */
1788 var config = invokeLater('$injector', 'invoke', 'push', configBlocks);
1790 /** @type {angular.Module} */
1791 var moduleInstance = {
1793 _invokeQueue: invokeQueue,
1794 _configBlocks: configBlocks,
1795 _runBlocks: runBlocks,
1799 * @name angular.Module#requires
1803 * Holds the list of modules which the injector will load before the current module is
1810 * @name angular.Module#name
1814 * Name of the module.
1821 * @name angular.Module#provider
1823 * @param {string} name service name
1824 * @param {Function} providerType Construction function for creating new instance of the
1827 * See {@link auto.$provide#provider $provide.provider()}.
1829 provider: invokeLater('$provide', 'provider'),
1833 * @name angular.Module#factory
1835 * @param {string} name service name
1836 * @param {Function} providerFunction Function for creating new instance of the service.
1838 * See {@link auto.$provide#factory $provide.factory()}.
1840 factory: invokeLater('$provide', 'factory'),
1844 * @name angular.Module#service
1846 * @param {string} name service name
1847 * @param {Function} constructor A constructor function that will be instantiated.
1849 * See {@link auto.$provide#service $provide.service()}.
1851 service: invokeLater('$provide', 'service'),
1855 * @name angular.Module#value
1857 * @param {string} name service name
1858 * @param {*} object Service instance object.
1860 * See {@link auto.$provide#value $provide.value()}.
1862 value: invokeLater('$provide', 'value'),
1866 * @name angular.Module#constant
1868 * @param {string} name constant name
1869 * @param {*} object Constant value.
1871 * Because the constant are fixed, they get applied before other provide methods.
1872 * See {@link auto.$provide#constant $provide.constant()}.
1874 constant: invokeLater('$provide', 'constant', 'unshift'),
1878 * @name angular.Module#animation
1880 * @param {string} name animation name
1881 * @param {Function} animationFactory Factory function for creating new instance of an
1885 * **NOTE**: animations take effect only if the **ngAnimate** module is loaded.
1888 * Defines an animation hook that can be later used with
1889 * {@link ngAnimate.$animate $animate} service and directives that use this service.
1892 * module.animation('.animation-name', function($inject1, $inject2) {
1894 * eventName : function(element, done) {
1895 * //code to run the animation
1896 * //once complete, then run done()
1897 * return function cancellationFunction(element) {
1898 * //code to cancel the animation
1905 * See {@link ng.$animateProvider#register $animateProvider.register()} and
1906 * {@link ngAnimate ngAnimate module} for more information.
1908 animation: invokeLater('$animateProvider', 'register'),
1912 * @name angular.Module#filter
1914 * @param {string} name Filter name.
1915 * @param {Function} filterFactory Factory function for creating new instance of filter.
1917 * See {@link ng.$filterProvider#register $filterProvider.register()}.
1919 filter: invokeLater('$filterProvider', 'register'),
1923 * @name angular.Module#controller
1925 * @param {string|Object} name Controller name, or an object map of controllers where the
1926 * keys are the names and the values are the constructors.
1927 * @param {Function} constructor Controller constructor function.
1929 * See {@link ng.$controllerProvider#register $controllerProvider.register()}.
1931 controller: invokeLater('$controllerProvider', 'register'),
1935 * @name angular.Module#directive
1937 * @param {string|Object} name Directive name, or an object map of directives where the
1938 * keys are the names and the values are the factories.
1939 * @param {Function} directiveFactory Factory function for creating new instance of
1942 * See {@link ng.$compileProvider#directive $compileProvider.directive()}.
1944 directive: invokeLater('$compileProvider', 'directive'),
1948 * @name angular.Module#config
1950 * @param {Function} configFn Execute this function on module load. Useful for service
1953 * Use this method to register work which needs to be performed on module loading.
1954 * For more about how to configure services, see
1955 * {@link providers#provider-recipe Provider Recipe}.
1961 * @name angular.Module#run
1963 * @param {Function} initializationFn Execute this function after injector creation.
1964 * Useful for application initialization.
1966 * Use this method to register work which should be performed when the injector is done
1967 * loading all modules.
1969 run: function(block) {
1970 runBlocks.push(block);
1979 return moduleInstance;
1982 * @param {string} provider
1983 * @param {string} method
1984 * @param {String=} insertMethod
1985 * @returns {angular.Module}
1987 function invokeLater(provider, method, insertMethod, queue) {
1988 if (!queue) queue = invokeQueue;
1990 queue[insertMethod || 'push']([provider, method, arguments]);
1991 return moduleInstance;
2000 /* global: toDebugString: true */
2002 function serializeObject(obj) {
2005 return JSON.stringify(obj, function(key, val) {
2006 val = toJsonReplacer(key, val);
2007 if (isObject(val)) {
2009 if (seen.indexOf(val) >= 0) return '<<already seen>>';
2017 function toDebugString(obj) {
2018 if (typeof obj === 'function') {
2019 return obj.toString().replace(/ \{[\s\S]*$/, '');
2020 } else if (typeof obj === 'undefined') {
2022 } else if (typeof obj !== 'string') {
2023 return serializeObject(obj);
2028 /* global angularModule: true,
2034 htmlAnchorDirective,
2043 ngBindHtmlDirective,
2044 ngBindTemplateDirective,
2046 ngClassEvenDirective,
2047 ngClassOddDirective,
2050 ngControllerDirective,
2055 ngIncludeFillContentDirective,
2057 ngNonBindableDirective,
2058 ngPluralizeDirective,
2063 ngSwitchWhenDirective,
2064 ngSwitchDefaultDirective,
2066 ngTranscludeDirective,
2079 ngModelOptionsDirective,
2080 ngAttributeAliasDirectives,
2083 $AnchorScrollProvider,
2086 $CacheFactoryProvider,
2087 $ControllerProvider,
2089 $ExceptionHandlerProvider,
2091 $InterpolateProvider,
2094 $HttpBackendProvider,
2101 $$SanitizeUriProvider,
2103 $SceDelegateProvider,
2105 $TemplateCacheProvider,
2106 $TemplateRequestProvider,
2107 $$TestabilityProvider,
2110 $$AsyncCallbackProvider,
2118 * @name angular.version
2121 * An object that contains information about the current AngularJS version. This object has the
2122 * following properties:
2124 * - `full` – `{string}` – Full version string, such as "0.9.18".
2125 * - `major` – `{number}` – Major version number, such as "0".
2126 * - `minor` – `{number}` – Minor version number, such as "9".
2127 * - `dot` – `{number}` – Dot version number, such as "18".
2128 * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat".
2131 full: '1.3.15', // all of these placeholder strings will be replaced by grunt's
2132 major: 1, // package task
2135 codeName: 'locality-filtration'
2139 function publishExternalAPI(angular) {
2141 'bootstrap': bootstrap,
2147 'injector': createInjector,
2151 'fromJson': fromJson,
2152 'identity': identity,
2153 'isUndefined': isUndefined,
2154 'isDefined': isDefined,
2155 'isString': isString,
2156 'isFunction': isFunction,
2157 'isObject': isObject,
2158 'isNumber': isNumber,
2159 'isElement': isElement,
2163 'lowercase': lowercase,
2164 'uppercase': uppercase,
2165 'callbacks': {counter: 0},
2166 'getTestability': getTestability,
2169 'reloadWithDebugInfo': reloadWithDebugInfo
2172 angularModule = setupModuleLoader(window);
2174 angularModule('ngLocale');
2176 angularModule('ngLocale', []).provider('$locale', $LocaleProvider);
2179 angularModule('ng', ['ngLocale'], ['$provide',
2180 function ngModule($provide) {
2181 // $$sanitizeUriProvider needs to be before $compileProvider as it is used by it.
2183 $$sanitizeUri: $$SanitizeUriProvider
2185 $provide.provider('$compile', $CompileProvider).
2187 a: htmlAnchorDirective,
2188 input: inputDirective,
2189 textarea: inputDirective,
2190 form: formDirective,
2191 script: scriptDirective,
2192 select: selectDirective,
2193 style: styleDirective,
2194 option: optionDirective,
2195 ngBind: ngBindDirective,
2196 ngBindHtml: ngBindHtmlDirective,
2197 ngBindTemplate: ngBindTemplateDirective,
2198 ngClass: ngClassDirective,
2199 ngClassEven: ngClassEvenDirective,
2200 ngClassOdd: ngClassOddDirective,
2201 ngCloak: ngCloakDirective,
2202 ngController: ngControllerDirective,
2203 ngForm: ngFormDirective,
2204 ngHide: ngHideDirective,
2205 ngIf: ngIfDirective,
2206 ngInclude: ngIncludeDirective,
2207 ngInit: ngInitDirective,
2208 ngNonBindable: ngNonBindableDirective,
2209 ngPluralize: ngPluralizeDirective,
2210 ngRepeat: ngRepeatDirective,
2211 ngShow: ngShowDirective,
2212 ngStyle: ngStyleDirective,
2213 ngSwitch: ngSwitchDirective,
2214 ngSwitchWhen: ngSwitchWhenDirective,
2215 ngSwitchDefault: ngSwitchDefaultDirective,
2216 ngOptions: ngOptionsDirective,
2217 ngTransclude: ngTranscludeDirective,
2218 ngModel: ngModelDirective,
2219 ngList: ngListDirective,
2220 ngChange: ngChangeDirective,
2221 pattern: patternDirective,
2222 ngPattern: patternDirective,
2223 required: requiredDirective,
2224 ngRequired: requiredDirective,
2225 minlength: minlengthDirective,
2226 ngMinlength: minlengthDirective,
2227 maxlength: maxlengthDirective,
2228 ngMaxlength: maxlengthDirective,
2229 ngValue: ngValueDirective,
2230 ngModelOptions: ngModelOptionsDirective
2233 ngInclude: ngIncludeFillContentDirective
2235 directive(ngAttributeAliasDirectives).
2236 directive(ngEventDirectives);
2238 $anchorScroll: $AnchorScrollProvider,
2239 $animate: $AnimateProvider,
2240 $browser: $BrowserProvider,
2241 $cacheFactory: $CacheFactoryProvider,
2242 $controller: $ControllerProvider,
2243 $document: $DocumentProvider,
2244 $exceptionHandler: $ExceptionHandlerProvider,
2245 $filter: $FilterProvider,
2246 $interpolate: $InterpolateProvider,
2247 $interval: $IntervalProvider,
2248 $http: $HttpProvider,
2249 $httpBackend: $HttpBackendProvider,
2250 $location: $LocationProvider,
2252 $parse: $ParseProvider,
2253 $rootScope: $RootScopeProvider,
2257 $sceDelegate: $SceDelegateProvider,
2258 $sniffer: $SnifferProvider,
2259 $templateCache: $TemplateCacheProvider,
2260 $templateRequest: $TemplateRequestProvider,
2261 $$testability: $$TestabilityProvider,
2262 $timeout: $TimeoutProvider,
2263 $window: $WindowProvider,
2264 $$rAF: $$RAFProvider,
2265 $$asyncCallback: $$AsyncCallbackProvider,
2266 $$jqLite: $$jqLiteProvider
2272 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2273 * Any commits to this file should be reviewed with security in mind. *
2274 * Changes to this file can potentially create security vulnerabilities. *
2275 * An approval from 2 Core members with history of modifying *
2276 * this file is required. *
2278 * Does the change somehow allow for arbitrary javascript to be executed? *
2279 * Or allows for someone to change the prototype of built-in objects? *
2280 * Or gives undesired access to variables likes document or window? *
2281 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2283 /* global JQLitePrototype: true,
2284 addEventListenerFn: true,
2285 removeEventListenerFn: true,
2290 //////////////////////////////////
2292 //////////////////////////////////
2296 * @name angular.element
2301 * Wraps a raw DOM element or HTML string as a [jQuery](http://jquery.com) element.
2303 * If jQuery is available, `angular.element` is an alias for the
2304 * [jQuery](http://api.jquery.com/jQuery/) function. If jQuery is not available, `angular.element`
2305 * delegates to Angular's built-in subset of jQuery, called "jQuery lite" or "jqLite."
2307 * <div class="alert alert-success">jqLite is a tiny, API-compatible subset of jQuery that allows
2308 * Angular to manipulate the DOM in a cross-browser compatible way. **jqLite** implements only the most
2309 * commonly needed functionality with the goal of having a very small footprint.</div>
2311 * To use jQuery, simply load it before `DOMContentLoaded` event fired.
2313 * <div class="alert">**Note:** all element references in Angular are always wrapped with jQuery or
2314 * jqLite; they are never raw DOM references.</div>
2316 * ## Angular's jqLite
2317 * jqLite provides only the following jQuery methods:
2319 * - [`addClass()`](http://api.jquery.com/addClass/)
2320 * - [`after()`](http://api.jquery.com/after/)
2321 * - [`append()`](http://api.jquery.com/append/)
2322 * - [`attr()`](http://api.jquery.com/attr/) - Does not support functions as parameters
2323 * - [`bind()`](http://api.jquery.com/bind/) - Does not support namespaces, selectors or eventData
2324 * - [`children()`](http://api.jquery.com/children/) - Does not support selectors
2325 * - [`clone()`](http://api.jquery.com/clone/)
2326 * - [`contents()`](http://api.jquery.com/contents/)
2327 * - [`css()`](http://api.jquery.com/css/) - Only retrieves inline-styles, does not call `getComputedStyle()`
2328 * - [`data()`](http://api.jquery.com/data/)
2329 * - [`detach()`](http://api.jquery.com/detach/)
2330 * - [`empty()`](http://api.jquery.com/empty/)
2331 * - [`eq()`](http://api.jquery.com/eq/)
2332 * - [`find()`](http://api.jquery.com/find/) - Limited to lookups by tag name
2333 * - [`hasClass()`](http://api.jquery.com/hasClass/)
2334 * - [`html()`](http://api.jquery.com/html/)
2335 * - [`next()`](http://api.jquery.com/next/) - Does not support selectors
2336 * - [`on()`](http://api.jquery.com/on/) - Does not support namespaces, selectors or eventData
2337 * - [`off()`](http://api.jquery.com/off/) - Does not support namespaces or selectors
2338 * - [`one()`](http://api.jquery.com/one/) - Does not support namespaces or selectors
2339 * - [`parent()`](http://api.jquery.com/parent/) - Does not support selectors
2340 * - [`prepend()`](http://api.jquery.com/prepend/)
2341 * - [`prop()`](http://api.jquery.com/prop/)
2342 * - [`ready()`](http://api.jquery.com/ready/)
2343 * - [`remove()`](http://api.jquery.com/remove/)
2344 * - [`removeAttr()`](http://api.jquery.com/removeAttr/)
2345 * - [`removeClass()`](http://api.jquery.com/removeClass/)
2346 * - [`removeData()`](http://api.jquery.com/removeData/)
2347 * - [`replaceWith()`](http://api.jquery.com/replaceWith/)
2348 * - [`text()`](http://api.jquery.com/text/)
2349 * - [`toggleClass()`](http://api.jquery.com/toggleClass/)
2350 * - [`triggerHandler()`](http://api.jquery.com/triggerHandler/) - Passes a dummy event object to handlers.
2351 * - [`unbind()`](http://api.jquery.com/unbind/) - Does not support namespaces
2352 * - [`val()`](http://api.jquery.com/val/)
2353 * - [`wrap()`](http://api.jquery.com/wrap/)
2355 * ## jQuery/jqLite Extras
2356 * Angular also provides the following additional methods and events to both jQuery and jqLite:
2359 * - `$destroy` - AngularJS intercepts all jqLite/jQuery's DOM destruction apis and fires this event
2360 * on all DOM nodes being removed. This can be used to clean up any 3rd party bindings to the DOM
2361 * element before it is removed.
2364 * - `controller(name)` - retrieves the controller of the current element or its parent. By default
2365 * retrieves controller associated with the `ngController` directive. If `name` is provided as
2366 * camelCase directive name, then the controller for this directive will be retrieved (e.g.
2368 * - `injector()` - retrieves the injector of the current element or its parent.
2369 * - `scope()` - retrieves the {@link ng.$rootScope.Scope scope} of the current
2370 * element or its parent. Requires {@link guide/production#disabling-debug-data Debug Data} to
2372 * - `isolateScope()` - retrieves an isolate {@link ng.$rootScope.Scope scope} if one is attached directly to the
2373 * current element. This getter should be used only on elements that contain a directive which starts a new isolate
2374 * scope. Calling `scope()` on this element always returns the original non-isolate scope.
2375 * Requires {@link guide/production#disabling-debug-data Debug Data} to be enabled.
2376 * - `inheritedData()` - same as `data()`, but walks up the DOM until a value is found or the top
2377 * parent element is reached.
2379 * @param {string|DOMElement} element HTML string or DOMElement to be wrapped into jQuery.
2380 * @returns {Object} jQuery object.
2383 JQLite.expando = 'ng339';
2385 var jqCache = JQLite.cache = {},
2387 addEventListenerFn = function(element, type, fn) {
2388 element.addEventListener(type, fn, false);
2390 removeEventListenerFn = function(element, type, fn) {
2391 element.removeEventListener(type, fn, false);
2395 * !!! This is an undocumented "private" function !!!
2397 JQLite._data = function(node) {
2398 //jQuery always returns an object on cache miss
2399 return this.cache[node[this.expando]] || {};
2402 function jqNextId() { return ++jqId; }
2405 var SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g;
2406 var MOZ_HACK_REGEXP = /^moz([A-Z])/;
2407 var MOUSE_EVENT_MAP= { mouseleave: "mouseout", mouseenter: "mouseover"};
2408 var jqLiteMinErr = minErr('jqLite');
2411 * Converts snake_case to camelCase.
2412 * Also there is special case for Moz prefix starting with upper case letter.
2413 * @param name Name to normalize
2415 function camelCase(name) {
2417 replace(SPECIAL_CHARS_REGEXP, function(_, separator, letter, offset) {
2418 return offset ? letter.toUpperCase() : letter;
2420 replace(MOZ_HACK_REGEXP, 'Moz$1');
2423 var SINGLE_TAG_REGEXP = /^<(\w+)\s*\/?>(?:<\/\1>|)$/;
2424 var HTML_REGEXP = /<|&#?\w+;/;
2425 var TAG_NAME_REGEXP = /<([\w:]+)/;
2426 var XHTML_TAG_REGEXP = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi;
2429 'option': [1, '<select multiple="multiple">', '</select>'],
2431 'thead': [1, '<table>', '</table>'],
2432 'col': [2, '<table><colgroup>', '</colgroup></table>'],
2433 'tr': [2, '<table><tbody>', '</tbody></table>'],
2434 'td': [3, '<table><tbody><tr>', '</tr></tbody></table>'],
2435 '_default': [0, "", ""]
2438 wrapMap.optgroup = wrapMap.option;
2439 wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
2440 wrapMap.th = wrapMap.td;
2443 function jqLiteIsTextNode(html) {
2444 return !HTML_REGEXP.test(html);
2447 function jqLiteAcceptsData(node) {
2448 // The window object can accept data but has no nodeType
2449 // Otherwise we are only interested in elements (1) and documents (9)
2450 var nodeType = node.nodeType;
2451 return nodeType === NODE_TYPE_ELEMENT || !nodeType || nodeType === NODE_TYPE_DOCUMENT;
2454 function jqLiteBuildFragment(html, context) {
2456 fragment = context.createDocumentFragment(),
2459 if (jqLiteIsTextNode(html)) {
2460 // Convert non-html into a text node
2461 nodes.push(context.createTextNode(html));
2463 // Convert html into DOM nodes
2464 tmp = tmp || fragment.appendChild(context.createElement("div"));
2465 tag = (TAG_NAME_REGEXP.exec(html) || ["", ""])[1].toLowerCase();
2466 wrap = wrapMap[tag] || wrapMap._default;
2467 tmp.innerHTML = wrap[1] + html.replace(XHTML_TAG_REGEXP, "<$1></$2>") + wrap[2];
2469 // Descend through wrappers to the right content
2472 tmp = tmp.lastChild;
2475 nodes = concat(nodes, tmp.childNodes);
2477 tmp = fragment.firstChild;
2478 tmp.textContent = "";
2481 // Remove wrapper from fragment
2482 fragment.textContent = "";
2483 fragment.innerHTML = ""; // Clear inner HTML
2484 forEach(nodes, function(node) {
2485 fragment.appendChild(node);
2491 function jqLiteParseHTML(html, context) {
2492 context = context || document;
2495 if ((parsed = SINGLE_TAG_REGEXP.exec(html))) {
2496 return [context.createElement(parsed[1])];
2499 if ((parsed = jqLiteBuildFragment(html, context))) {
2500 return parsed.childNodes;
2506 /////////////////////////////////////////////
2507 function JQLite(element) {
2508 if (element instanceof JQLite) {
2514 if (isString(element)) {
2515 element = trim(element);
2518 if (!(this instanceof JQLite)) {
2519 if (argIsString && element.charAt(0) != '<') {
2520 throw jqLiteMinErr('nosel', 'Looking up elements via selectors is not supported by jqLite! See: http://docs.angularjs.org/api/angular.element');
2522 return new JQLite(element);
2526 jqLiteAddNodes(this, jqLiteParseHTML(element));
2528 jqLiteAddNodes(this, element);
2532 function jqLiteClone(element) {
2533 return element.cloneNode(true);
2536 function jqLiteDealoc(element, onlyDescendants) {
2537 if (!onlyDescendants) jqLiteRemoveData(element);
2539 if (element.querySelectorAll) {
2540 var descendants = element.querySelectorAll('*');
2541 for (var i = 0, l = descendants.length; i < l; i++) {
2542 jqLiteRemoveData(descendants[i]);
2547 function jqLiteOff(element, type, fn, unsupported) {
2548 if (isDefined(unsupported)) throw jqLiteMinErr('offargs', 'jqLite#off() does not support the `selector` argument');
2550 var expandoStore = jqLiteExpandoStore(element);
2551 var events = expandoStore && expandoStore.events;
2552 var handle = expandoStore && expandoStore.handle;
2554 if (!handle) return; //no listeners registered
2557 for (type in events) {
2558 if (type !== '$destroy') {
2559 removeEventListenerFn(element, type, handle);
2561 delete events[type];
2564 forEach(type.split(' '), function(type) {
2565 if (isDefined(fn)) {
2566 var listenerFns = events[type];
2567 arrayRemove(listenerFns || [], fn);
2568 if (listenerFns && listenerFns.length > 0) {
2573 removeEventListenerFn(element, type, handle);
2574 delete events[type];
2579 function jqLiteRemoveData(element, name) {
2580 var expandoId = element.ng339;
2581 var expandoStore = expandoId && jqCache[expandoId];
2585 delete expandoStore.data[name];
2589 if (expandoStore.handle) {
2590 if (expandoStore.events.$destroy) {
2591 expandoStore.handle({}, '$destroy');
2595 delete jqCache[expandoId];
2596 element.ng339 = undefined; // don't delete DOM expandos. IE and Chrome don't like it
2601 function jqLiteExpandoStore(element, createIfNecessary) {
2602 var expandoId = element.ng339,
2603 expandoStore = expandoId && jqCache[expandoId];
2605 if (createIfNecessary && !expandoStore) {
2606 element.ng339 = expandoId = jqNextId();
2607 expandoStore = jqCache[expandoId] = {events: {}, data: {}, handle: undefined};
2610 return expandoStore;
2614 function jqLiteData(element, key, value) {
2615 if (jqLiteAcceptsData(element)) {
2617 var isSimpleSetter = isDefined(value);
2618 var isSimpleGetter = !isSimpleSetter && key && !isObject(key);
2619 var massGetter = !key;
2620 var expandoStore = jqLiteExpandoStore(element, !isSimpleGetter);
2621 var data = expandoStore && expandoStore.data;
2623 if (isSimpleSetter) { // data('key', value)
2626 if (massGetter) { // data()
2629 if (isSimpleGetter) { // data('key')
2630 // don't force creation of expandoStore if it doesn't exist yet
2631 return data && data[key];
2632 } else { // mass-setter: data({key1: val1, key2: val2})
2640 function jqLiteHasClass(element, selector) {
2641 if (!element.getAttribute) return false;
2642 return ((" " + (element.getAttribute('class') || '') + " ").replace(/[\n\t]/g, " ").
2643 indexOf(" " + selector + " ") > -1);
2646 function jqLiteRemoveClass(element, cssClasses) {
2647 if (cssClasses && element.setAttribute) {
2648 forEach(cssClasses.split(' '), function(cssClass) {
2649 element.setAttribute('class', trim(
2650 (" " + (element.getAttribute('class') || '') + " ")
2651 .replace(/[\n\t]/g, " ")
2652 .replace(" " + trim(cssClass) + " ", " "))
2658 function jqLiteAddClass(element, cssClasses) {
2659 if (cssClasses && element.setAttribute) {
2660 var existingClasses = (' ' + (element.getAttribute('class') || '') + ' ')
2661 .replace(/[\n\t]/g, " ");
2663 forEach(cssClasses.split(' '), function(cssClass) {
2664 cssClass = trim(cssClass);
2665 if (existingClasses.indexOf(' ' + cssClass + ' ') === -1) {
2666 existingClasses += cssClass + ' ';
2670 element.setAttribute('class', trim(existingClasses));
2675 function jqLiteAddNodes(root, elements) {
2676 // THIS CODE IS VERY HOT. Don't make changes without benchmarking.
2680 // if a Node (the most common case)
2681 if (elements.nodeType) {
2682 root[root.length++] = elements;
2684 var length = elements.length;
2686 // if an Array or NodeList and not a Window
2687 if (typeof length === 'number' && elements.window !== elements) {
2689 for (var i = 0; i < length; i++) {
2690 root[root.length++] = elements[i];
2694 root[root.length++] = elements;
2701 function jqLiteController(element, name) {
2702 return jqLiteInheritedData(element, '$' + (name || 'ngController') + 'Controller');
2705 function jqLiteInheritedData(element, name, value) {
2706 // if element is the document object work with the html element instead
2707 // this makes $(document).scope() possible
2708 if (element.nodeType == NODE_TYPE_DOCUMENT) {
2709 element = element.documentElement;
2711 var names = isArray(name) ? name : [name];
2714 for (var i = 0, ii = names.length; i < ii; i++) {
2715 if ((value = jqLite.data(element, names[i])) !== undefined) return value;
2718 // If dealing with a document fragment node with a host element, and no parent, use the host
2719 // element as the parent. This enables directives within a Shadow DOM or polyfilled Shadow DOM
2720 // to lookup parent controllers.
2721 element = element.parentNode || (element.nodeType === NODE_TYPE_DOCUMENT_FRAGMENT && element.host);
2725 function jqLiteEmpty(element) {
2726 jqLiteDealoc(element, true);
2727 while (element.firstChild) {
2728 element.removeChild(element.firstChild);
2732 function jqLiteRemove(element, keepData) {
2733 if (!keepData) jqLiteDealoc(element);
2734 var parent = element.parentNode;
2735 if (parent) parent.removeChild(element);
2739 function jqLiteDocumentLoaded(action, win) {
2740 win = win || window;
2741 if (win.document.readyState === 'complete') {
2742 // Force the action to be run async for consistent behaviour
2743 // from the action's point of view
2744 // i.e. it will definitely not be in a $apply
2745 win.setTimeout(action);
2747 // No need to unbind this handler as load is only ever called once
2748 jqLite(win).on('load', action);
2752 //////////////////////////////////////////
2753 // Functions which are declared directly.
2754 //////////////////////////////////////////
2755 var JQLitePrototype = JQLite.prototype = {
2756 ready: function(fn) {
2759 function trigger() {
2765 // check if document is already loaded
2766 if (document.readyState === 'complete') {
2767 setTimeout(trigger);
2769 this.on('DOMContentLoaded', trigger); // works for modern browsers and IE9
2770 // we can not use jqLite since we are not done loading and jQuery could be loaded later.
2772 JQLite(window).on('load', trigger); // fallback to window.onload for others
2776 toString: function() {
2778 forEach(this, function(e) { value.push('' + e);});
2779 return '[' + value.join(', ') + ']';
2782 eq: function(index) {
2783 return (index >= 0) ? jqLite(this[index]) : jqLite(this[this.length + index]);
2792 //////////////////////////////////////////
2793 // Functions iterating getter/setters.
2794 // these functions return self on setter and
2796 //////////////////////////////////////////
2797 var BOOLEAN_ATTR = {};
2798 forEach('multiple,selected,checked,disabled,readOnly,required,open'.split(','), function(value) {
2799 BOOLEAN_ATTR[lowercase(value)] = value;
2801 var BOOLEAN_ELEMENTS = {};
2802 forEach('input,select,option,textarea,button,form,details'.split(','), function(value) {
2803 BOOLEAN_ELEMENTS[value] = true;
2805 var ALIASED_ATTR = {
2806 'ngMinlength': 'minlength',
2807 'ngMaxlength': 'maxlength',
2810 'ngPattern': 'pattern'
2813 function getBooleanAttrName(element, name) {
2814 // check dom last since we will most likely fail on name
2815 var booleanAttr = BOOLEAN_ATTR[name.toLowerCase()];
2817 // booleanAttr is here twice to minimize DOM access
2818 return booleanAttr && BOOLEAN_ELEMENTS[nodeName_(element)] && booleanAttr;
2821 function getAliasedAttrName(element, name) {
2822 var nodeName = element.nodeName;
2823 return (nodeName === 'INPUT' || nodeName === 'TEXTAREA') && ALIASED_ATTR[name];
2828 removeData: jqLiteRemoveData
2829 }, function(fn, name) {
2835 inheritedData: jqLiteInheritedData,
2837 scope: function(element) {
2838 // Can't use jqLiteData here directly so we stay compatible with jQuery!
2839 return jqLite.data(element, '$scope') || jqLiteInheritedData(element.parentNode || element, ['$isolateScope', '$scope']);
2842 isolateScope: function(element) {
2843 // Can't use jqLiteData here directly so we stay compatible with jQuery!
2844 return jqLite.data(element, '$isolateScope') || jqLite.data(element, '$isolateScopeNoTemplate');
2847 controller: jqLiteController,
2849 injector: function(element) {
2850 return jqLiteInheritedData(element, '$injector');
2853 removeAttr: function(element, name) {
2854 element.removeAttribute(name);
2857 hasClass: jqLiteHasClass,
2859 css: function(element, name, value) {
2860 name = camelCase(name);
2862 if (isDefined(value)) {
2863 element.style[name] = value;
2865 return element.style[name];
2869 attr: function(element, name, value) {
2870 var lowercasedName = lowercase(name);
2871 if (BOOLEAN_ATTR[lowercasedName]) {
2872 if (isDefined(value)) {
2874 element[name] = true;
2875 element.setAttribute(name, lowercasedName);
2877 element[name] = false;
2878 element.removeAttribute(lowercasedName);
2881 return (element[name] ||
2882 (element.attributes.getNamedItem(name) || noop).specified)
2886 } else if (isDefined(value)) {
2887 element.setAttribute(name, value);
2888 } else if (element.getAttribute) {
2889 // the extra argument "2" is to get the right thing for a.href in IE, see jQuery code
2890 // some elements (e.g. Document) don't have get attribute, so return undefined
2891 var ret = element.getAttribute(name, 2);
2892 // normalize non-existing attributes to undefined (as jQuery)
2893 return ret === null ? undefined : ret;
2897 prop: function(element, name, value) {
2898 if (isDefined(value)) {
2899 element[name] = value;
2901 return element[name];
2909 function getText(element, value) {
2910 if (isUndefined(value)) {
2911 var nodeType = element.nodeType;
2912 return (nodeType === NODE_TYPE_ELEMENT || nodeType === NODE_TYPE_TEXT) ? element.textContent : '';
2914 element.textContent = value;
2918 val: function(element, value) {
2919 if (isUndefined(value)) {
2920 if (element.multiple && nodeName_(element) === 'select') {
2922 forEach(element.options, function(option) {
2923 if (option.selected) {
2924 result.push(option.value || option.text);
2927 return result.length === 0 ? null : result;
2929 return element.value;
2931 element.value = value;
2934 html: function(element, value) {
2935 if (isUndefined(value)) {
2936 return element.innerHTML;
2938 jqLiteDealoc(element, true);
2939 element.innerHTML = value;
2943 }, function(fn, name) {
2945 * Properties: writes return selection, reads return first value
2947 JQLite.prototype[name] = function(arg1, arg2) {
2949 var nodeCount = this.length;
2951 // jqLiteHasClass has only two arguments, but is a getter-only fn, so we need to special-case it
2952 // in a way that survives minification.
2953 // jqLiteEmpty takes no arguments but is a setter.
2954 if (fn !== jqLiteEmpty &&
2955 (((fn.length == 2 && (fn !== jqLiteHasClass && fn !== jqLiteController)) ? arg1 : arg2) === undefined)) {
2956 if (isObject(arg1)) {
2958 // we are a write, but the object properties are the key/values
2959 for (i = 0; i < nodeCount; i++) {
2960 if (fn === jqLiteData) {
2961 // data() takes the whole object in jQuery
2965 fn(this[i], key, arg1[key]);
2969 // return self for chaining
2972 // we are a read, so read the first child.
2973 // TODO: do we still need this?
2975 // Only if we have $dv do we iterate over all, otherwise it is just the first element.
2976 var jj = (value === undefined) ? Math.min(nodeCount, 1) : nodeCount;
2977 for (var j = 0; j < jj; j++) {
2978 var nodeValue = fn(this[j], arg1, arg2);
2979 value = value ? value + nodeValue : nodeValue;
2984 // we are a write, so apply to all children
2985 for (i = 0; i < nodeCount; i++) {
2986 fn(this[i], arg1, arg2);
2988 // return self for chaining
2994 function createEventHandler(element, events) {
2995 var eventHandler = function(event, type) {
2996 // jQuery specific api
2997 event.isDefaultPrevented = function() {
2998 return event.defaultPrevented;
3001 var eventFns = events[type || event.type];
3002 var eventFnsLength = eventFns ? eventFns.length : 0;
3004 if (!eventFnsLength) return;
3006 if (isUndefined(event.immediatePropagationStopped)) {
3007 var originalStopImmediatePropagation = event.stopImmediatePropagation;
3008 event.stopImmediatePropagation = function() {
3009 event.immediatePropagationStopped = true;
3011 if (event.stopPropagation) {
3012 event.stopPropagation();
3015 if (originalStopImmediatePropagation) {
3016 originalStopImmediatePropagation.call(event);
3021 event.isImmediatePropagationStopped = function() {
3022 return event.immediatePropagationStopped === true;
3025 // Copy event handlers in case event handlers array is modified during execution.
3026 if ((eventFnsLength > 1)) {
3027 eventFns = shallowCopy(eventFns);
3030 for (var i = 0; i < eventFnsLength; i++) {
3031 if (!event.isImmediatePropagationStopped()) {
3032 eventFns[i].call(element, event);
3037 // TODO: this is a hack for angularMocks/clearDataCache that makes it possible to deregister all
3038 // events on `element`
3039 eventHandler.elem = element;
3040 return eventHandler;
3043 //////////////////////////////////////////
3044 // Functions iterating traversal.
3045 // These functions chain results into a single
3047 //////////////////////////////////////////
3049 removeData: jqLiteRemoveData,
3051 on: function jqLiteOn(element, type, fn, unsupported) {
3052 if (isDefined(unsupported)) throw jqLiteMinErr('onargs', 'jqLite#on() does not support the `selector` or `eventData` parameters');
3054 // Do not add event handlers to non-elements because they will not be cleaned up.
3055 if (!jqLiteAcceptsData(element)) {
3059 var expandoStore = jqLiteExpandoStore(element, true);
3060 var events = expandoStore.events;
3061 var handle = expandoStore.handle;
3064 handle = expandoStore.handle = createEventHandler(element, events);
3067 // http://jsperf.com/string-indexof-vs-split
3068 var types = type.indexOf(' ') >= 0 ? type.split(' ') : [type];
3069 var i = types.length;
3073 var eventFns = events[type];
3078 if (type === 'mouseenter' || type === 'mouseleave') {
3079 // Refer to jQuery's implementation of mouseenter & mouseleave
3080 // Read about mouseenter and mouseleave:
3081 // http://www.quirksmode.org/js/events_mouse.html#link8
3083 jqLiteOn(element, MOUSE_EVENT_MAP[type], function(event) {
3084 var target = this, related = event.relatedTarget;
3085 // For mousenter/leave call the handler if related is outside the target.
3086 // NB: No relatedTarget if the mouse left/entered the browser window
3087 if (!related || (related !== target && !target.contains(related))) {
3088 handle(event, type);
3093 if (type !== '$destroy') {
3094 addEventListenerFn(element, type, handle);
3097 eventFns = events[type];
3105 one: function(element, type, fn) {
3106 element = jqLite(element);
3108 //add the listener twice so that when it is called
3109 //you can remove the original function and still be
3110 //able to call element.off(ev, fn) normally
3111 element.on(type, function onFn() {
3112 element.off(type, fn);
3113 element.off(type, onFn);
3115 element.on(type, fn);
3118 replaceWith: function(element, replaceNode) {
3119 var index, parent = element.parentNode;
3120 jqLiteDealoc(element);
3121 forEach(new JQLite(replaceNode), function(node) {
3123 parent.insertBefore(node, index.nextSibling);
3125 parent.replaceChild(node, element);
3131 children: function(element) {
3133 forEach(element.childNodes, function(element) {
3134 if (element.nodeType === NODE_TYPE_ELEMENT)
3135 children.push(element);
3140 contents: function(element) {
3141 return element.contentDocument || element.childNodes || [];
3144 append: function(element, node) {
3145 var nodeType = element.nodeType;
3146 if (nodeType !== NODE_TYPE_ELEMENT && nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT) return;
3148 node = new JQLite(node);
3150 for (var i = 0, ii = node.length; i < ii; i++) {
3151 var child = node[i];
3152 element.appendChild(child);
3156 prepend: function(element, node) {
3157 if (element.nodeType === NODE_TYPE_ELEMENT) {
3158 var index = element.firstChild;
3159 forEach(new JQLite(node), function(child) {
3160 element.insertBefore(child, index);
3165 wrap: function(element, wrapNode) {
3166 wrapNode = jqLite(wrapNode).eq(0).clone()[0];
3167 var parent = element.parentNode;
3169 parent.replaceChild(wrapNode, element);
3171 wrapNode.appendChild(element);
3174 remove: jqLiteRemove,
3176 detach: function(element) {
3177 jqLiteRemove(element, true);
3180 after: function(element, newElement) {
3181 var index = element, parent = element.parentNode;
3182 newElement = new JQLite(newElement);
3184 for (var i = 0, ii = newElement.length; i < ii; i++) {
3185 var node = newElement[i];
3186 parent.insertBefore(node, index.nextSibling);
3191 addClass: jqLiteAddClass,
3192 removeClass: jqLiteRemoveClass,
3194 toggleClass: function(element, selector, condition) {
3196 forEach(selector.split(' '), function(className) {
3197 var classCondition = condition;
3198 if (isUndefined(classCondition)) {
3199 classCondition = !jqLiteHasClass(element, className);
3201 (classCondition ? jqLiteAddClass : jqLiteRemoveClass)(element, className);
3206 parent: function(element) {
3207 var parent = element.parentNode;
3208 return parent && parent.nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT ? parent : null;
3211 next: function(element) {
3212 return element.nextElementSibling;
3215 find: function(element, selector) {
3216 if (element.getElementsByTagName) {
3217 return element.getElementsByTagName(selector);
3225 triggerHandler: function(element, event, extraParameters) {
3227 var dummyEvent, eventFnsCopy, handlerArgs;
3228 var eventName = event.type || event;
3229 var expandoStore = jqLiteExpandoStore(element);
3230 var events = expandoStore && expandoStore.events;
3231 var eventFns = events && events[eventName];
3234 // Create a dummy event to pass to the handlers
3236 preventDefault: function() { this.defaultPrevented = true; },
3237 isDefaultPrevented: function() { return this.defaultPrevented === true; },
3238 stopImmediatePropagation: function() { this.immediatePropagationStopped = true; },
3239 isImmediatePropagationStopped: function() { return this.immediatePropagationStopped === true; },
3240 stopPropagation: noop,
3245 // If a custom event was provided then extend our dummy event with it
3247 dummyEvent = extend(dummyEvent, event);
3250 // Copy event handlers in case event handlers array is modified during execution.
3251 eventFnsCopy = shallowCopy(eventFns);
3252 handlerArgs = extraParameters ? [dummyEvent].concat(extraParameters) : [dummyEvent];
3254 forEach(eventFnsCopy, function(fn) {
3255 if (!dummyEvent.isImmediatePropagationStopped()) {
3256 fn.apply(element, handlerArgs);
3261 }, function(fn, name) {
3263 * chaining functions
3265 JQLite.prototype[name] = function(arg1, arg2, arg3) {
3268 for (var i = 0, ii = this.length; i < ii; i++) {
3269 if (isUndefined(value)) {
3270 value = fn(this[i], arg1, arg2, arg3);
3271 if (isDefined(value)) {
3272 // any function which returns a value needs to be wrapped
3273 value = jqLite(value);
3276 jqLiteAddNodes(value, fn(this[i], arg1, arg2, arg3));
3279 return isDefined(value) ? value : this;
3282 // bind legacy bind/unbind to on/off
3283 JQLite.prototype.bind = JQLite.prototype.on;
3284 JQLite.prototype.unbind = JQLite.prototype.off;
3288 // Provider for private $$jqLite service
3289 function $$jqLiteProvider() {
3290 this.$get = function $$jqLite() {
3291 return extend(JQLite, {
3292 hasClass: function(node, classes) {
3293 if (node.attr) node = node[0];
3294 return jqLiteHasClass(node, classes);
3296 addClass: function(node, classes) {
3297 if (node.attr) node = node[0];
3298 return jqLiteAddClass(node, classes);
3300 removeClass: function(node, classes) {
3301 if (node.attr) node = node[0];
3302 return jqLiteRemoveClass(node, classes);
3309 * Computes a hash of an 'obj'.
3312 * number is number as string
3313 * object is either result of calling $$hashKey function on the object or uniquely generated id,
3314 * that is also assigned to the $$hashKey property of the object.
3317 * @returns {string} hash string such that the same input will have the same hash string.
3318 * The resulting string key is in 'type:hashKey' format.
3320 function hashKey(obj, nextUidFn) {
3321 var key = obj && obj.$$hashKey;
3324 if (typeof key === 'function') {
3325 key = obj.$$hashKey();
3330 var objType = typeof obj;
3331 if (objType == 'function' || (objType == 'object' && obj !== null)) {
3332 key = obj.$$hashKey = objType + ':' + (nextUidFn || nextUid)();
3334 key = objType + ':' + obj;
3341 * HashMap which can use objects as keys
3343 function HashMap(array, isolatedUid) {
3346 this.nextUid = function() {
3350 forEach(array, this.put, this);
3352 HashMap.prototype = {
3354 * Store key value pair
3355 * @param key key to store can be any type
3356 * @param value value to store can be any type
3358 put: function(key, value) {
3359 this[hashKey(key, this.nextUid)] = value;
3364 * @returns {Object} the value for the key
3366 get: function(key) {
3367 return this[hashKey(key, this.nextUid)];
3371 * Remove the key/value pair
3374 remove: function(key) {
3375 var value = this[key = hashKey(key, this.nextUid)];
3384 * @name angular.injector
3388 * Creates an injector object that can be used for retrieving services as well as for
3389 * dependency injection (see {@link guide/di dependency injection}).
3391 * @param {Array.<string|Function>} modules A list of module functions or their aliases. See
3392 * {@link angular.module}. The `ng` module must be explicitly added.
3393 * @param {boolean=} [strictDi=false] Whether the injector should be in strict mode, which
3394 * disallows argument name annotation inference.
3395 * @returns {injector} Injector object. See {@link auto.$injector $injector}.
3400 * // create an injector
3401 * var $injector = angular.injector(['ng']);
3403 * // use the injector to kick off your application
3404 * // use the type inference to auto inject arguments, or use implicit injection
3405 * $injector.invoke(function($rootScope, $compile, $document) {
3406 * $compile($document)($rootScope);
3407 * $rootScope.$digest();
3411 * Sometimes you want to get access to the injector of a currently running Angular app
3412 * from outside Angular. Perhaps, you want to inject and compile some markup after the
3413 * application has been bootstrapped. You can do this using the extra `injector()` added
3414 * to JQuery/jqLite elements. See {@link angular.element}.
3416 * *This is fairly rare but could be the case if a third party library is injecting the
3419 * In the following example a new block of HTML containing a `ng-controller`
3420 * directive is added to the end of the document body by JQuery. We then compile and link
3421 * it into the current AngularJS scope.
3424 * var $div = $('<div ng-controller="MyCtrl">{{content.label}}</div>');
3425 * $(document.body).append($div);
3427 * angular.element(document).injector().invoke(function($compile) {
3428 * var scope = angular.element($div).scope();
3429 * $compile($div)(scope);
3440 * Implicit module which gets automatically added to each {@link auto.$injector $injector}.
3443 var FN_ARGS = /^function\s*[^\(]*\(\s*([^\)]*)\)/m;
3444 var FN_ARG_SPLIT = /,/;
3445 var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/;
3446 var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
3447 var $injectorMinErr = minErr('$injector');
3449 function anonFn(fn) {
3450 // For anonymous functions, showing at the very least the function signature can help in
3452 var fnText = fn.toString().replace(STRIP_COMMENTS, ''),
3453 args = fnText.match(FN_ARGS);
3455 return 'function(' + (args[1] || '').replace(/[\s\r\n]+/, ' ') + ')';
3460 function annotate(fn, strictDi, name) {
3466 if (typeof fn === 'function') {
3467 if (!($inject = fn.$inject)) {
3471 if (!isString(name) || !name) {
3472 name = fn.name || anonFn(fn);
3474 throw $injectorMinErr('strictdi',
3475 '{0} is not using explicit annotation and cannot be invoked in strict mode', name);
3477 fnText = fn.toString().replace(STRIP_COMMENTS, '');
3478 argDecl = fnText.match(FN_ARGS);
3479 forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg) {
3480 arg.replace(FN_ARG, function(all, underscore, name) {
3485 fn.$inject = $inject;
3487 } else if (isArray(fn)) {
3488 last = fn.length - 1;
3489 assertArgFn(fn[last], 'fn');
3490 $inject = fn.slice(0, last);
3492 assertArgFn(fn, 'fn', true);
3497 ///////////////////////////////////////
3505 * `$injector` is used to retrieve object instances as defined by
3506 * {@link auto.$provide provider}, instantiate types, invoke methods,
3509 * The following always holds true:
3512 * var $injector = angular.injector();
3513 * expect($injector.get('$injector')).toBe($injector);
3514 * expect($injector.invoke(function($injector) {
3516 * })).toBe($injector);
3519 * # Injection Function Annotation
3521 * JavaScript does not have annotations, and annotations are needed for dependency injection. The
3522 * following are all valid ways of annotating function with injection arguments and are equivalent.
3525 * // inferred (only works if code not minified/obfuscated)
3526 * $injector.invoke(function(serviceA){});
3529 * function explicit(serviceA) {};
3530 * explicit.$inject = ['serviceA'];
3531 * $injector.invoke(explicit);
3534 * $injector.invoke(['serviceA', function(serviceA){}]);
3539 * In JavaScript calling `toString()` on a function returns the function definition. The definition
3540 * can then be parsed and the function arguments can be extracted. This method of discovering
3541 * annotations is disallowed when the injector is in strict mode.
3542 * *NOTE:* This does not work with minification, and obfuscation tools since these tools change the
3545 * ## `$inject` Annotation
3546 * By adding an `$inject` property onto a function the injection parameters can be specified.
3549 * As an array of injection names, where the last item in the array is the function to call.
3554 * @name $injector#get
3557 * Return an instance of the service.
3559 * @param {string} name The name of the instance to retrieve.
3560 * @param {string} caller An optional string to provide the origin of the function call for error messages.
3561 * @return {*} The instance.
3566 * @name $injector#invoke
3569 * Invoke the method and supply the method arguments from the `$injector`.
3571 * @param {!Function} fn The function to invoke. Function parameters are injected according to the
3572 * {@link guide/di $inject Annotation} rules.
3573 * @param {Object=} self The `this` for the invoked method.
3574 * @param {Object=} locals Optional object. If preset then any argument names are read from this
3575 * object first, before the `$injector` is consulted.
3576 * @returns {*} the value returned by the invoked `fn` function.
3581 * @name $injector#has
3584 * Allows the user to query if the particular service exists.
3586 * @param {string} name Name of the service to query.
3587 * @returns {boolean} `true` if injector has given service.
3592 * @name $injector#instantiate
3594 * Create a new instance of JS type. The method takes a constructor function, invokes the new
3595 * operator, and supplies all of the arguments to the constructor function as specified by the
3596 * constructor annotation.
3598 * @param {Function} Type Annotated constructor function.
3599 * @param {Object=} locals Optional object. If preset then any argument names are read from this
3600 * object first, before the `$injector` is consulted.
3601 * @returns {Object} new instance of `Type`.
3606 * @name $injector#annotate
3609 * Returns an array of service names which the function is requesting for injection. This API is
3610 * used by the injector to determine which services need to be injected into the function when the
3611 * function is invoked. There are three ways in which the function can be annotated with the needed
3616 * The simplest form is to extract the dependencies from the arguments of the function. This is done
3617 * by converting the function into a string using `toString()` method and extracting the argument
3621 * function MyController($scope, $route) {
3626 * expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
3629 * You can disallow this method by using strict injection mode.
3631 * This method does not work with code minification / obfuscation. For this reason the following
3632 * annotation strategies are supported.
3634 * # The `$inject` property
3636 * If a function has an `$inject` property and its value is an array of strings, then the strings
3637 * represent names of services to be injected into the function.
3640 * var MyController = function(obfuscatedScope, obfuscatedRoute) {
3643 * // Define function dependencies
3644 * MyController['$inject'] = ['$scope', '$route'];
3647 * expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
3650 * # The array notation
3652 * It is often desirable to inline Injected functions and that's when setting the `$inject` property
3653 * is very inconvenient. In these situations using the array notation to specify the dependencies in
3654 * a way that survives minification is a better choice:
3657 * // We wish to write this (not minification / obfuscation safe)
3658 * injector.invoke(function($compile, $rootScope) {
3662 * // We are forced to write break inlining
3663 * var tmpFn = function(obfuscatedCompile, obfuscatedRootScope) {
3666 * tmpFn.$inject = ['$compile', '$rootScope'];
3667 * injector.invoke(tmpFn);
3669 * // To better support inline function the inline annotation is supported
3670 * injector.invoke(['$compile', '$rootScope', function(obfCompile, obfRootScope) {
3675 * expect(injector.annotate(
3676 * ['$compile', '$rootScope', function(obfus_$compile, obfus_$rootScope) {}])
3677 * ).toEqual(['$compile', '$rootScope']);
3680 * @param {Function|Array.<string|Function>} fn Function for which dependent service names need to
3681 * be retrieved as described above.
3683 * @param {boolean=} [strictDi=false] Disallow argument name annotation inference.
3685 * @returns {Array.<string>} The names of the services which the function requires.
3697 * The {@link auto.$provide $provide} service has a number of methods for registering components
3698 * with the {@link auto.$injector $injector}. Many of these functions are also exposed on
3699 * {@link angular.Module}.
3701 * An Angular **service** is a singleton object created by a **service factory**. These **service
3702 * factories** are functions which, in turn, are created by a **service provider**.
3703 * The **service providers** are constructor functions. When instantiated they must contain a
3704 * property called `$get`, which holds the **service factory** function.
3706 * When you request a service, the {@link auto.$injector $injector} is responsible for finding the
3707 * correct **service provider**, instantiating it and then calling its `$get` **service factory**
3708 * function to get the instance of the **service**.
3710 * Often services have no configuration options and there is no need to add methods to the service
3711 * provider. The provider will be no more than a constructor function with a `$get` property. For
3712 * these cases the {@link auto.$provide $provide} service has additional helper methods to register
3713 * services without specifying a provider.
3715 * * {@link auto.$provide#provider provider(provider)} - registers a **service provider** with the
3716 * {@link auto.$injector $injector}
3717 * * {@link auto.$provide#constant constant(obj)} - registers a value/object that can be accessed by
3718 * providers and services.
3719 * * {@link auto.$provide#value value(obj)} - registers a value/object that can only be accessed by
3720 * services, not providers.
3721 * * {@link auto.$provide#factory factory(fn)} - registers a service **factory function**, `fn`,
3722 * that will be wrapped in a **service provider** object, whose `$get` property will contain the
3723 * given factory function.
3724 * * {@link auto.$provide#service service(class)} - registers a **constructor function**, `class`
3725 * that will be wrapped in a **service provider** object, whose `$get` property will instantiate
3726 * a new object using the given constructor function.
3728 * See the individual methods for more information and examples.
3733 * @name $provide#provider
3736 * Register a **provider function** with the {@link auto.$injector $injector}. Provider functions
3737 * are constructor functions, whose instances are responsible for "providing" a factory for a
3740 * Service provider names start with the name of the service they provide followed by `Provider`.
3741 * For example, the {@link ng.$log $log} service has a provider called
3742 * {@link ng.$logProvider $logProvider}.
3744 * Service provider objects can have additional methods which allow configuration of the provider
3745 * and its service. Importantly, you can configure what kind of service is created by the `$get`
3746 * method, or how that service will act. For example, the {@link ng.$logProvider $logProvider} has a
3747 * method {@link ng.$logProvider#debugEnabled debugEnabled}
3748 * which lets you specify whether the {@link ng.$log $log} service will log debug messages to the
3751 * @param {string} name The name of the instance. NOTE: the provider will be available under `name +
3753 * @param {(Object|function())} provider If the provider is:
3755 * - `Object`: then it should have a `$get` method. The `$get` method will be invoked using
3756 * {@link auto.$injector#invoke $injector.invoke()} when an instance needs to be created.
3757 * - `Constructor`: a new instance of the provider will be created using
3758 * {@link auto.$injector#instantiate $injector.instantiate()}, then treated as `object`.
3760 * @returns {Object} registered provider instance
3764 * The following example shows how to create a simple event tracking service and register it using
3765 * {@link auto.$provide#provider $provide.provider()}.
3768 * // Define the eventTracker provider
3769 * function EventTrackerProvider() {
3770 * var trackingUrl = '/track';
3772 * // A provider method for configuring where the tracked events should been saved
3773 * this.setTrackingUrl = function(url) {
3774 * trackingUrl = url;
3777 * // The service factory function
3778 * this.$get = ['$http', function($http) {
3779 * var trackedEvents = {};
3781 * // Call this to track an event
3782 * event: function(event) {
3783 * var count = trackedEvents[event] || 0;
3785 * trackedEvents[event] = count;
3788 * // Call this to save the tracked events to the trackingUrl
3789 * save: function() {
3790 * $http.post(trackingUrl, trackedEvents);
3796 * describe('eventTracker', function() {
3799 * beforeEach(module(function($provide) {
3800 * // Register the eventTracker provider
3801 * $provide.provider('eventTracker', EventTrackerProvider);
3804 * beforeEach(module(function(eventTrackerProvider) {
3805 * // Configure eventTracker provider
3806 * eventTrackerProvider.setTrackingUrl('/custom-track');
3809 * it('tracks events', inject(function(eventTracker) {
3810 * expect(eventTracker.event('login')).toEqual(1);
3811 * expect(eventTracker.event('login')).toEqual(2);
3814 * it('saves to the tracking url', inject(function(eventTracker, $http) {
3815 * postSpy = spyOn($http, 'post');
3816 * eventTracker.event('login');
3817 * eventTracker.save();
3818 * expect(postSpy).toHaveBeenCalled();
3819 * expect(postSpy.mostRecentCall.args[0]).not.toEqual('/track');
3820 * expect(postSpy.mostRecentCall.args[0]).toEqual('/custom-track');
3821 * expect(postSpy.mostRecentCall.args[1]).toEqual({ 'login': 1 });
3829 * @name $provide#factory
3832 * Register a **service factory**, which will be called to return the service instance.
3833 * This is short for registering a service where its provider consists of only a `$get` property,
3834 * which is the given service factory function.
3835 * You should use {@link auto.$provide#factory $provide.factory(getFn)} if you do not need to
3836 * configure your service in a provider.
3838 * @param {string} name The name of the instance.
3839 * @param {function()} $getFn The $getFn for the instance creation. Internally this is a short hand
3840 * for `$provide.provider(name, {$get: $getFn})`.
3841 * @returns {Object} registered provider instance
3844 * Here is an example of registering a service
3846 * $provide.factory('ping', ['$http', function($http) {
3847 * return function ping() {
3848 * return $http.send('/ping');
3852 * You would then inject and use this service like this:
3854 * someModule.controller('Ctrl', ['ping', function(ping) {
3863 * @name $provide#service
3866 * Register a **service constructor**, which will be invoked with `new` to create the service
3868 * This is short for registering a service where its provider's `$get` property is the service
3869 * constructor function that will be used to instantiate the service instance.
3871 * You should use {@link auto.$provide#service $provide.service(class)} if you define your service
3874 * @param {string} name The name of the instance.
3875 * @param {Function} constructor A class (constructor function) that will be instantiated.
3876 * @returns {Object} registered provider instance
3879 * Here is an example of registering a service using
3880 * {@link auto.$provide#service $provide.service(class)}.
3882 * var Ping = function($http) {
3883 * this.$http = $http;
3886 * Ping.$inject = ['$http'];
3888 * Ping.prototype.send = function() {
3889 * return this.$http.get('/ping');
3891 * $provide.service('ping', Ping);
3893 * You would then inject and use this service like this:
3895 * someModule.controller('Ctrl', ['ping', function(ping) {
3904 * @name $provide#value
3907 * Register a **value service** with the {@link auto.$injector $injector}, such as a string, a
3908 * number, an array, an object or a function. This is short for registering a service where its
3909 * provider's `$get` property is a factory function that takes no arguments and returns the **value
3912 * Value services are similar to constant services, except that they cannot be injected into a
3913 * module configuration function (see {@link angular.Module#config}) but they can be overridden by
3915 * {@link auto.$provide#decorator decorator}.
3917 * @param {string} name The name of the instance.
3918 * @param {*} value The value.
3919 * @returns {Object} registered provider instance
3922 * Here are some examples of creating value services.
3924 * $provide.value('ADMIN_USER', 'admin');
3926 * $provide.value('RoleLookup', { admin: 0, writer: 1, reader: 2 });
3928 * $provide.value('halfOf', function(value) {
3937 * @name $provide#constant
3940 * Register a **constant service**, such as a string, a number, an array, an object or a function,
3941 * with the {@link auto.$injector $injector}. Unlike {@link auto.$provide#value value} it can be
3942 * injected into a module configuration function (see {@link angular.Module#config}) and it cannot
3943 * be overridden by an Angular {@link auto.$provide#decorator decorator}.
3945 * @param {string} name The name of the constant.
3946 * @param {*} value The constant value.
3947 * @returns {Object} registered instance
3950 * Here a some examples of creating constants:
3952 * $provide.constant('SHARD_HEIGHT', 306);
3954 * $provide.constant('MY_COLOURS', ['red', 'blue', 'grey']);
3956 * $provide.constant('double', function(value) {
3965 * @name $provide#decorator
3968 * Register a **service decorator** with the {@link auto.$injector $injector}. A service decorator
3969 * intercepts the creation of a service, allowing it to override or modify the behaviour of the
3970 * service. The object returned by the decorator may be the original service, or a new service
3971 * object which replaces or wraps and delegates to the original service.
3973 * @param {string} name The name of the service to decorate.
3974 * @param {function()} decorator This function will be invoked when the service needs to be
3975 * instantiated and should return the decorated service instance. The function is called using
3976 * the {@link auto.$injector#invoke injector.invoke} method and is therefore fully injectable.
3977 * Local injection arguments:
3979 * * `$delegate` - The original service instance, which can be monkey patched, configured,
3980 * decorated or delegated to.
3983 * Here we decorate the {@link ng.$log $log} service to convert warnings to errors by intercepting
3984 * calls to {@link ng.$log#error $log.warn()}.
3986 * $provide.decorator('$log', ['$delegate', function($delegate) {
3987 * $delegate.warn = $delegate.error;
3994 function createInjector(modulesToLoad, strictDi) {
3995 strictDi = (strictDi === true);
3996 var INSTANTIATING = {},
3997 providerSuffix = 'Provider',
3999 loadedModules = new HashMap([], true),
4002 provider: supportObject(provider),
4003 factory: supportObject(factory),
4004 service: supportObject(service),
4005 value: supportObject(value),
4006 constant: supportObject(constant),
4007 decorator: decorator
4010 providerInjector = (providerCache.$injector =
4011 createInternalInjector(providerCache, function(serviceName, caller) {
4012 if (angular.isString(caller)) {
4015 throw $injectorMinErr('unpr', "Unknown provider: {0}", path.join(' <- '));
4018 instanceInjector = (instanceCache.$injector =
4019 createInternalInjector(instanceCache, function(serviceName, caller) {
4020 var provider = providerInjector.get(serviceName + providerSuffix, caller);
4021 return instanceInjector.invoke(provider.$get, provider, undefined, serviceName);
4025 forEach(loadModules(modulesToLoad), function(fn) { instanceInjector.invoke(fn || noop); });
4027 return instanceInjector;
4029 ////////////////////////////////////
4031 ////////////////////////////////////
4033 function supportObject(delegate) {
4034 return function(key, value) {
4035 if (isObject(key)) {
4036 forEach(key, reverseParams(delegate));
4038 return delegate(key, value);
4043 function provider(name, provider_) {
4044 assertNotHasOwnProperty(name, 'service');
4045 if (isFunction(provider_) || isArray(provider_)) {
4046 provider_ = providerInjector.instantiate(provider_);
4048 if (!provider_.$get) {
4049 throw $injectorMinErr('pget', "Provider '{0}' must define $get factory method.", name);
4051 return providerCache[name + providerSuffix] = provider_;
4054 function enforceReturnValue(name, factory) {
4055 return function enforcedReturnValue() {
4056 var result = instanceInjector.invoke(factory, this);
4057 if (isUndefined(result)) {
4058 throw $injectorMinErr('undef', "Provider '{0}' must return a value from $get factory method.", name);
4064 function factory(name, factoryFn, enforce) {
4065 return provider(name, {
4066 $get: enforce !== false ? enforceReturnValue(name, factoryFn) : factoryFn
4070 function service(name, constructor) {
4071 return factory(name, ['$injector', function($injector) {
4072 return $injector.instantiate(constructor);
4076 function value(name, val) { return factory(name, valueFn(val), false); }
4078 function constant(name, value) {
4079 assertNotHasOwnProperty(name, 'constant');
4080 providerCache[name] = value;
4081 instanceCache[name] = value;
4084 function decorator(serviceName, decorFn) {
4085 var origProvider = providerInjector.get(serviceName + providerSuffix),
4086 orig$get = origProvider.$get;
4088 origProvider.$get = function() {
4089 var origInstance = instanceInjector.invoke(orig$get, origProvider);
4090 return instanceInjector.invoke(decorFn, null, {$delegate: origInstance});
4094 ////////////////////////////////////
4096 ////////////////////////////////////
4097 function loadModules(modulesToLoad) {
4098 var runBlocks = [], moduleFn;
4099 forEach(modulesToLoad, function(module) {
4100 if (loadedModules.get(module)) return;
4101 loadedModules.put(module, true);
4103 function runInvokeQueue(queue) {
4105 for (i = 0, ii = queue.length; i < ii; i++) {
4106 var invokeArgs = queue[i],
4107 provider = providerInjector.get(invokeArgs[0]);
4109 provider[invokeArgs[1]].apply(provider, invokeArgs[2]);
4114 if (isString(module)) {
4115 moduleFn = angularModule(module);
4116 runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks);
4117 runInvokeQueue(moduleFn._invokeQueue);
4118 runInvokeQueue(moduleFn._configBlocks);
4119 } else if (isFunction(module)) {
4120 runBlocks.push(providerInjector.invoke(module));
4121 } else if (isArray(module)) {
4122 runBlocks.push(providerInjector.invoke(module));
4124 assertArgFn(module, 'module');
4127 if (isArray(module)) {
4128 module = module[module.length - 1];
4130 if (e.message && e.stack && e.stack.indexOf(e.message) == -1) {
4131 // Safari & FF's stack traces don't contain error.message content
4132 // unlike those of Chrome and IE
4133 // So if stack doesn't contain message, we create a new string that contains both.
4134 // Since error.stack is read-only in Safari, I'm overriding e and not e.stack here.
4136 e = e.message + '\n' + e.stack;
4138 throw $injectorMinErr('modulerr', "Failed to instantiate module {0} due to:\n{1}",
4139 module, e.stack || e.message || e);
4145 ////////////////////////////////////
4146 // internal Injector
4147 ////////////////////////////////////
4149 function createInternalInjector(cache, factory) {
4151 function getService(serviceName, caller) {
4152 if (cache.hasOwnProperty(serviceName)) {
4153 if (cache[serviceName] === INSTANTIATING) {
4154 throw $injectorMinErr('cdep', 'Circular dependency found: {0}',
4155 serviceName + ' <- ' + path.join(' <- '));
4157 return cache[serviceName];
4160 path.unshift(serviceName);
4161 cache[serviceName] = INSTANTIATING;
4162 return cache[serviceName] = factory(serviceName, caller);
4164 if (cache[serviceName] === INSTANTIATING) {
4165 delete cache[serviceName];
4174 function invoke(fn, self, locals, serviceName) {
4175 if (typeof locals === 'string') {
4176 serviceName = locals;
4181 $inject = createInjector.$$annotate(fn, strictDi, serviceName),
4185 for (i = 0, length = $inject.length; i < length; i++) {
4187 if (typeof key !== 'string') {
4188 throw $injectorMinErr('itkn',
4189 'Incorrect injection token! Expected service name as string, got {0}', key);
4192 locals && locals.hasOwnProperty(key)
4194 : getService(key, serviceName)
4201 // http://jsperf.com/angularjs-invoke-apply-vs-switch
4203 return fn.apply(self, args);
4206 function instantiate(Type, locals, serviceName) {
4207 // Check if Type is annotated and use just the given function at n-1 as parameter
4208 // e.g. someModule.factory('greeter', ['$window', function(renamed$window) {}]);
4209 // Object creation: http://jsperf.com/create-constructor/2
4210 var instance = Object.create((isArray(Type) ? Type[Type.length - 1] : Type).prototype || null);
4211 var returnedValue = invoke(Type, instance, locals, serviceName);
4213 return isObject(returnedValue) || isFunction(returnedValue) ? returnedValue : instance;
4218 instantiate: instantiate,
4220 annotate: createInjector.$$annotate,
4221 has: function(name) {
4222 return providerCache.hasOwnProperty(name + providerSuffix) || cache.hasOwnProperty(name);
4228 createInjector.$$annotate = annotate;
4232 * @name $anchorScrollProvider
4235 * Use `$anchorScrollProvider` to disable automatic scrolling whenever
4236 * {@link ng.$location#hash $location.hash()} changes.
4238 function $AnchorScrollProvider() {
4240 var autoScrollingEnabled = true;
4244 * @name $anchorScrollProvider#disableAutoScrolling
4247 * By default, {@link ng.$anchorScroll $anchorScroll()} will automatically detect changes to
4248 * {@link ng.$location#hash $location.hash()} and scroll to the element matching the new hash.<br />
4249 * Use this method to disable automatic scrolling.
4251 * If automatic scrolling is disabled, one must explicitly call
4252 * {@link ng.$anchorScroll $anchorScroll()} in order to scroll to the element related to the
4255 this.disableAutoScrolling = function() {
4256 autoScrollingEnabled = false;
4261 * @name $anchorScroll
4264 * @requires $location
4265 * @requires $rootScope
4268 * When called, it checks the current value of {@link ng.$location#hash $location.hash()} and
4269 * scrolls to the related element, according to the rules specified in the
4270 * [Html5 spec](http://dev.w3.org/html5/spec/Overview.html#the-indicated-part-of-the-document).
4272 * It also watches the {@link ng.$location#hash $location.hash()} and automatically scrolls to
4273 * match any anchor whenever it changes. This can be disabled by calling
4274 * {@link ng.$anchorScrollProvider#disableAutoScrolling $anchorScrollProvider.disableAutoScrolling()}.
4276 * Additionally, you can use its {@link ng.$anchorScroll#yOffset yOffset} property to specify a
4277 * vertical scroll-offset (either fixed or dynamic).
4279 * @property {(number|function|jqLite)} yOffset
4280 * If set, specifies a vertical scroll-offset. This is often useful when there are fixed
4281 * positioned elements at the top of the page, such as navbars, headers etc.
4283 * `yOffset` can be specified in various ways:
4284 * - **number**: A fixed number of pixels to be used as offset.<br /><br />
4285 * - **function**: A getter function called everytime `$anchorScroll()` is executed. Must return
4286 * a number representing the offset (in pixels).<br /><br />
4287 * - **jqLite**: A jqLite/jQuery element to be used for specifying the offset. The distance from
4288 * the top of the page to the element's bottom will be used as offset.<br />
4289 * **Note**: The element will be taken into account only as long as its `position` is set to
4290 * `fixed`. This option is useful, when dealing with responsive navbars/headers that adjust
4291 * their height and/or positioning according to the viewport's size.
4294 * <div class="alert alert-warning">
4295 * In order for `yOffset` to work properly, scrolling should take place on the document's root and
4296 * not some child element.
4300 <example module="anchorScrollExample">
4301 <file name="index.html">
4302 <div id="scrollArea" ng-controller="ScrollController">
4303 <a ng-click="gotoBottom()">Go to bottom</a>
4304 <a id="bottom"></a> You're at the bottom!
4307 <file name="script.js">
4308 angular.module('anchorScrollExample', [])
4309 .controller('ScrollController', ['$scope', '$location', '$anchorScroll',
4310 function ($scope, $location, $anchorScroll) {
4311 $scope.gotoBottom = function() {
4312 // set the location.hash to the id of
4313 // the element you wish to scroll to.
4314 $location.hash('bottom');
4316 // call $anchorScroll()
4321 <file name="style.css">
4335 * The example below illustrates the use of a vertical scroll-offset (specified as a fixed value).
4336 * See {@link ng.$anchorScroll#yOffset $anchorScroll.yOffset} for more details.
4339 <example module="anchorScrollOffsetExample">
4340 <file name="index.html">
4341 <div class="fixed-header" ng-controller="headerCtrl">
4342 <a href="" ng-click="gotoAnchor(x)" ng-repeat="x in [1,2,3,4,5]">
4346 <div id="anchor{{x}}" class="anchor" ng-repeat="x in [1,2,3,4,5]">
4350 <file name="script.js">
4351 angular.module('anchorScrollOffsetExample', [])
4352 .run(['$anchorScroll', function($anchorScroll) {
4353 $anchorScroll.yOffset = 50; // always scroll by 50 extra pixels
4355 .controller('headerCtrl', ['$anchorScroll', '$location', '$scope',
4356 function ($anchorScroll, $location, $scope) {
4357 $scope.gotoAnchor = function(x) {
4358 var newHash = 'anchor' + x;
4359 if ($location.hash() !== newHash) {
4360 // set the $location.hash to `newHash` and
4361 // $anchorScroll will automatically scroll to it
4362 $location.hash('anchor' + x);
4364 // call $anchorScroll() explicitly,
4365 // since $location.hash hasn't changed
4372 <file name="style.css">
4378 border: 2px dashed DarkOrchid;
4379 padding: 10px 10px 200px 10px;
4383 background-color: rgba(0, 0, 0, 0.2);
4386 top: 0; left: 0; right: 0;
4390 display: inline-block;
4396 this.$get = ['$window', '$location', '$rootScope', function($window, $location, $rootScope) {
4397 var document = $window.document;
4399 // Helper function to get first anchor from a NodeList
4400 // (using `Array#some()` instead of `angular#forEach()` since it's more performant
4401 // and working in all supported browsers.)
4402 function getFirstAnchor(list) {
4404 Array.prototype.some.call(list, function(element) {
4405 if (nodeName_(element) === 'a') {
4413 function getYOffset() {
4415 var offset = scroll.yOffset;
4417 if (isFunction(offset)) {
4419 } else if (isElement(offset)) {
4420 var elem = offset[0];
4421 var style = $window.getComputedStyle(elem);
4422 if (style.position !== 'fixed') {
4425 offset = elem.getBoundingClientRect().bottom;
4427 } else if (!isNumber(offset)) {
4434 function scrollTo(elem) {
4436 elem.scrollIntoView();
4438 var offset = getYOffset();
4441 // `offset` is the number of pixels we should scroll UP in order to align `elem` properly.
4442 // This is true ONLY if the call to `elem.scrollIntoView()` initially aligns `elem` at the
4443 // top of the viewport.
4445 // IF the number of pixels from the top of `elem` to the end of the page's content is less
4446 // than the height of the viewport, then `elem.scrollIntoView()` will align the `elem` some
4447 // way down the page.
4449 // This is often the case for elements near the bottom of the page.
4451 // In such cases we do not need to scroll the whole `offset` up, just the difference between
4452 // the top of the element and the offset, which is enough to align the top of `elem` at the
4453 // desired position.
4454 var elemTop = elem.getBoundingClientRect().top;
4455 $window.scrollBy(0, elemTop - offset);
4458 $window.scrollTo(0, 0);
4463 var hash = $location.hash(), elm;
4465 // empty hash, scroll to the top of the page
4466 if (!hash) scrollTo(null);
4468 // element with given id
4469 else if ((elm = document.getElementById(hash))) scrollTo(elm);
4471 // first anchor with given name :-D
4472 else if ((elm = getFirstAnchor(document.getElementsByName(hash)))) scrollTo(elm);
4474 // no element and hash == 'top', scroll to the top of the page
4475 else if (hash === 'top') scrollTo(null);
4478 // does not scroll when user clicks on anchor link that is currently on
4479 // (no url change, no $location.hash() change), browser native does scroll
4480 if (autoScrollingEnabled) {
4481 $rootScope.$watch(function autoScrollWatch() {return $location.hash();},
4482 function autoScrollWatchAction(newVal, oldVal) {
4483 // skip the initial scroll if $location.hash is empty
4484 if (newVal === oldVal && newVal === '') return;
4486 jqLiteDocumentLoaded(function() {
4487 $rootScope.$evalAsync(scroll);
4496 var $animateMinErr = minErr('$animate');
4500 * @name $animateProvider
4503 * Default implementation of $animate that doesn't perform any animations, instead just
4504 * synchronously performs DOM
4505 * updates and calls done() callbacks.
4507 * In order to enable animations the ngAnimate module has to be loaded.
4509 * To see the functional implementation check out src/ngAnimate/animate.js
4511 var $AnimateProvider = ['$provide', function($provide) {
4514 this.$$selectors = {};
4519 * @name $animateProvider#register
4522 * Registers a new injectable animation factory function. The factory function produces the
4523 * animation object which contains callback functions for each event that is expected to be
4526 * * `eventFn`: `function(Element, doneFunction)` The element to animate, the `doneFunction`
4527 * must be called once the element animation is complete. If a function is returned then the
4528 * animation service will use this function to cancel the animation whenever a cancel event is
4534 * eventFn : function(element, done) {
4535 * //code to run the animation
4536 * //once complete, then run done()
4537 * return function cancellationFunction() {
4538 * //code to cancel the animation
4544 * @param {string} name The name of the animation.
4545 * @param {Function} factory The factory function that will be executed to return the animation
4548 this.register = function(name, factory) {
4549 var key = name + '-animation';
4550 if (name && name.charAt(0) != '.') throw $animateMinErr('notcsel',
4551 "Expecting class selector starting with '.' got '{0}'.", name);
4552 this.$$selectors[name.substr(1)] = key;
4553 $provide.factory(key, factory);
4558 * @name $animateProvider#classNameFilter
4561 * Sets and/or returns the CSS class regular expression that is checked when performing
4562 * an animation. Upon bootstrap the classNameFilter value is not set at all and will
4563 * therefore enable $animate to attempt to perform an animation on any element.
4564 * When setting the classNameFilter value, animations will only be performed on elements
4565 * that successfully match the filter expression. This in turn can boost performance
4566 * for low-powered devices as well as applications containing a lot of structural operations.
4567 * @param {RegExp=} expression The className expression which will be checked against all animations
4568 * @return {RegExp} The current CSS className expression value. If null then there is no expression value
4570 this.classNameFilter = function(expression) {
4571 if (arguments.length === 1) {
4572 this.$$classNameFilter = (expression instanceof RegExp) ? expression : null;
4574 return this.$$classNameFilter;
4577 this.$get = ['$$q', '$$asyncCallback', '$rootScope', function($$q, $$asyncCallback, $rootScope) {
4581 function runAnimationPostDigest(fn) {
4582 var cancelFn, defer = $$q.defer();
4583 defer.promise.$$cancelFn = function ngAnimateMaybeCancel() {
4584 cancelFn && cancelFn();
4587 $rootScope.$$postDigest(function ngAnimatePostDigest() {
4588 cancelFn = fn(function ngAnimateNotifyComplete() {
4593 return defer.promise;
4596 function resolveElementClasses(element, classes) {
4597 var toAdd = [], toRemove = [];
4599 var hasClasses = createMap();
4600 forEach((element.attr('class') || '').split(/\s+/), function(className) {
4601 hasClasses[className] = true;
4604 forEach(classes, function(status, className) {
4605 var hasClass = hasClasses[className];
4607 // If the most recent class manipulation (via $animate) was to remove the class, and the
4608 // element currently has the class, the class is scheduled for removal. Otherwise, if
4609 // the most recent class manipulation (via $animate) was to add the class, and the
4610 // element does not currently have the class, the class is scheduled to be added.
4611 if (status === false && hasClass) {
4612 toRemove.push(className);
4613 } else if (status === true && !hasClass) {
4614 toAdd.push(className);
4618 return (toAdd.length + toRemove.length) > 0 &&
4619 [toAdd.length ? toAdd : null, toRemove.length ? toRemove : null];
4622 function cachedClassManipulation(cache, classes, op) {
4623 for (var i=0, ii = classes.length; i < ii; ++i) {
4624 var className = classes[i];
4625 cache[className] = op;
4629 function asyncPromise() {
4630 // only serve one instance of a promise in order to save CPU cycles
4631 if (!currentDefer) {
4632 currentDefer = $$q.defer();
4633 $$asyncCallback(function() {
4634 currentDefer.resolve();
4635 currentDefer = null;
4638 return currentDefer.promise;
4641 function applyStyles(element, options) {
4642 if (angular.isObject(options)) {
4643 var styles = extend(options.from || {}, options.to || {});
4644 element.css(styles);
4652 * @description The $animate service provides rudimentary DOM manipulation functions to
4653 * insert, remove and move elements within the DOM, as well as adding and removing classes.
4654 * This service is the core service used by the ngAnimate $animator service which provides
4655 * high-level animation hooks for CSS and JavaScript.
4657 * $animate is available in the AngularJS core, however, the ngAnimate module must be included
4658 * to enable full out animation support. Otherwise, $animate will only perform simple DOM
4659 * manipulation operations.
4661 * To learn more about enabling animation support, click here to visit the {@link ngAnimate
4662 * ngAnimate module page} as well as the {@link ngAnimate.$animate ngAnimate $animate service
4666 animate: function(element, from, to) {
4667 applyStyles(element, { from: from, to: to });
4668 return asyncPromise();
4674 * @name $animate#enter
4676 * @description Inserts the element into the DOM either after the `after` element or
4677 * as the first child within the `parent` element. When the function is called a promise
4678 * is returned that will be resolved at a later time.
4679 * @param {DOMElement} element the element which will be inserted into the DOM
4680 * @param {DOMElement} parent the parent element which will append the element as
4681 * a child (if the after element is not present)
4682 * @param {DOMElement} after the sibling element which will append the element
4684 * @param {object=} options an optional collection of styles that will be applied to the element.
4685 * @return {Promise} the animation callback promise
4687 enter: function(element, parent, after, options) {
4688 applyStyles(element, options);
4689 after ? after.after(element)
4690 : parent.prepend(element);
4691 return asyncPromise();
4697 * @name $animate#leave
4699 * @description Removes the element from the DOM. When the function is called a promise
4700 * is returned that will be resolved at a later time.
4701 * @param {DOMElement} element the element which will be removed from the DOM
4702 * @param {object=} options an optional collection of options that will be applied to the element.
4703 * @return {Promise} the animation callback promise
4705 leave: function(element, options) {
4706 applyStyles(element, options);
4708 return asyncPromise();
4714 * @name $animate#move
4716 * @description Moves the position of the provided element within the DOM to be placed
4717 * either after the `after` element or inside of the `parent` element. When the function
4718 * is called a promise is returned that will be resolved at a later time.
4720 * @param {DOMElement} element the element which will be moved around within the
4722 * @param {DOMElement} parent the parent element where the element will be
4723 * inserted into (if the after element is not present)
4724 * @param {DOMElement} after the sibling element where the element will be
4725 * positioned next to
4726 * @param {object=} options an optional collection of options that will be applied to the element.
4727 * @return {Promise} the animation callback promise
4729 move: function(element, parent, after, options) {
4730 // Do not remove element before insert. Removing will cause data associated with the
4731 // element to be dropped. Insert will implicitly do the remove.
4732 return this.enter(element, parent, after, options);
4738 * @name $animate#addClass
4740 * @description Adds the provided className CSS class value to the provided element.
4741 * When the function is called a promise is returned that will be resolved at a later time.
4742 * @param {DOMElement} element the element which will have the className value
4744 * @param {string} className the CSS class which will be added to the element
4745 * @param {object=} options an optional collection of options that will be applied to the element.
4746 * @return {Promise} the animation callback promise
4748 addClass: function(element, className, options) {
4749 return this.setClass(element, className, [], options);
4752 $$addClassImmediately: function(element, className, options) {
4753 element = jqLite(element);
4754 className = !isString(className)
4755 ? (isArray(className) ? className.join(' ') : '')
4757 forEach(element, function(element) {
4758 jqLiteAddClass(element, className);
4760 applyStyles(element, options);
4761 return asyncPromise();
4767 * @name $animate#removeClass
4769 * @description Removes the provided className CSS class value from the provided element.
4770 * When the function is called a promise is returned that will be resolved at a later time.
4771 * @param {DOMElement} element the element which will have the className value
4773 * @param {string} className the CSS class which will be removed from the element
4774 * @param {object=} options an optional collection of options that will be applied to the element.
4775 * @return {Promise} the animation callback promise
4777 removeClass: function(element, className, options) {
4778 return this.setClass(element, [], className, options);
4781 $$removeClassImmediately: function(element, className, options) {
4782 element = jqLite(element);
4783 className = !isString(className)
4784 ? (isArray(className) ? className.join(' ') : '')
4786 forEach(element, function(element) {
4787 jqLiteRemoveClass(element, className);
4789 applyStyles(element, options);
4790 return asyncPromise();
4796 * @name $animate#setClass
4798 * @description Adds and/or removes the given CSS classes to and from the element.
4799 * When the function is called a promise is returned that will be resolved at a later time.
4800 * @param {DOMElement} element the element which will have its CSS classes changed
4802 * @param {string} add the CSS classes which will be added to the element
4803 * @param {string} remove the CSS class which will be removed from the element
4804 * @param {object=} options an optional collection of options that will be applied to the element.
4805 * @return {Promise} the animation callback promise
4807 setClass: function(element, add, remove, options) {
4809 var STORAGE_KEY = '$$animateClasses';
4810 var createdCache = false;
4811 element = jqLite(element);
4813 var cache = element.data(STORAGE_KEY);
4819 createdCache = true;
4820 } else if (options && cache.options) {
4821 cache.options = angular.extend(cache.options || {}, options);
4824 var classes = cache.classes;
4826 add = isArray(add) ? add : add.split(' ');
4827 remove = isArray(remove) ? remove : remove.split(' ');
4828 cachedClassManipulation(classes, add, true);
4829 cachedClassManipulation(classes, remove, false);
4832 cache.promise = runAnimationPostDigest(function(done) {
4833 var cache = element.data(STORAGE_KEY);
4834 element.removeData(STORAGE_KEY);
4836 // in the event that the element is removed before postDigest
4837 // is run then the cache will be undefined and there will be
4838 // no need anymore to add or remove and of the element classes
4840 var classes = resolveElementClasses(element, cache.classes);
4842 self.$$setClassImmediately(element, classes[0], classes[1], cache.options);
4848 element.data(STORAGE_KEY, cache);
4851 return cache.promise;
4854 $$setClassImmediately: function(element, add, remove, options) {
4855 add && this.$$addClassImmediately(element, add);
4856 remove && this.$$removeClassImmediately(element, remove);
4857 applyStyles(element, options);
4858 return asyncPromise();
4867 function $$AsyncCallbackProvider() {
4868 this.$get = ['$$rAF', '$timeout', function($$rAF, $timeout) {
4869 return $$rAF.supported
4870 ? function(fn) { return $$rAF(fn); }
4872 return $timeout(fn, 0, false);
4877 /* global stripHash: true */
4880 * ! This is a private undocumented service !
4885 * This object has two goals:
4887 * - hide all the global state in the browser caused by the window object
4888 * - abstract away all the browser specific features and inconsistencies
4890 * For tests we provide {@link ngMock.$browser mock implementation} of the `$browser`
4891 * service, which can be used for convenient testing of the application without the interaction with
4892 * the real browser apis.
4895 * @param {object} window The global window object.
4896 * @param {object} document jQuery wrapped document.
4897 * @param {object} $log window.console or an object with the same interface.
4898 * @param {object} $sniffer $sniffer service
4900 function Browser(window, document, $log, $sniffer) {
4902 rawDocument = document[0],
4903 location = window.location,
4904 history = window.history,
4905 setTimeout = window.setTimeout,
4906 clearTimeout = window.clearTimeout,
4907 pendingDeferIds = {};
4909 self.isMock = false;
4911 var outstandingRequestCount = 0;
4912 var outstandingRequestCallbacks = [];
4914 // TODO(vojta): remove this temporary api
4915 self.$$completeOutstandingRequest = completeOutstandingRequest;
4916 self.$$incOutstandingRequestCount = function() { outstandingRequestCount++; };
4919 * Executes the `fn` function(supports currying) and decrements the `outstandingRequestCallbacks`
4920 * counter. If the counter reaches 0, all the `outstandingRequestCallbacks` are executed.
4922 function completeOutstandingRequest(fn) {
4924 fn.apply(null, sliceArgs(arguments, 1));
4926 outstandingRequestCount--;
4927 if (outstandingRequestCount === 0) {
4928 while (outstandingRequestCallbacks.length) {
4930 outstandingRequestCallbacks.pop()();
4939 function getHash(url) {
4940 var index = url.indexOf('#');
4941 return index === -1 ? '' : url.substr(index + 1);
4946 * Note: this method is used only by scenario runner
4947 * TODO(vojta): prefix this method with $$ ?
4948 * @param {function()} callback Function that will be called when no outstanding request
4950 self.notifyWhenNoOutstandingRequests = function(callback) {
4951 // force browser to execute all pollFns - this is needed so that cookies and other pollers fire
4952 // at some deterministic time in respect to the test runner's actions. Leaving things up to the
4953 // regular poller would result in flaky tests.
4954 forEach(pollFns, function(pollFn) { pollFn(); });
4956 if (outstandingRequestCount === 0) {
4959 outstandingRequestCallbacks.push(callback);
4963 //////////////////////////////////////////////////////////////
4965 //////////////////////////////////////////////////////////////
4970 * @name $browser#addPollFn
4972 * @param {function()} fn Poll function to add
4975 * Adds a function to the list of functions that poller periodically executes,
4976 * and starts polling if not started yet.
4978 * @returns {function()} the added function
4980 self.addPollFn = function(fn) {
4981 if (isUndefined(pollTimeout)) startPoller(100, setTimeout);
4987 * @param {number} interval How often should browser call poll functions (ms)
4988 * @param {function()} setTimeout Reference to a real or fake `setTimeout` function.
4991 * Configures the poller to run in the specified intervals, using the specified
4992 * setTimeout fn and kicks it off.
4994 function startPoller(interval, setTimeout) {
4996 forEach(pollFns, function(pollFn) { pollFn(); });
4997 pollTimeout = setTimeout(check, interval);
5001 //////////////////////////////////////////////////////////////
5003 //////////////////////////////////////////////////////////////
5005 var cachedState, lastHistoryState,
5006 lastBrowserUrl = location.href,
5007 baseElement = document.find('base'),
5008 reloadLocation = null;
5011 lastHistoryState = cachedState;
5014 * @name $browser#url
5018 * Without any argument, this method just returns current value of location.href.
5021 * With at least one argument, this method sets url to new value.
5022 * If html5 history api supported, pushState/replaceState is used, otherwise
5023 * location.href/location.replace is used.
5024 * Returns its own instance to allow chaining
5026 * NOTE: this api is intended for use only by the $location service. Please use the
5027 * {@link ng.$location $location service} to change url.
5029 * @param {string} url New url (when used as setter)
5030 * @param {boolean=} replace Should new url replace current history record?
5031 * @param {object=} state object to use with pushState/replaceState
5033 self.url = function(url, replace, state) {
5034 // In modern browsers `history.state` is `null` by default; treating it separately
5035 // from `undefined` would cause `$browser.url('/foo')` to change `history.state`
5036 // to undefined via `pushState`. Instead, let's change `undefined` to `null` here.
5037 if (isUndefined(state)) {
5041 // Android Browser BFCache causes location, history reference to become stale.
5042 if (location !== window.location) location = window.location;
5043 if (history !== window.history) history = window.history;
5047 var sameState = lastHistoryState === state;
5049 // Don't change anything if previous and current URLs and states match. This also prevents
5050 // IE<10 from getting into redirect loop when in LocationHashbangInHtml5Url mode.
5051 // See https://github.com/angular/angular.js/commit/ffb2701
5052 if (lastBrowserUrl === url && (!$sniffer.history || sameState)) {
5055 var sameBase = lastBrowserUrl && stripHash(lastBrowserUrl) === stripHash(url);
5056 lastBrowserUrl = url;
5057 lastHistoryState = state;
5058 // Don't use history API if only the hash changed
5059 // due to a bug in IE10/IE11 which leads
5060 // to not firing a `hashchange` nor `popstate` event
5061 // in some cases (see #9143).
5062 if ($sniffer.history && (!sameBase || !sameState)) {
5063 history[replace ? 'replaceState' : 'pushState'](state, '', url);
5065 // Do the assignment again so that those two variables are referentially identical.
5066 lastHistoryState = cachedState;
5069 reloadLocation = url;
5072 location.replace(url);
5073 } else if (!sameBase) {
5074 location.href = url;
5076 location.hash = getHash(url);
5082 // - reloadLocation is needed as browsers don't allow to read out
5083 // the new location.href if a reload happened.
5084 // - the replacement is a workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=407172
5085 return reloadLocation || location.href.replace(/%27/g,"'");
5090 * @name $browser#state
5093 * This method is a getter.
5095 * Return history.state or null if history.state is undefined.
5097 * @returns {object} state
5099 self.state = function() {
5103 var urlChangeListeners = [],
5104 urlChangeInit = false;
5106 function cacheStateAndFireUrlChange() {
5111 function getCurrentState() {
5113 return history.state;
5115 // MSIE can reportedly throw when there is no state (UNCONFIRMED).
5119 // This variable should be used *only* inside the cacheState function.
5120 var lastCachedState = null;
5121 function cacheState() {
5122 // This should be the only place in $browser where `history.state` is read.
5123 cachedState = getCurrentState();
5124 cachedState = isUndefined(cachedState) ? null : cachedState;
5126 // Prevent callbacks fo fire twice if both hashchange & popstate were fired.
5127 if (equals(cachedState, lastCachedState)) {
5128 cachedState = lastCachedState;
5130 lastCachedState = cachedState;
5133 function fireUrlChange() {
5134 if (lastBrowserUrl === self.url() && lastHistoryState === cachedState) {
5138 lastBrowserUrl = self.url();
5139 lastHistoryState = cachedState;
5140 forEach(urlChangeListeners, function(listener) {
5141 listener(self.url(), cachedState);
5146 * @name $browser#onUrlChange
5149 * Register callback function that will be called, when url changes.
5151 * It's only called when the url is changed from outside of angular:
5152 * - user types different url into address bar
5153 * - user clicks on history (forward/back) button
5154 * - user clicks on a link
5156 * It's not called when url is changed by $browser.url() method
5158 * The listener gets called with new url as parameter.
5160 * NOTE: this api is intended for use only by the $location service. Please use the
5161 * {@link ng.$location $location service} to monitor url changes in angular apps.
5163 * @param {function(string)} listener Listener function to be called when url changes.
5164 * @return {function(string)} Returns the registered listener fn - handy if the fn is anonymous.
5166 self.onUrlChange = function(callback) {
5167 // TODO(vojta): refactor to use node's syntax for events
5168 if (!urlChangeInit) {
5169 // We listen on both (hashchange/popstate) when available, as some browsers (e.g. Opera)
5170 // don't fire popstate when user change the address bar and don't fire hashchange when url
5171 // changed by push/replaceState
5173 // html5 history api - popstate event
5174 if ($sniffer.history) jqLite(window).on('popstate', cacheStateAndFireUrlChange);
5176 jqLite(window).on('hashchange', cacheStateAndFireUrlChange);
5178 urlChangeInit = true;
5181 urlChangeListeners.push(callback);
5186 * Checks whether the url has changed outside of Angular.
5187 * Needs to be exported to be able to check for changes that have been done in sync,
5188 * as hashchange/popstate events fire in async.
5190 self.$$checkUrlChange = fireUrlChange;
5192 //////////////////////////////////////////////////////////////
5194 //////////////////////////////////////////////////////////////
5197 * @name $browser#baseHref
5200 * Returns current <base href>
5201 * (always relative - without domain)
5203 * @returns {string} The current base href
5205 self.baseHref = function() {
5206 var href = baseElement.attr('href');
5207 return href ? href.replace(/^(https?\:)?\/\/[^\/]*/, '') : '';
5210 //////////////////////////////////////////////////////////////
5212 //////////////////////////////////////////////////////////////
5213 var lastCookies = {};
5214 var lastCookieString = '';
5215 var cookiePath = self.baseHref();
5217 function safeDecodeURIComponent(str) {
5219 return decodeURIComponent(str);
5226 * @name $browser#cookies
5228 * @param {string=} name Cookie name
5229 * @param {string=} value Cookie value
5232 * The cookies method provides a 'private' low level access to browser cookies.
5233 * It is not meant to be used directly, use the $cookie service instead.
5235 * The return values vary depending on the arguments that the method was called with as follows:
5237 * - cookies() -> hash of all cookies, this is NOT a copy of the internal state, so do not modify
5239 * - cookies(name, value) -> set name to value, if value is undefined delete the cookie
5240 * - cookies(name) -> the same as (name, undefined) == DELETES (no one calls it right now that
5243 * @returns {Object} Hash of all cookies (if called without any parameter)
5245 self.cookies = function(name, value) {
5246 var cookieLength, cookieArray, cookie, i, index;
5249 if (value === undefined) {
5250 rawDocument.cookie = encodeURIComponent(name) + "=;path=" + cookiePath +
5251 ";expires=Thu, 01 Jan 1970 00:00:00 GMT";
5253 if (isString(value)) {
5254 cookieLength = (rawDocument.cookie = encodeURIComponent(name) + '=' + encodeURIComponent(value) +
5255 ';path=' + cookiePath).length + 1;
5257 // per http://www.ietf.org/rfc/rfc2109.txt browser must allow at minimum:
5259 // - 20 cookies per unique domain
5260 // - 4096 bytes per cookie
5261 if (cookieLength > 4096) {
5262 $log.warn("Cookie '" + name +
5263 "' possibly not set or overflowed because it was too large (" +
5264 cookieLength + " > 4096 bytes)!");
5269 if (rawDocument.cookie !== lastCookieString) {
5270 lastCookieString = rawDocument.cookie;
5271 cookieArray = lastCookieString.split("; ");
5274 for (i = 0; i < cookieArray.length; i++) {
5275 cookie = cookieArray[i];
5276 index = cookie.indexOf('=');
5277 if (index > 0) { //ignore nameless cookies
5278 name = safeDecodeURIComponent(cookie.substring(0, index));
5279 // the first value that is seen for a cookie is the most
5280 // specific one. values for the same cookie name that
5281 // follow are for less specific paths.
5282 if (lastCookies[name] === undefined) {
5283 lastCookies[name] = safeDecodeURIComponent(cookie.substring(index + 1));
5294 * @name $browser#defer
5295 * @param {function()} fn A function, who's execution should be deferred.
5296 * @param {number=} [delay=0] of milliseconds to defer the function execution.
5297 * @returns {*} DeferId that can be used to cancel the task via `$browser.defer.cancel()`.
5300 * Executes a fn asynchronously via `setTimeout(fn, delay)`.
5302 * Unlike when calling `setTimeout` directly, in test this function is mocked and instead of using
5303 * `setTimeout` in tests, the fns are queued in an array, which can be programmatically flushed
5304 * via `$browser.defer.flush()`.
5307 self.defer = function(fn, delay) {
5309 outstandingRequestCount++;
5310 timeoutId = setTimeout(function() {
5311 delete pendingDeferIds[timeoutId];
5312 completeOutstandingRequest(fn);
5314 pendingDeferIds[timeoutId] = true;
5320 * @name $browser#defer.cancel
5323 * Cancels a deferred task identified with `deferId`.
5325 * @param {*} deferId Token returned by the `$browser.defer` function.
5326 * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully
5329 self.defer.cancel = function(deferId) {
5330 if (pendingDeferIds[deferId]) {
5331 delete pendingDeferIds[deferId];
5332 clearTimeout(deferId);
5333 completeOutstandingRequest(noop);
5341 function $BrowserProvider() {
5342 this.$get = ['$window', '$log', '$sniffer', '$document',
5343 function($window, $log, $sniffer, $document) {
5344 return new Browser($window, $document, $log, $sniffer);
5350 * @name $cacheFactory
5353 * Factory that constructs {@link $cacheFactory.Cache Cache} objects and gives access to
5358 * var cache = $cacheFactory('cacheId');
5359 * expect($cacheFactory.get('cacheId')).toBe(cache);
5360 * expect($cacheFactory.get('noSuchCacheId')).not.toBeDefined();
5362 * cache.put("key", "value");
5363 * cache.put("another key", "another value");
5365 * // We've specified no options on creation
5366 * expect(cache.info()).toEqual({id: 'cacheId', size: 2});
5371 * @param {string} cacheId Name or id of the newly created cache.
5372 * @param {object=} options Options object that specifies the cache behavior. Properties:
5374 * - `{number=}` `capacity` — turns the cache into LRU cache.
5376 * @returns {object} Newly created cache object with the following set of methods:
5378 * - `{object}` `info()` — Returns id, size, and options of cache.
5379 * - `{{*}}` `put({string} key, {*} value)` — Puts a new key-value pair into the cache and returns
5381 * - `{{*}}` `get({string} key)` — Returns cached value for `key` or undefined for cache miss.
5382 * - `{void}` `remove({string} key)` — Removes a key-value pair from the cache.
5383 * - `{void}` `removeAll()` — Removes all cached values.
5384 * - `{void}` `destroy()` — Removes references to this cache from $cacheFactory.
5387 <example module="cacheExampleApp">
5388 <file name="index.html">
5389 <div ng-controller="CacheController">
5390 <input ng-model="newCacheKey" placeholder="Key">
5391 <input ng-model="newCacheValue" placeholder="Value">
5392 <button ng-click="put(newCacheKey, newCacheValue)">Cache</button>
5394 <p ng-if="keys.length">Cached Values</p>
5395 <div ng-repeat="key in keys">
5396 <span ng-bind="key"></span>
5398 <b ng-bind="cache.get(key)"></b>
5402 <div ng-repeat="(key, value) in cache.info()">
5403 <span ng-bind="key"></span>
5405 <b ng-bind="value"></b>
5409 <file name="script.js">
5410 angular.module('cacheExampleApp', []).
5411 controller('CacheController', ['$scope', '$cacheFactory', function($scope, $cacheFactory) {
5413 $scope.cache = $cacheFactory('cacheId');
5414 $scope.put = function(key, value) {
5415 if ($scope.cache.get(key) === undefined) {
5416 $scope.keys.push(key);
5418 $scope.cache.put(key, value === undefined ? null : value);
5422 <file name="style.css">
5429 function $CacheFactoryProvider() {
5431 this.$get = function() {
5434 function cacheFactory(cacheId, options) {
5435 if (cacheId in caches) {
5436 throw minErr('$cacheFactory')('iid', "CacheId '{0}' is already taken!", cacheId);
5440 stats = extend({}, options, {id: cacheId}),
5442 capacity = (options && options.capacity) || Number.MAX_VALUE,
5449 * @name $cacheFactory.Cache
5452 * A cache object used to store and retrieve data, primarily used by
5453 * {@link $http $http} and the {@link ng.directive:script script} directive to cache
5454 * templates and other data.
5457 * angular.module('superCache')
5458 * .factory('superCache', ['$cacheFactory', function($cacheFactory) {
5459 * return $cacheFactory('super-cache');
5466 * it('should behave like a cache', inject(function(superCache) {
5467 * superCache.put('key', 'value');
5468 * superCache.put('another key', 'another value');
5470 * expect(superCache.info()).toEqual({
5471 * id: 'super-cache',
5475 * superCache.remove('another key');
5476 * expect(superCache.get('another key')).toBeUndefined();
5478 * superCache.removeAll();
5479 * expect(superCache.info()).toEqual({
5480 * id: 'super-cache',
5486 return caches[cacheId] = {
5490 * @name $cacheFactory.Cache#put
5494 * Inserts a named entry into the {@link $cacheFactory.Cache Cache} object to be
5495 * retrieved later, and incrementing the size of the cache if the key was not already
5496 * present in the cache. If behaving like an LRU cache, it will also remove stale
5497 * entries from the set.
5499 * It will not insert undefined values into the cache.
5501 * @param {string} key the key under which the cached data is stored.
5502 * @param {*} value the value to store alongside the key. If it is undefined, the key
5503 * will not be stored.
5504 * @returns {*} the value stored.
5506 put: function(key, value) {
5507 if (capacity < Number.MAX_VALUE) {
5508 var lruEntry = lruHash[key] || (lruHash[key] = {key: key});
5513 if (isUndefined(value)) return;
5514 if (!(key in data)) size++;
5517 if (size > capacity) {
5518 this.remove(staleEnd.key);
5526 * @name $cacheFactory.Cache#get
5530 * Retrieves named data stored in the {@link $cacheFactory.Cache Cache} object.
5532 * @param {string} key the key of the data to be retrieved
5533 * @returns {*} the value stored.
5535 get: function(key) {
5536 if (capacity < Number.MAX_VALUE) {
5537 var lruEntry = lruHash[key];
5539 if (!lruEntry) return;
5550 * @name $cacheFactory.Cache#remove
5554 * Removes an entry from the {@link $cacheFactory.Cache Cache} object.
5556 * @param {string} key the key of the entry to be removed
5558 remove: function(key) {
5559 if (capacity < Number.MAX_VALUE) {
5560 var lruEntry = lruHash[key];
5562 if (!lruEntry) return;
5564 if (lruEntry == freshEnd) freshEnd = lruEntry.p;
5565 if (lruEntry == staleEnd) staleEnd = lruEntry.n;
5566 link(lruEntry.n,lruEntry.p);
5568 delete lruHash[key];
5578 * @name $cacheFactory.Cache#removeAll
5582 * Clears the cache object of any entries.
5584 removeAll: function() {
5588 freshEnd = staleEnd = null;
5594 * @name $cacheFactory.Cache#destroy
5598 * Destroys the {@link $cacheFactory.Cache Cache} object entirely,
5599 * removing it from the {@link $cacheFactory $cacheFactory} set.
5601 destroy: function() {
5605 delete caches[cacheId];
5611 * @name $cacheFactory.Cache#info
5615 * Retrieve information regarding a particular {@link $cacheFactory.Cache Cache}.
5617 * @returns {object} an object with the following properties:
5619 * <li>**id**: the id of the cache instance</li>
5620 * <li>**size**: the number of entries kept in the cache instance</li>
5621 * <li>**...**: any additional properties from the options object when creating the
5626 return extend({}, stats, {size: size});
5632 * makes the `entry` the freshEnd of the LRU linked list
5634 function refresh(entry) {
5635 if (entry != freshEnd) {
5638 } else if (staleEnd == entry) {
5642 link(entry.n, entry.p);
5643 link(entry, freshEnd);
5651 * bidirectionally links two entries of the LRU linked list
5653 function link(nextEntry, prevEntry) {
5654 if (nextEntry != prevEntry) {
5655 if (nextEntry) nextEntry.p = prevEntry; //p stands for previous, 'prev' didn't minify
5656 if (prevEntry) prevEntry.n = nextEntry; //n stands for next, 'next' didn't minify
5664 * @name $cacheFactory#info
5667 * Get information about all the caches that have been created
5669 * @returns {Object} - key-value map of `cacheId` to the result of calling `cache#info`
5671 cacheFactory.info = function() {
5673 forEach(caches, function(cache, cacheId) {
5674 info[cacheId] = cache.info();
5682 * @name $cacheFactory#get
5685 * Get access to a cache object by the `cacheId` used when it was created.
5687 * @param {string} cacheId Name or id of a cache to access.
5688 * @returns {object} Cache object identified by the cacheId or undefined if no such cache.
5690 cacheFactory.get = function(cacheId) {
5691 return caches[cacheId];
5695 return cacheFactory;
5701 * @name $templateCache
5704 * The first time a template is used, it is loaded in the template cache for quick retrieval. You
5705 * can load templates directly into the cache in a `script` tag, or by consuming the
5706 * `$templateCache` service directly.
5708 * Adding via the `script` tag:
5711 * <script type="text/ng-template" id="templateId.html">
5712 * <p>This is the content of the template</p>
5716 * **Note:** the `script` tag containing the template does not need to be included in the `head` of
5717 * the document, but it must be a descendent of the {@link ng.$rootElement $rootElement} (IE,
5718 * element with ng-app attribute), otherwise the template will be ignored.
5720 * Adding via the `$templateCache` service:
5723 * var myApp = angular.module('myApp', []);
5724 * myApp.run(function($templateCache) {
5725 * $templateCache.put('templateId.html', 'This is the content of the template');
5729 * To retrieve the template later, simply use it in your HTML:
5731 * <div ng-include=" 'templateId.html' "></div>
5734 * or get it via Javascript:
5736 * $templateCache.get('templateId.html')
5739 * See {@link ng.$cacheFactory $cacheFactory}.
5742 function $TemplateCacheProvider() {
5743 this.$get = ['$cacheFactory', function($cacheFactory) {
5744 return $cacheFactory('templates');
5748 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
5749 * Any commits to this file should be reviewed with security in mind. *
5750 * Changes to this file can potentially create security vulnerabilities. *
5751 * An approval from 2 Core members with history of modifying *
5752 * this file is required. *
5754 * Does the change somehow allow for arbitrary javascript to be executed? *
5755 * Or allows for someone to change the prototype of built-in objects? *
5756 * Or gives undesired access to variables likes document or window? *
5757 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
5759 /* ! VARIABLE/FUNCTION NAMING CONVENTIONS THAT APPLY TO THIS FILE!
5761 * DOM-related variables:
5763 * - "node" - DOM Node
5764 * - "element" - DOM Element or Node
5765 * - "$node" or "$element" - jqLite-wrapped node or element
5768 * Compiler related stuff:
5770 * - "linkFn" - linking fn of a single directive
5771 * - "nodeLinkFn" - function that aggregates all linking fns for a particular node
5772 * - "childLinkFn" - function that aggregates all linking fns for child nodes of a particular node
5773 * - "compositeLinkFn" - function that aggregates all linking fns for a compilation root (nodeList)
5783 * Compiles an HTML string or DOM into a template and produces a template function, which
5784 * can then be used to link {@link ng.$rootScope.Scope `scope`} and the template together.
5786 * The compilation is a process of walking the DOM tree and matching DOM elements to
5787 * {@link ng.$compileProvider#directive directives}.
5789 * <div class="alert alert-warning">
5790 * **Note:** This document is an in-depth reference of all directive options.
5791 * For a gentle introduction to directives with examples of common use cases,
5792 * see the {@link guide/directive directive guide}.
5795 * ## Comprehensive Directive API
5797 * There are many different options for a directive.
5799 * The difference resides in the return value of the factory function.
5800 * You can either return a "Directive Definition Object" (see below) that defines the directive properties,
5801 * or just the `postLink` function (all other properties will have the default values).
5803 * <div class="alert alert-success">
5804 * **Best Practice:** It's recommended to use the "directive definition object" form.
5807 * Here's an example directive declared with a Directive Definition Object:
5810 * var myModule = angular.module(...);
5812 * myModule.directive('directiveName', function factory(injectables) {
5813 * var directiveDefinitionObject = {
5815 * template: '<div></div>', // or // function(tElement, tAttrs) { ... },
5817 * // templateUrl: 'directive.html', // or // function(tElement, tAttrs) { ... },
5818 * transclude: false,
5820 * templateNamespace: 'html',
5822 * controller: function($scope, $element, $attrs, $transclude, otherInjectables) { ... },
5823 * controllerAs: 'stringAlias',
5824 * require: 'siblingDirectiveName', // or // ['^parentDirectiveName', '?optionalDirectiveName', '?^optionalParent'],
5825 * compile: function compile(tElement, tAttrs, transclude) {
5827 * pre: function preLink(scope, iElement, iAttrs, controller) { ... },
5828 * post: function postLink(scope, iElement, iAttrs, controller) { ... }
5831 * // return function postLink( ... ) { ... }
5835 * // pre: function preLink(scope, iElement, iAttrs, controller) { ... },
5836 * // post: function postLink(scope, iElement, iAttrs, controller) { ... }
5839 * // link: function postLink( ... ) { ... }
5841 * return directiveDefinitionObject;
5845 * <div class="alert alert-warning">
5846 * **Note:** Any unspecified options will use the default value. You can see the default values below.
5849 * Therefore the above can be simplified as:
5852 * var myModule = angular.module(...);
5854 * myModule.directive('directiveName', function factory(injectables) {
5855 * var directiveDefinitionObject = {
5856 * link: function postLink(scope, iElement, iAttrs) { ... }
5858 * return directiveDefinitionObject;
5860 * // return function postLink(scope, iElement, iAttrs) { ... }
5866 * ### Directive Definition Object
5868 * The directive definition object provides instructions to the {@link ng.$compile
5869 * compiler}. The attributes are:
5871 * #### `multiElement`
5872 * When this property is set to true, the HTML compiler will collect DOM nodes between
5873 * nodes with the attributes `directive-name-start` and `directive-name-end`, and group them
5874 * together as the directive elements. It is recommended that this feature be used on directives
5875 * which are not strictly behavioural (such as {@link ngClick}), and which
5876 * do not manipulate or replace child nodes (such as {@link ngInclude}).
5879 * When there are multiple directives defined on a single DOM element, sometimes it
5880 * is necessary to specify the order in which the directives are applied. The `priority` is used
5881 * to sort the directives before their `compile` functions get called. Priority is defined as a
5882 * number. Directives with greater numerical `priority` are compiled first. Pre-link functions
5883 * are also run in priority order, but post-link functions are run in reverse order. The order
5884 * of directives with the same priority is undefined. The default priority is `0`.
5887 * If set to true then the current `priority` will be the last set of directives
5888 * which will execute (any directives at the current priority will still execute
5889 * as the order of execution on same `priority` is undefined). Note that expressions
5890 * and other directives used in the directive's template will also be excluded from execution.
5893 * **If set to `true`,** then a new scope will be created for this directive. If multiple directives on the
5894 * same element request a new scope, only one new scope is created. The new scope rule does not
5895 * apply for the root of the template since the root of the template always gets a new scope.
5897 * **If set to `{}` (object hash),** then a new "isolate" scope is created. The 'isolate' scope differs from
5898 * normal scope in that it does not prototypically inherit from the parent scope. This is useful
5899 * when creating reusable components, which should not accidentally read or modify data in the
5902 * The 'isolate' scope takes an object hash which defines a set of local scope properties
5903 * derived from the parent scope. These local properties are useful for aliasing values for
5904 * templates. Locals definition is a hash of local scope property to its source:
5906 * * `@` or `@attr` - bind a local scope property to the value of DOM attribute. The result is
5907 * always a string since DOM attributes are strings. If no `attr` name is specified then the
5908 * attribute name is assumed to be the same as the local name.
5909 * Given `<widget my-attr="hello {{name}}">` and widget definition
5910 * of `scope: { localName:'@myAttr' }`, then widget scope property `localName` will reflect
5911 * the interpolated value of `hello {{name}}`. As the `name` attribute changes so will the
5912 * `localName` property on the widget scope. The `name` is read from the parent scope (not
5915 * * `=` or `=attr` - set up bi-directional binding between a local scope property and the
5916 * parent scope property of name defined via the value of the `attr` attribute. If no `attr`
5917 * name is specified then the attribute name is assumed to be the same as the local name.
5918 * Given `<widget my-attr="parentModel">` and widget definition of
5919 * `scope: { localModel:'=myAttr' }`, then widget scope property `localModel` will reflect the
5920 * value of `parentModel` on the parent scope. Any changes to `parentModel` will be reflected
5921 * in `localModel` and any changes in `localModel` will reflect in `parentModel`. If the parent
5922 * scope property doesn't exist, it will throw a NON_ASSIGNABLE_MODEL_EXPRESSION exception. You
5923 * can avoid this behavior using `=?` or `=?attr` in order to flag the property as optional. If
5924 * you want to shallow watch for changes (i.e. $watchCollection instead of $watch) you can use
5925 * `=*` or `=*attr` (`=*?` or `=*?attr` if the property is optional).
5927 * * `&` or `&attr` - provides a way to execute an expression in the context of the parent scope.
5928 * If no `attr` name is specified then the attribute name is assumed to be the same as the
5929 * local name. Given `<widget my-attr="count = count + value">` and widget definition of
5930 * `scope: { localFn:'&myAttr' }`, then isolate scope property `localFn` will point to
5931 * a function wrapper for the `count = count + value` expression. Often it's desirable to
5932 * pass data from the isolated scope via an expression to the parent scope, this can be
5933 * done by passing a map of local variable names and values into the expression wrapper fn.
5934 * For example, if the expression is `increment(amount)` then we can specify the amount value
5935 * by calling the `localFn` as `localFn({amount: 22})`.
5938 * #### `bindToController`
5939 * When an isolate scope is used for a component (see above), and `controllerAs` is used, `bindToController: true` will
5940 * allow a component to have its properties bound to the controller, rather than to scope. When the controller
5941 * is instantiated, the initial values of the isolate scope bindings are already available.
5944 * Controller constructor function. The controller is instantiated before the
5945 * pre-linking phase and it is shared with other directives (see
5946 * `require` attribute). This allows the directives to communicate with each other and augment
5947 * each other's behavior. The controller is injectable (and supports bracket notation) with the following locals:
5949 * * `$scope` - Current scope associated with the element
5950 * * `$element` - Current element
5951 * * `$attrs` - Current attributes object for the element
5952 * * `$transclude` - A transclude linking function pre-bound to the correct transclusion scope:
5953 * `function([scope], cloneLinkingFn, futureParentElement)`.
5954 * * `scope`: optional argument to override the scope.
5955 * * `cloneLinkingFn`: optional argument to create clones of the original transcluded content.
5956 * * `futureParentElement`:
5957 * * defines the parent to which the `cloneLinkingFn` will add the cloned elements.
5958 * * default: `$element.parent()` resp. `$element` for `transclude:'element'` resp. `transclude:true`.
5959 * * only needed for transcludes that are allowed to contain non html elements (e.g. SVG elements)
5960 * and when the `cloneLinkinFn` is passed,
5961 * as those elements need to created and cloned in a special way when they are defined outside their
5962 * usual containers (e.g. like `<svg>`).
5963 * * See also the `directive.templateNamespace` property.
5967 * Require another directive and inject its controller as the fourth argument to the linking function. The
5968 * `require` takes a string name (or array of strings) of the directive(s) to pass in. If an array is used, the
5969 * injected argument will be an array in corresponding order. If no such directive can be
5970 * found, or if the directive does not have a controller, then an error is raised (unless no link function
5971 * is specified, in which case error checking is skipped). The name can be prefixed with:
5973 * * (no prefix) - Locate the required controller on the current element. Throw an error if not found.
5974 * * `?` - Attempt to locate the required controller or pass `null` to the `link` fn if not found.
5975 * * `^` - Locate the required controller by searching the element and its parents. Throw an error if not found.
5976 * * `^^` - Locate the required controller by searching the element's parents. Throw an error if not found.
5977 * * `?^` - Attempt to locate the required controller by searching the element and its parents or pass
5978 * `null` to the `link` fn if not found.
5979 * * `?^^` - Attempt to locate the required controller by searching the element's parents, or pass
5980 * `null` to the `link` fn if not found.
5983 * #### `controllerAs`
5984 * Controller alias at the directive scope. An alias for the controller so it
5985 * can be referenced at the directive template. The directive needs to define a scope for this
5986 * configuration to be used. Useful in the case when directive is used as component.
5990 * String of subset of `EACM` which restricts the directive to a specific directive
5991 * declaration style. If omitted, the defaults (elements and attributes) are used.
5993 * * `E` - Element name (default): `<my-directive></my-directive>`
5994 * * `A` - Attribute (default): `<div my-directive="exp"></div>`
5995 * * `C` - Class: `<div class="my-directive: exp;"></div>`
5996 * * `M` - Comment: `<!-- directive: my-directive exp -->`
5999 * #### `templateNamespace`
6000 * String representing the document type used by the markup in the template.
6001 * AngularJS needs this information as those elements need to be created and cloned
6002 * in a special way when they are defined outside their usual containers like `<svg>` and `<math>`.
6004 * * `html` - All root nodes in the template are HTML. Root nodes may also be
6005 * top-level elements such as `<svg>` or `<math>`.
6006 * * `svg` - The root nodes in the template are SVG elements (excluding `<math>`).
6007 * * `math` - The root nodes in the template are MathML elements (excluding `<svg>`).
6009 * If no `templateNamespace` is specified, then the namespace is considered to be `html`.
6012 * HTML markup that may:
6013 * * Replace the contents of the directive's element (default).
6014 * * Replace the directive's element itself (if `replace` is true - DEPRECATED).
6015 * * Wrap the contents of the directive's element (if `transclude` is true).
6019 * * A string. For example `<div red-on-hover>{{delete_str}}</div>`.
6020 * * A function which takes two arguments `tElement` and `tAttrs` (described in the `compile`
6021 * function api below) and returns a string value.
6024 * #### `templateUrl`
6025 * This is similar to `template` but the template is loaded from the specified URL, asynchronously.
6027 * Because template loading is asynchronous the compiler will suspend compilation of directives on that element
6028 * for later when the template has been resolved. In the meantime it will continue to compile and link
6029 * sibling and parent elements as though this element had not contained any directives.
6031 * The compiler does not suspend the entire compilation to wait for templates to be loaded because this
6032 * would result in the whole app "stalling" until all templates are loaded asynchronously - even in the
6033 * case when only one deeply nested directive has `templateUrl`.
6035 * Template loading is asynchronous even if the template has been preloaded into the {@link $templateCache}
6037 * You can specify `templateUrl` as a string representing the URL or as a function which takes two
6038 * arguments `tElement` and `tAttrs` (described in the `compile` function api below) and returns
6039 * a string value representing the url. In either case, the template URL is passed through {@link
6040 * $sce#getTrustedResourceUrl $sce.getTrustedResourceUrl}.
6043 * #### `replace` ([*DEPRECATED*!], will be removed in next major release - i.e. v2.0)
6044 * specify what the template should replace. Defaults to `false`.
6046 * * `true` - the template will replace the directive's element.
6047 * * `false` - the template will replace the contents of the directive's element.
6049 * The replacement process migrates all of the attributes / classes from the old element to the new
6050 * one. See the {@link guide/directive#template-expanding-directive
6051 * Directives Guide} for an example.
6053 * There are very few scenarios where element replacement is required for the application function,
6054 * the main one being reusable custom components that are used within SVG contexts
6055 * (because SVG doesn't work with custom elements in the DOM tree).
6058 * Extract the contents of the element where the directive appears and make it available to the directive.
6059 * The contents are compiled and provided to the directive as a **transclusion function**. See the
6060 * {@link $compile#transclusion Transclusion} section below.
6062 * There are two kinds of transclusion depending upon whether you want to transclude just the contents of the
6063 * directive's element or the entire element:
6065 * * `true` - transclude the content (i.e. the child nodes) of the directive's element.
6066 * * `'element'` - transclude the whole of the directive's element including any directives on this
6067 * element that defined at a lower priority than this directive. When used, the `template`
6068 * property is ignored.
6074 * function compile(tElement, tAttrs, transclude) { ... }
6077 * The compile function deals with transforming the template DOM. Since most directives do not do
6078 * template transformation, it is not used often. The compile function takes the following arguments:
6080 * * `tElement` - template element - The element where the directive has been declared. It is
6081 * safe to do template transformation on the element and child elements only.
6083 * * `tAttrs` - template attributes - Normalized list of attributes declared on this element shared
6084 * between all directive compile functions.
6086 * * `transclude` - [*DEPRECATED*!] A transclude linking function: `function(scope, cloneLinkingFn)`
6088 * <div class="alert alert-warning">
6089 * **Note:** The template instance and the link instance may be different objects if the template has
6090 * been cloned. For this reason it is **not** safe to do anything other than DOM transformations that
6091 * apply to all cloned DOM nodes within the compile function. Specifically, DOM listener registration
6092 * should be done in a linking function rather than in a compile function.
6095 * <div class="alert alert-warning">
6096 * **Note:** The compile function cannot handle directives that recursively use themselves in their
6097 * own templates or compile functions. Compiling these directives results in an infinite loop and a
6098 * stack overflow errors.
6100 * This can be avoided by manually using $compile in the postLink function to imperatively compile
6101 * a directive's template instead of relying on automatic template compilation via `template` or
6102 * `templateUrl` declaration or manual compilation inside the compile function.
6105 * <div class="alert alert-error">
6106 * **Note:** The `transclude` function that is passed to the compile function is deprecated, as it
6107 * e.g. does not know about the right outer scope. Please use the transclude function that is passed
6108 * to the link function instead.
6111 * A compile function can have a return value which can be either a function or an object.
6113 * * returning a (post-link) function - is equivalent to registering the linking function via the
6114 * `link` property of the config object when the compile function is empty.
6116 * * returning an object with function(s) registered via `pre` and `post` properties - allows you to
6117 * control when a linking function should be called during the linking phase. See info about
6118 * pre-linking and post-linking functions below.
6122 * This property is used only if the `compile` property is not defined.
6125 * function link(scope, iElement, iAttrs, controller, transcludeFn) { ... }
6128 * The link function is responsible for registering DOM listeners as well as updating the DOM. It is
6129 * executed after the template has been cloned. This is where most of the directive logic will be
6132 * * `scope` - {@link ng.$rootScope.Scope Scope} - The scope to be used by the
6133 * directive for registering {@link ng.$rootScope.Scope#$watch watches}.
6135 * * `iElement` - instance element - The element where the directive is to be used. It is safe to
6136 * manipulate the children of the element only in `postLink` function since the children have
6137 * already been linked.
6139 * * `iAttrs` - instance attributes - Normalized list of attributes declared on this element shared
6140 * between all directive linking functions.
6142 * * `controller` - a controller instance - A controller instance if at least one directive on the
6143 * element defines a controller. The controller is shared among all the directives, which allows
6144 * the directives to use the controllers as a communication channel.
6146 * * `transcludeFn` - A transclude linking function pre-bound to the correct transclusion scope.
6147 * This is the same as the `$transclude`
6148 * parameter of directive controllers, see there for details.
6149 * `function([scope], cloneLinkingFn, futureParentElement)`.
6151 * #### Pre-linking function
6153 * Executed before the child elements are linked. Not safe to do DOM transformation since the
6154 * compiler linking function will fail to locate the correct elements for linking.
6156 * #### Post-linking function
6158 * Executed after the child elements are linked.
6160 * Note that child elements that contain `templateUrl` directives will not have been compiled
6161 * and linked since they are waiting for their template to load asynchronously and their own
6162 * compilation and linking has been suspended until that occurs.
6164 * It is safe to do DOM transformation in the post-linking function on elements that are not waiting
6165 * for their async templates to be resolved.
6170 * Transclusion is the process of extracting a collection of DOM element from one part of the DOM and
6171 * copying them to another part of the DOM, while maintaining their connection to the original AngularJS
6172 * scope from where they were taken.
6174 * Transclusion is used (often with {@link ngTransclude}) to insert the
6175 * original contents of a directive's element into a specified place in the template of the directive.
6176 * The benefit of transclusion, over simply moving the DOM elements manually, is that the transcluded
6177 * content has access to the properties on the scope from which it was taken, even if the directive
6178 * has isolated scope.
6179 * See the {@link guide/directive#creating-a-directive-that-wraps-other-elements Directives Guide}.
6181 * This makes it possible for the widget to have private state for its template, while the transcluded
6182 * content has access to its originating scope.
6184 * <div class="alert alert-warning">
6185 * **Note:** When testing an element transclude directive you must not place the directive at the root of the
6186 * DOM fragment that is being compiled. See {@link guide/unit-testing#testing-transclusion-directives
6187 * Testing Transclusion Directives}.
6190 * #### Transclusion Functions
6192 * When a directive requests transclusion, the compiler extracts its contents and provides a **transclusion
6193 * function** to the directive's `link` function and `controller`. This transclusion function is a special
6194 * **linking function** that will return the compiled contents linked to a new transclusion scope.
6196 * <div class="alert alert-info">
6197 * If you are just using {@link ngTransclude} then you don't need to worry about this function, since
6198 * ngTransclude will deal with it for us.
6201 * If you want to manually control the insertion and removal of the transcluded content in your directive
6202 * then you must use this transclude function. When you call a transclude function it returns a a jqLite/JQuery
6203 * object that contains the compiled DOM, which is linked to the correct transclusion scope.
6205 * When you call a transclusion function you can pass in a **clone attach function**. This function accepts
6206 * two parameters, `function(clone, scope) { ... }`, where the `clone` is a fresh compiled copy of your transcluded
6207 * content and the `scope` is the newly created transclusion scope, to which the clone is bound.
6209 * <div class="alert alert-info">
6210 * **Best Practice**: Always provide a `cloneFn` (clone attach function) when you call a translude function
6211 * since you then get a fresh clone of the original DOM and also have access to the new transclusion scope.
6214 * It is normal practice to attach your transcluded content (`clone`) to the DOM inside your **clone
6215 * attach function**:
6218 * var transcludedContent, transclusionScope;
6220 * $transclude(function(clone, scope) {
6221 * element.append(clone);
6222 * transcludedContent = clone;
6223 * transclusionScope = scope;
6227 * Later, if you want to remove the transcluded content from your DOM then you should also destroy the
6228 * associated transclusion scope:
6231 * transcludedContent.remove();
6232 * transclusionScope.$destroy();
6235 * <div class="alert alert-info">
6236 * **Best Practice**: if you intend to add and remove transcluded content manually in your directive
6237 * (by calling the transclude function to get the DOM and and calling `element.remove()` to remove it),
6238 * then you are also responsible for calling `$destroy` on the transclusion scope.
6241 * The built-in DOM manipulation directives, such as {@link ngIf}, {@link ngSwitch} and {@link ngRepeat}
6242 * automatically destroy their transluded clones as necessary so you do not need to worry about this if
6243 * you are simply using {@link ngTransclude} to inject the transclusion into your directive.
6246 * #### Transclusion Scopes
6248 * When you call a transclude function it returns a DOM fragment that is pre-bound to a **transclusion
6249 * scope**. This scope is special, in that it is a child of the directive's scope (and so gets destroyed
6250 * when the directive's scope gets destroyed) but it inherits the properties of the scope from which it
6253 * For example consider a directive that uses transclusion and isolated scope. The DOM hierarchy might look
6259 * <div transclusion>
6265 * The `$parent` scope hierarchy will look like this:
6273 * but the scopes will inherit prototypically from different scopes to their `$parent`.
6284 * The {@link ng.$compile.directive.Attributes Attributes} object - passed as a parameter in the
6285 * `link()` or `compile()` functions. It has a variety of uses.
6287 * accessing *Normalized attribute names:*
6288 * Directives like 'ngBind' can be expressed in many ways: 'ng:bind', `data-ng-bind`, or 'x-ng-bind'.
6289 * the attributes object allows for normalized access to
6292 * * *Directive inter-communication:* All directives share the same instance of the attributes
6293 * object which allows the directives to use the attributes object as inter directive
6296 * * *Supports interpolation:* Interpolation attributes are assigned to the attribute object
6297 * allowing other directives to read the interpolated value.
6299 * * *Observing interpolated attributes:* Use `$observe` to observe the value changes of attributes
6300 * that contain interpolation (e.g. `src="{{bar}}"`). Not only is this very efficient but it's also
6301 * the only way to easily get the actual value because during the linking phase the interpolation
6302 * hasn't been evaluated yet and so the value is at this time set to `undefined`.
6305 * function linkingFn(scope, elm, attrs, ctrl) {
6306 * // get the attribute value
6307 * console.log(attrs.ngModel);
6309 * // change the attribute
6310 * attrs.$set('ngModel', 'new value');
6312 * // observe changes to interpolated attribute
6313 * attrs.$observe('ngModel', function(value) {
6314 * console.log('ngModel has changed value to ' + value);
6321 * <div class="alert alert-warning">
6322 * **Note**: Typically directives are registered with `module.directive`. The example below is
6323 * to illustrate how `$compile` works.
6326 <example module="compileExample">
6327 <file name="index.html">
6329 angular.module('compileExample', [], function($compileProvider) {
6330 // configure new 'compile' directive by passing a directive
6331 // factory function. The factory function injects the '$compile'
6332 $compileProvider.directive('compile', function($compile) {
6333 // directive factory creates a link function
6334 return function(scope, element, attrs) {
6337 // watch the 'compile' expression for changes
6338 return scope.$eval(attrs.compile);
6341 // when the 'compile' expression changes
6342 // assign it into the current DOM
6343 element.html(value);
6345 // compile the new DOM and link it to the current
6347 // NOTE: we only compile .childNodes so that
6348 // we don't get into infinite loop compiling ourselves
6349 $compile(element.contents())(scope);
6355 .controller('GreeterController', ['$scope', function($scope) {
6356 $scope.name = 'Angular';
6357 $scope.html = 'Hello {{name}}';
6360 <div ng-controller="GreeterController">
6361 <input ng-model="name"> <br>
6362 <textarea ng-model="html"></textarea> <br>
6363 <div compile="html"></div>
6366 <file name="protractor.js" type="protractor">
6367 it('should auto compile', function() {
6368 var textarea = $('textarea');
6369 var output = $('div[compile]');
6370 // The initial state reads 'Hello Angular'.
6371 expect(output.getText()).toBe('Hello Angular');
6373 textarea.sendKeys('{{name}}!');
6374 expect(output.getText()).toBe('Angular!');
6381 * @param {string|DOMElement} element Element or HTML string to compile into a template function.
6382 * @param {function(angular.Scope, cloneAttachFn=)} transclude function available to directives - DEPRECATED.
6384 * <div class="alert alert-error">
6385 * **Note:** Passing a `transclude` function to the $compile function is deprecated, as it
6386 * e.g. will not use the right outer scope. Please pass the transclude function as a
6387 * `parentBoundTranscludeFn` to the link function instead.
6390 * @param {number} maxPriority only apply directives lower than given priority (Only effects the
6391 * root element(s), not their children)
6392 * @returns {function(scope, cloneAttachFn=, options=)} a link function which is used to bind template
6393 * (a DOM element/tree) to a scope. Where:
6395 * * `scope` - A {@link ng.$rootScope.Scope Scope} to bind to.
6396 * * `cloneAttachFn` - If `cloneAttachFn` is provided, then the link function will clone the
6397 * `template` and call the `cloneAttachFn` function allowing the caller to attach the
6398 * cloned elements to the DOM document at the appropriate place. The `cloneAttachFn` is
6399 * called as: <br> `cloneAttachFn(clonedElement, scope)` where:
6401 * * `clonedElement` - is a clone of the original `element` passed into the compiler.
6402 * * `scope` - is the current scope with which the linking function is working with.
6404 * * `options` - An optional object hash with linking options. If `options` is provided, then the following
6405 * keys may be used to control linking behavior:
6407 * * `parentBoundTranscludeFn` - the transclude function made available to
6408 * directives; if given, it will be passed through to the link functions of
6409 * directives found in `element` during compilation.
6410 * * `transcludeControllers` - an object hash with keys that map controller names
6411 * to controller instances; if given, it will make the controllers
6412 * available to directives.
6413 * * `futureParentElement` - defines the parent to which the `cloneAttachFn` will add
6414 * the cloned elements; only needed for transcludes that are allowed to contain non html
6415 * elements (e.g. SVG elements). See also the directive.controller property.
6417 * Calling the linking function returns the element of the template. It is either the original
6418 * element passed in, or the clone of the element if the `cloneAttachFn` is provided.
6420 * After linking the view is not updated until after a call to $digest which typically is done by
6421 * Angular automatically.
6423 * If you need access to the bound view, there are two ways to do it:
6425 * - If you are not asking the linking function to clone the template, create the DOM element(s)
6426 * before you send them to the compiler and keep this reference around.
6428 * var element = $compile('<p>{{total}}</p>')(scope);
6431 * - if on the other hand, you need the element to be cloned, the view reference from the original
6432 * example would not point to the clone, but rather to the original template that was cloned. In
6433 * this case, you can access the clone via the cloneAttachFn:
6435 * var templateElement = angular.element('<p>{{total}}</p>'),
6438 * var clonedElement = $compile(templateElement)(scope, function(clonedElement, scope) {
6439 * //attach the clone to DOM document at the right place
6442 * //now we have reference to the cloned DOM via `clonedElement`
6446 * For information on how the compiler works, see the
6447 * {@link guide/compiler Angular HTML Compiler} section of the Developer Guide.
6450 var $compileMinErr = minErr('$compile');
6454 * @name $compileProvider
6458 $CompileProvider.$inject = ['$provide', '$$sanitizeUriProvider'];
6459 function $CompileProvider($provide, $$sanitizeUriProvider) {
6460 var hasDirectives = {},
6461 Suffix = 'Directive',
6462 COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\w\-]+)\s+(.*)$/,
6463 CLASS_DIRECTIVE_REGEXP = /(([\w\-]+)(?:\:([^;]+))?;?)/,
6464 ALL_OR_NOTHING_ATTRS = makeMap('ngSrc,ngSrcset,src,srcset'),
6465 REQUIRE_PREFIX_REGEXP = /^(?:(\^\^?)?(\?)?(\^\^?)?)?/;
6467 // Ref: http://developers.whatwg.org/webappapis.html#event-handler-idl-attributes
6468 // The assumption is that future DOM event attribute names will begin with
6469 // 'on' and be composed of only English letters.
6470 var EVENT_HANDLER_ATTR_REGEXP = /^(on[a-z]+|formaction)$/;
6472 function parseIsolateBindings(scope, directiveName) {
6473 var LOCAL_REGEXP = /^\s*([@&]|=(\*?))(\??)\s*(\w*)\s*$/;
6477 forEach(scope, function(definition, scopeName) {
6478 var match = definition.match(LOCAL_REGEXP);
6481 throw $compileMinErr('iscp',
6482 "Invalid isolate scope definition for directive '{0}'." +
6483 " Definition: {... {1}: '{2}' ...}",
6484 directiveName, scopeName, definition);
6487 bindings[scopeName] = {
6489 collection: match[2] === '*',
6490 optional: match[3] === '?',
6491 attrName: match[4] || scopeName
6500 * @name $compileProvider#directive
6504 * Register a new directive with the compiler.
6506 * @param {string|Object} name Name of the directive in camel-case (i.e. <code>ngBind</code> which
6507 * will match as <code>ng-bind</code>), or an object map of directives where the keys are the
6508 * names and the values are the factories.
6509 * @param {Function|Array} directiveFactory An injectable directive factory function. See
6510 * {@link guide/directive} for more info.
6511 * @returns {ng.$compileProvider} Self for chaining.
6513 this.directive = function registerDirective(name, directiveFactory) {
6514 assertNotHasOwnProperty(name, 'directive');
6515 if (isString(name)) {
6516 assertArg(directiveFactory, 'directiveFactory');
6517 if (!hasDirectives.hasOwnProperty(name)) {
6518 hasDirectives[name] = [];
6519 $provide.factory(name + Suffix, ['$injector', '$exceptionHandler',
6520 function($injector, $exceptionHandler) {
6521 var directives = [];
6522 forEach(hasDirectives[name], function(directiveFactory, index) {
6524 var directive = $injector.invoke(directiveFactory);
6525 if (isFunction(directive)) {
6526 directive = { compile: valueFn(directive) };
6527 } else if (!directive.compile && directive.link) {
6528 directive.compile = valueFn(directive.link);
6530 directive.priority = directive.priority || 0;
6531 directive.index = index;
6532 directive.name = directive.name || name;
6533 directive.require = directive.require || (directive.controller && directive.name);
6534 directive.restrict = directive.restrict || 'EA';
6535 if (isObject(directive.scope)) {
6536 directive.$$isolateBindings = parseIsolateBindings(directive.scope, directive.name);
6538 directives.push(directive);
6540 $exceptionHandler(e);
6546 hasDirectives[name].push(directiveFactory);
6548 forEach(name, reverseParams(registerDirective));
6556 * @name $compileProvider#aHrefSanitizationWhitelist
6560 * Retrieves or overrides the default regular expression that is used for whitelisting of safe
6561 * urls during a[href] sanitization.
6563 * The sanitization is a security measure aimed at preventing XSS attacks via html links.
6565 * Any url about to be assigned to a[href] via data-binding is first normalized and turned into
6566 * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist`
6567 * regular expression. If a match is found, the original url is written into the dom. Otherwise,
6568 * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
6570 * @param {RegExp=} regexp New regexp to whitelist urls with.
6571 * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
6572 * chaining otherwise.
6574 this.aHrefSanitizationWhitelist = function(regexp) {
6575 if (isDefined(regexp)) {
6576 $$sanitizeUriProvider.aHrefSanitizationWhitelist(regexp);
6579 return $$sanitizeUriProvider.aHrefSanitizationWhitelist();
6586 * @name $compileProvider#imgSrcSanitizationWhitelist
6590 * Retrieves or overrides the default regular expression that is used for whitelisting of safe
6591 * urls during img[src] sanitization.
6593 * The sanitization is a security measure aimed at prevent XSS attacks via html links.
6595 * Any url about to be assigned to img[src] via data-binding is first normalized and turned into
6596 * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist`
6597 * regular expression. If a match is found, the original url is written into the dom. Otherwise,
6598 * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
6600 * @param {RegExp=} regexp New regexp to whitelist urls with.
6601 * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
6602 * chaining otherwise.
6604 this.imgSrcSanitizationWhitelist = function(regexp) {
6605 if (isDefined(regexp)) {
6606 $$sanitizeUriProvider.imgSrcSanitizationWhitelist(regexp);
6609 return $$sanitizeUriProvider.imgSrcSanitizationWhitelist();
6615 * @name $compileProvider#debugInfoEnabled
6617 * @param {boolean=} enabled update the debugInfoEnabled state if provided, otherwise just return the
6618 * current debugInfoEnabled state
6619 * @returns {*} current value if used as getter or itself (chaining) if used as setter
6624 * Call this method to enable/disable various debug runtime information in the compiler such as adding
6625 * binding information and a reference to the current scope on to DOM elements.
6626 * If enabled, the compiler will add the following to DOM elements that have been bound to the scope
6627 * * `ng-binding` CSS class
6628 * * `$binding` data property containing an array of the binding expressions
6630 * You may want to disable this in production for a significant performance boost. See
6631 * {@link guide/production#disabling-debug-data Disabling Debug Data} for more.
6633 * The default value is true.
6635 var debugInfoEnabled = true;
6636 this.debugInfoEnabled = function(enabled) {
6637 if (isDefined(enabled)) {
6638 debugInfoEnabled = enabled;
6641 return debugInfoEnabled;
6645 '$injector', '$interpolate', '$exceptionHandler', '$templateRequest', '$parse',
6646 '$controller', '$rootScope', '$document', '$sce', '$animate', '$$sanitizeUri',
6647 function($injector, $interpolate, $exceptionHandler, $templateRequest, $parse,
6648 $controller, $rootScope, $document, $sce, $animate, $$sanitizeUri) {
6650 var Attributes = function(element, attributesToCopy) {
6651 if (attributesToCopy) {
6652 var keys = Object.keys(attributesToCopy);
6655 for (i = 0, l = keys.length; i < l; i++) {
6657 this[key] = attributesToCopy[key];
6663 this.$$element = element;
6666 Attributes.prototype = {
6669 * @name $compile.directive.Attributes#$normalize
6673 * Converts an attribute name (e.g. dash/colon/underscore-delimited string, optionally prefixed with `x-` or
6674 * `data-`) to its normalized, camelCase form.
6676 * Also there is special case for Moz prefix starting with upper case letter.
6678 * For further information check out the guide on {@link guide/directive#matching-directives Matching Directives}
6680 * @param {string} name Name to normalize
6682 $normalize: directiveNormalize,
6687 * @name $compile.directive.Attributes#$addClass
6691 * Adds the CSS class value specified by the classVal parameter to the element. If animations
6692 * are enabled then an animation will be triggered for the class addition.
6694 * @param {string} classVal The className value that will be added to the element
6696 $addClass: function(classVal) {
6697 if (classVal && classVal.length > 0) {
6698 $animate.addClass(this.$$element, classVal);
6704 * @name $compile.directive.Attributes#$removeClass
6708 * Removes the CSS class value specified by the classVal parameter from the element. If
6709 * animations are enabled then an animation will be triggered for the class removal.
6711 * @param {string} classVal The className value that will be removed from the element
6713 $removeClass: function(classVal) {
6714 if (classVal && classVal.length > 0) {
6715 $animate.removeClass(this.$$element, classVal);
6721 * @name $compile.directive.Attributes#$updateClass
6725 * Adds and removes the appropriate CSS class values to the element based on the difference
6726 * between the new and old CSS class values (specified as newClasses and oldClasses).
6728 * @param {string} newClasses The current CSS className value
6729 * @param {string} oldClasses The former CSS className value
6731 $updateClass: function(newClasses, oldClasses) {
6732 var toAdd = tokenDifference(newClasses, oldClasses);
6733 if (toAdd && toAdd.length) {
6734 $animate.addClass(this.$$element, toAdd);
6737 var toRemove = tokenDifference(oldClasses, newClasses);
6738 if (toRemove && toRemove.length) {
6739 $animate.removeClass(this.$$element, toRemove);
6744 * Set a normalized attribute on the element in a way such that all directives
6745 * can share the attribute. This function properly handles boolean attributes.
6746 * @param {string} key Normalized key. (ie ngAttribute)
6747 * @param {string|boolean} value The value to set. If `null` attribute will be deleted.
6748 * @param {boolean=} writeAttr If false, does not write the value to DOM element attribute.
6750 * @param {string=} attrName Optional none normalized name. Defaults to key.
6752 $set: function(key, value, writeAttr, attrName) {
6753 // TODO: decide whether or not to throw an error if "class"
6754 //is set through this function since it may cause $updateClass to
6757 var node = this.$$element[0],
6758 booleanKey = getBooleanAttrName(node, key),
6759 aliasedKey = getAliasedAttrName(node, key),
6764 this.$$element.prop(key, value);
6765 attrName = booleanKey;
6766 } else if (aliasedKey) {
6767 this[aliasedKey] = value;
6768 observer = aliasedKey;
6773 // translate normalized key to actual key
6775 this.$attr[key] = attrName;
6777 attrName = this.$attr[key];
6779 this.$attr[key] = attrName = snake_case(key, '-');
6783 nodeName = nodeName_(this.$$element);
6785 if ((nodeName === 'a' && key === 'href') ||
6786 (nodeName === 'img' && key === 'src')) {
6787 // sanitize a[href] and img[src] values
6788 this[key] = value = $$sanitizeUri(value, key === 'src');
6789 } else if (nodeName === 'img' && key === 'srcset') {
6790 // sanitize img[srcset] values
6793 // first check if there are spaces because it's not the same pattern
6794 var trimmedSrcset = trim(value);
6795 // ( 999x ,| 999w ,| ,|, )
6796 var srcPattern = /(\s+\d+x\s*,|\s+\d+w\s*,|\s+,|,\s+)/;
6797 var pattern = /\s/.test(trimmedSrcset) ? srcPattern : /(,)/;
6799 // split srcset into tuple of uri and descriptor except for the last item
6800 var rawUris = trimmedSrcset.split(pattern);
6803 var nbrUrisWith2parts = Math.floor(rawUris.length / 2);
6804 for (var i = 0; i < nbrUrisWith2parts; i++) {
6805 var innerIdx = i * 2;
6807 result += $$sanitizeUri(trim(rawUris[innerIdx]), true);
6808 // add the descriptor
6809 result += (" " + trim(rawUris[innerIdx + 1]));
6812 // split the last item into uri and descriptor
6813 var lastTuple = trim(rawUris[i * 2]).split(/\s/);
6815 // sanitize the last uri
6816 result += $$sanitizeUri(trim(lastTuple[0]), true);
6818 // and add the last descriptor if any
6819 if (lastTuple.length === 2) {
6820 result += (" " + trim(lastTuple[1]));
6822 this[key] = value = result;
6825 if (writeAttr !== false) {
6826 if (value === null || value === undefined) {
6827 this.$$element.removeAttr(attrName);
6829 this.$$element.attr(attrName, value);
6834 var $$observers = this.$$observers;
6835 $$observers && forEach($$observers[observer], function(fn) {
6839 $exceptionHandler(e);
6847 * @name $compile.directive.Attributes#$observe
6851 * Observes an interpolated attribute.
6853 * The observer function will be invoked once during the next `$digest` following
6854 * compilation. The observer is then invoked whenever the interpolated value
6857 * @param {string} key Normalized key. (ie ngAttribute) .
6858 * @param {function(interpolatedValue)} fn Function that will be called whenever
6859 the interpolated value of the attribute changes.
6860 * See the {@link guide/directive#text-and-attribute-bindings Directives} guide for more info.
6861 * @returns {function()} Returns a deregistration function for this observer.
6863 $observe: function(key, fn) {
6865 $$observers = (attrs.$$observers || (attrs.$$observers = createMap())),
6866 listeners = ($$observers[key] || ($$observers[key] = []));
6869 $rootScope.$evalAsync(function() {
6870 if (!listeners.$$inter && attrs.hasOwnProperty(key)) {
6871 // no one registered attribute interpolation function, so lets call it manually
6877 arrayRemove(listeners, fn);
6883 function safeAddClass($element, className) {
6885 $element.addClass(className);
6887 // ignore, since it means that we are trying to set class on
6888 // SVG element, where class name is read-only.
6893 var startSymbol = $interpolate.startSymbol(),
6894 endSymbol = $interpolate.endSymbol(),
6895 denormalizeTemplate = (startSymbol == '{{' || endSymbol == '}}')
6897 : function denormalizeTemplate(template) {
6898 return template.replace(/\{\{/g, startSymbol).replace(/}}/g, endSymbol);
6900 NG_ATTR_BINDING = /^ngAttr[A-Z]/;
6902 compile.$$addBindingInfo = debugInfoEnabled ? function $$addBindingInfo($element, binding) {
6903 var bindings = $element.data('$binding') || [];
6905 if (isArray(binding)) {
6906 bindings = bindings.concat(binding);
6908 bindings.push(binding);
6911 $element.data('$binding', bindings);
6914 compile.$$addBindingClass = debugInfoEnabled ? function $$addBindingClass($element) {
6915 safeAddClass($element, 'ng-binding');
6918 compile.$$addScopeInfo = debugInfoEnabled ? function $$addScopeInfo($element, scope, isolated, noTemplate) {
6919 var dataName = isolated ? (noTemplate ? '$isolateScopeNoTemplate' : '$isolateScope') : '$scope';
6920 $element.data(dataName, scope);
6923 compile.$$addScopeClass = debugInfoEnabled ? function $$addScopeClass($element, isolated) {
6924 safeAddClass($element, isolated ? 'ng-isolate-scope' : 'ng-scope');
6929 //================================
6931 function compile($compileNodes, transcludeFn, maxPriority, ignoreDirective,
6932 previousCompileContext) {
6933 if (!($compileNodes instanceof jqLite)) {
6934 // jquery always rewraps, whereas we need to preserve the original selector so that we can
6936 $compileNodes = jqLite($compileNodes);
6938 // We can not compile top level text elements since text nodes can be merged and we will
6939 // not be able to attach scope data to them, so we will wrap them in <span>
6940 forEach($compileNodes, function(node, index) {
6941 if (node.nodeType == NODE_TYPE_TEXT && node.nodeValue.match(/\S+/) /* non-empty */ ) {
6942 $compileNodes[index] = jqLite(node).wrap('<span></span>').parent()[0];
6945 var compositeLinkFn =
6946 compileNodes($compileNodes, transcludeFn, $compileNodes,
6947 maxPriority, ignoreDirective, previousCompileContext);
6948 compile.$$addScopeClass($compileNodes);
6949 var namespace = null;
6950 return function publicLinkFn(scope, cloneConnectFn, options) {
6951 assertArg(scope, 'scope');
6953 options = options || {};
6954 var parentBoundTranscludeFn = options.parentBoundTranscludeFn,
6955 transcludeControllers = options.transcludeControllers,
6956 futureParentElement = options.futureParentElement;
6958 // When `parentBoundTranscludeFn` is passed, it is a
6959 // `controllersBoundTransclude` function (it was previously passed
6960 // as `transclude` to directive.link) so we must unwrap it to get
6961 // its `boundTranscludeFn`
6962 if (parentBoundTranscludeFn && parentBoundTranscludeFn.$$boundTransclude) {
6963 parentBoundTranscludeFn = parentBoundTranscludeFn.$$boundTransclude;
6967 namespace = detectNamespaceForChildElements(futureParentElement);
6970 if (namespace !== 'html') {
6971 // When using a directive with replace:true and templateUrl the $compileNodes
6972 // (or a child element inside of them)
6973 // might change, so we need to recreate the namespace adapted compileNodes
6974 // for call to the link function.
6975 // Note: This will already clone the nodes...
6977 wrapTemplate(namespace, jqLite('<div>').append($compileNodes).html())
6979 } else if (cloneConnectFn) {
6980 // important!!: we must call our jqLite.clone() since the jQuery one is trying to be smart
6981 // and sometimes changes the structure of the DOM.
6982 $linkNode = JQLitePrototype.clone.call($compileNodes);
6984 $linkNode = $compileNodes;
6987 if (transcludeControllers) {
6988 for (var controllerName in transcludeControllers) {
6989 $linkNode.data('$' + controllerName + 'Controller', transcludeControllers[controllerName].instance);
6993 compile.$$addScopeInfo($linkNode, scope);
6995 if (cloneConnectFn) cloneConnectFn($linkNode, scope);
6996 if (compositeLinkFn) compositeLinkFn(scope, $linkNode, $linkNode, parentBoundTranscludeFn);
7001 function detectNamespaceForChildElements(parentElement) {
7002 // TODO: Make this detect MathML as well...
7003 var node = parentElement && parentElement[0];
7007 return nodeName_(node) !== 'foreignobject' && node.toString().match(/SVG/) ? 'svg' : 'html';
7012 * Compile function matches each node in nodeList against the directives. Once all directives
7013 * for a particular node are collected their compile functions are executed. The compile
7014 * functions return values - the linking functions - are combined into a composite linking
7015 * function, which is the a linking function for the node.
7017 * @param {NodeList} nodeList an array of nodes or NodeList to compile
7018 * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the
7019 * scope argument is auto-generated to the new child of the transcluded parent scope.
7020 * @param {DOMElement=} $rootElement If the nodeList is the root of the compilation tree then
7021 * the rootElement must be set the jqLite collection of the compile root. This is
7022 * needed so that the jqLite collection items can be replaced with widgets.
7023 * @param {number=} maxPriority Max directive priority.
7024 * @returns {Function} A composite linking function of all of the matched directives or null.
7026 function compileNodes(nodeList, transcludeFn, $rootElement, maxPriority, ignoreDirective,
7027 previousCompileContext) {
7029 attrs, directives, nodeLinkFn, childNodes, childLinkFn, linkFnFound, nodeLinkFnFound;
7031 for (var i = 0; i < nodeList.length; i++) {
7032 attrs = new Attributes();
7034 // we must always refer to nodeList[i] since the nodes can be replaced underneath us.
7035 directives = collectDirectives(nodeList[i], [], attrs, i === 0 ? maxPriority : undefined,
7038 nodeLinkFn = (directives.length)
7039 ? applyDirectivesToNode(directives, nodeList[i], attrs, transcludeFn, $rootElement,
7040 null, [], [], previousCompileContext)
7043 if (nodeLinkFn && nodeLinkFn.scope) {
7044 compile.$$addScopeClass(attrs.$$element);
7047 childLinkFn = (nodeLinkFn && nodeLinkFn.terminal ||
7048 !(childNodes = nodeList[i].childNodes) ||
7051 : compileNodes(childNodes,
7053 (nodeLinkFn.transcludeOnThisElement || !nodeLinkFn.templateOnThisElement)
7054 && nodeLinkFn.transclude) : transcludeFn);
7056 if (nodeLinkFn || childLinkFn) {
7057 linkFns.push(i, nodeLinkFn, childLinkFn);
7059 nodeLinkFnFound = nodeLinkFnFound || nodeLinkFn;
7062 //use the previous context only for the first element in the virtual group
7063 previousCompileContext = null;
7066 // return a linking function if we have found anything, null otherwise
7067 return linkFnFound ? compositeLinkFn : null;
7069 function compositeLinkFn(scope, nodeList, $rootElement, parentBoundTranscludeFn) {
7070 var nodeLinkFn, childLinkFn, node, childScope, i, ii, idx, childBoundTranscludeFn;
7074 if (nodeLinkFnFound) {
7075 // copy nodeList so that if a nodeLinkFn removes or adds an element at this DOM level our
7076 // offsets don't get screwed up
7077 var nodeListLength = nodeList.length;
7078 stableNodeList = new Array(nodeListLength);
7080 // create a sparse array by only copying the elements which have a linkFn
7081 for (i = 0; i < linkFns.length; i+=3) {
7083 stableNodeList[idx] = nodeList[idx];
7086 stableNodeList = nodeList;
7089 for (i = 0, ii = linkFns.length; i < ii;) {
7090 node = stableNodeList[linkFns[i++]];
7091 nodeLinkFn = linkFns[i++];
7092 childLinkFn = linkFns[i++];
7095 if (nodeLinkFn.scope) {
7096 childScope = scope.$new();
7097 compile.$$addScopeInfo(jqLite(node), childScope);
7102 if (nodeLinkFn.transcludeOnThisElement) {
7103 childBoundTranscludeFn = createBoundTranscludeFn(
7104 scope, nodeLinkFn.transclude, parentBoundTranscludeFn,
7105 nodeLinkFn.elementTranscludeOnThisElement);
7107 } else if (!nodeLinkFn.templateOnThisElement && parentBoundTranscludeFn) {
7108 childBoundTranscludeFn = parentBoundTranscludeFn;
7110 } else if (!parentBoundTranscludeFn && transcludeFn) {
7111 childBoundTranscludeFn = createBoundTranscludeFn(scope, transcludeFn);
7114 childBoundTranscludeFn = null;
7117 nodeLinkFn(childLinkFn, childScope, node, $rootElement, childBoundTranscludeFn);
7119 } else if (childLinkFn) {
7120 childLinkFn(scope, node.childNodes, undefined, parentBoundTranscludeFn);
7126 function createBoundTranscludeFn(scope, transcludeFn, previousBoundTranscludeFn, elementTransclusion) {
7128 var boundTranscludeFn = function(transcludedScope, cloneFn, controllers, futureParentElement, containingScope) {
7130 if (!transcludedScope) {
7131 transcludedScope = scope.$new(false, containingScope);
7132 transcludedScope.$$transcluded = true;
7135 return transcludeFn(transcludedScope, cloneFn, {
7136 parentBoundTranscludeFn: previousBoundTranscludeFn,
7137 transcludeControllers: controllers,
7138 futureParentElement: futureParentElement
7142 return boundTranscludeFn;
7146 * Looks for directives on the given node and adds them to the directive collection which is
7149 * @param node Node to search.
7150 * @param directives An array to which the directives are added to. This array is sorted before
7151 * the function returns.
7152 * @param attrs The shared attrs object which is used to populate the normalized attributes.
7153 * @param {number=} maxPriority Max directive priority.
7155 function collectDirectives(node, directives, attrs, maxPriority, ignoreDirective) {
7156 var nodeType = node.nodeType,
7157 attrsMap = attrs.$attr,
7162 case NODE_TYPE_ELEMENT: /* Element */
7163 // use the node name: <directive>
7164 addDirective(directives,
7165 directiveNormalize(nodeName_(node)), 'E', maxPriority, ignoreDirective);
7167 // iterate over the attributes
7168 for (var attr, name, nName, ngAttrName, value, isNgAttr, nAttrs = node.attributes,
7169 j = 0, jj = nAttrs && nAttrs.length; j < jj; j++) {
7170 var attrStartName = false;
7171 var attrEndName = false;
7175 value = trim(attr.value);
7177 // support ngAttr attribute binding
7178 ngAttrName = directiveNormalize(name);
7179 if (isNgAttr = NG_ATTR_BINDING.test(ngAttrName)) {
7180 name = name.replace(PREFIX_REGEXP, '')
7181 .substr(8).replace(/_(.)/g, function(match, letter) {
7182 return letter.toUpperCase();
7186 var directiveNName = ngAttrName.replace(/(Start|End)$/, '');
7187 if (directiveIsMultiElement(directiveNName)) {
7188 if (ngAttrName === directiveNName + 'Start') {
7189 attrStartName = name;
7190 attrEndName = name.substr(0, name.length - 5) + 'end';
7191 name = name.substr(0, name.length - 6);
7195 nName = directiveNormalize(name.toLowerCase());
7196 attrsMap[nName] = name;
7197 if (isNgAttr || !attrs.hasOwnProperty(nName)) {
7198 attrs[nName] = value;
7199 if (getBooleanAttrName(node, nName)) {
7200 attrs[nName] = true; // presence means true
7203 addAttrInterpolateDirective(node, directives, value, nName, isNgAttr);
7204 addDirective(directives, nName, 'A', maxPriority, ignoreDirective, attrStartName,
7208 // use class as directive
7209 className = node.className;
7210 if (isObject(className)) {
7211 // Maybe SVGAnimatedString
7212 className = className.animVal;
7214 if (isString(className) && className !== '') {
7215 while (match = CLASS_DIRECTIVE_REGEXP.exec(className)) {
7216 nName = directiveNormalize(match[2]);
7217 if (addDirective(directives, nName, 'C', maxPriority, ignoreDirective)) {
7218 attrs[nName] = trim(match[3]);
7220 className = className.substr(match.index + match[0].length);
7224 case NODE_TYPE_TEXT: /* Text Node */
7225 addTextInterpolateDirective(directives, node.nodeValue);
7227 case NODE_TYPE_COMMENT: /* Comment */
7229 match = COMMENT_DIRECTIVE_REGEXP.exec(node.nodeValue);
7231 nName = directiveNormalize(match[1]);
7232 if (addDirective(directives, nName, 'M', maxPriority, ignoreDirective)) {
7233 attrs[nName] = trim(match[2]);
7237 // turns out that under some circumstances IE9 throws errors when one attempts to read
7238 // comment's node value.
7239 // Just ignore it and continue. (Can't seem to reproduce in test case.)
7244 directives.sort(byPriority);
7249 * Given a node with an directive-start it collects all of the siblings until it finds
7256 function groupScan(node, attrStart, attrEnd) {
7259 if (attrStart && node.hasAttribute && node.hasAttribute(attrStart)) {
7262 throw $compileMinErr('uterdir',
7263 "Unterminated attribute, found '{0}' but no matching '{1}' found.",
7264 attrStart, attrEnd);
7266 if (node.nodeType == NODE_TYPE_ELEMENT) {
7267 if (node.hasAttribute(attrStart)) depth++;
7268 if (node.hasAttribute(attrEnd)) depth--;
7271 node = node.nextSibling;
7272 } while (depth > 0);
7277 return jqLite(nodes);
7281 * Wrapper for linking function which converts normal linking function into a grouped
7286 * @returns {Function}
7288 function groupElementsLinkFnWrapper(linkFn, attrStart, attrEnd) {
7289 return function(scope, element, attrs, controllers, transcludeFn) {
7290 element = groupScan(element[0], attrStart, attrEnd);
7291 return linkFn(scope, element, attrs, controllers, transcludeFn);
7296 * Once the directives have been collected, their compile functions are executed. This method
7297 * is responsible for inlining directive templates as well as terminating the application
7298 * of the directives if the terminal directive has been reached.
7300 * @param {Array} directives Array of collected directives to execute their compile function.
7301 * this needs to be pre-sorted by priority order.
7302 * @param {Node} compileNode The raw DOM node to apply the compile functions to
7303 * @param {Object} templateAttrs The shared attribute function
7304 * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the
7305 * scope argument is auto-generated to the new
7306 * child of the transcluded parent scope.
7307 * @param {JQLite} jqCollection If we are working on the root of the compile tree then this
7308 * argument has the root jqLite array so that we can replace nodes
7310 * @param {Object=} originalReplaceDirective An optional directive that will be ignored when
7311 * compiling the transclusion.
7312 * @param {Array.<Function>} preLinkFns
7313 * @param {Array.<Function>} postLinkFns
7314 * @param {Object} previousCompileContext Context used for previous compilation of the current
7316 * @returns {Function} linkFn
7318 function applyDirectivesToNode(directives, compileNode, templateAttrs, transcludeFn,
7319 jqCollection, originalReplaceDirective, preLinkFns, postLinkFns,
7320 previousCompileContext) {
7321 previousCompileContext = previousCompileContext || {};
7323 var terminalPriority = -Number.MAX_VALUE,
7325 controllerDirectives = previousCompileContext.controllerDirectives,
7327 newIsolateScopeDirective = previousCompileContext.newIsolateScopeDirective,
7328 templateDirective = previousCompileContext.templateDirective,
7329 nonTlbTranscludeDirective = previousCompileContext.nonTlbTranscludeDirective,
7330 hasTranscludeDirective = false,
7331 hasTemplate = false,
7332 hasElementTranscludeDirective = previousCompileContext.hasElementTranscludeDirective,
7333 $compileNode = templateAttrs.$$element = jqLite(compileNode),
7337 replaceDirective = originalReplaceDirective,
7338 childTranscludeFn = transcludeFn,
7342 // executes all directives on the current element
7343 for (var i = 0, ii = directives.length; i < ii; i++) {
7344 directive = directives[i];
7345 var attrStart = directive.$$start;
7346 var attrEnd = directive.$$end;
7348 // collect multiblock sections
7350 $compileNode = groupScan(compileNode, attrStart, attrEnd);
7352 $template = undefined;
7354 if (terminalPriority > directive.priority) {
7355 break; // prevent further processing of directives
7358 if (directiveValue = directive.scope) {
7360 // skip the check for directives with async templates, we'll check the derived sync
7361 // directive when the template arrives
7362 if (!directive.templateUrl) {
7363 if (isObject(directiveValue)) {
7364 // This directive is trying to add an isolated scope.
7365 // Check that there is no scope of any kind already
7366 assertNoDuplicate('new/isolated scope', newIsolateScopeDirective || newScopeDirective,
7367 directive, $compileNode);
7368 newIsolateScopeDirective = directive;
7370 // This directive is trying to add a child scope.
7371 // Check that there is no isolated scope already
7372 assertNoDuplicate('new/isolated scope', newIsolateScopeDirective, directive,
7377 newScopeDirective = newScopeDirective || directive;
7380 directiveName = directive.name;
7382 if (!directive.templateUrl && directive.controller) {
7383 directiveValue = directive.controller;
7384 controllerDirectives = controllerDirectives || {};
7385 assertNoDuplicate("'" + directiveName + "' controller",
7386 controllerDirectives[directiveName], directive, $compileNode);
7387 controllerDirectives[directiveName] = directive;
7390 if (directiveValue = directive.transclude) {
7391 hasTranscludeDirective = true;
7393 // Special case ngIf and ngRepeat so that we don't complain about duplicate transclusion.
7394 // This option should only be used by directives that know how to safely handle element transclusion,
7395 // where the transcluded nodes are added or replaced after linking.
7396 if (!directive.$$tlb) {
7397 assertNoDuplicate('transclusion', nonTlbTranscludeDirective, directive, $compileNode);
7398 nonTlbTranscludeDirective = directive;
7401 if (directiveValue == 'element') {
7402 hasElementTranscludeDirective = true;
7403 terminalPriority = directive.priority;
7404 $template = $compileNode;
7405 $compileNode = templateAttrs.$$element =
7406 jqLite(document.createComment(' ' + directiveName + ': ' +
7407 templateAttrs[directiveName] + ' '));
7408 compileNode = $compileNode[0];
7409 replaceWith(jqCollection, sliceArgs($template), compileNode);
7411 childTranscludeFn = compile($template, transcludeFn, terminalPriority,
7412 replaceDirective && replaceDirective.name, {
7414 // - controllerDirectives - otherwise we'll create duplicates controllers
7415 // - newIsolateScopeDirective or templateDirective - combining templates with
7416 // element transclusion doesn't make sense.
7418 // We need only nonTlbTranscludeDirective so that we prevent putting transclusion
7419 // on the same element more than once.
7420 nonTlbTranscludeDirective: nonTlbTranscludeDirective
7423 $template = jqLite(jqLiteClone(compileNode)).contents();
7424 $compileNode.empty(); // clear contents
7425 childTranscludeFn = compile($template, transcludeFn);
7429 if (directive.template) {
7431 assertNoDuplicate('template', templateDirective, directive, $compileNode);
7432 templateDirective = directive;
7434 directiveValue = (isFunction(directive.template))
7435 ? directive.template($compileNode, templateAttrs)
7436 : directive.template;
7438 directiveValue = denormalizeTemplate(directiveValue);
7440 if (directive.replace) {
7441 replaceDirective = directive;
7442 if (jqLiteIsTextNode(directiveValue)) {
7445 $template = removeComments(wrapTemplate(directive.templateNamespace, trim(directiveValue)));
7447 compileNode = $template[0];
7449 if ($template.length != 1 || compileNode.nodeType !== NODE_TYPE_ELEMENT) {
7450 throw $compileMinErr('tplrt',
7451 "Template for directive '{0}' must have exactly one root element. {1}",
7455 replaceWith(jqCollection, $compileNode, compileNode);
7457 var newTemplateAttrs = {$attr: {}};
7459 // combine directives from the original node and from the template:
7460 // - take the array of directives for this element
7461 // - split it into two parts, those that already applied (processed) and those that weren't (unprocessed)
7462 // - collect directives from the template and sort them by priority
7463 // - combine directives as: processed + template + unprocessed
7464 var templateDirectives = collectDirectives(compileNode, [], newTemplateAttrs);
7465 var unprocessedDirectives = directives.splice(i + 1, directives.length - (i + 1));
7467 if (newIsolateScopeDirective) {
7468 markDirectivesAsIsolate(templateDirectives);
7470 directives = directives.concat(templateDirectives).concat(unprocessedDirectives);
7471 mergeTemplateAttributes(templateAttrs, newTemplateAttrs);
7473 ii = directives.length;
7475 $compileNode.html(directiveValue);
7479 if (directive.templateUrl) {
7481 assertNoDuplicate('template', templateDirective, directive, $compileNode);
7482 templateDirective = directive;
7484 if (directive.replace) {
7485 replaceDirective = directive;
7488 nodeLinkFn = compileTemplateUrl(directives.splice(i, directives.length - i), $compileNode,
7489 templateAttrs, jqCollection, hasTranscludeDirective && childTranscludeFn, preLinkFns, postLinkFns, {
7490 controllerDirectives: controllerDirectives,
7491 newIsolateScopeDirective: newIsolateScopeDirective,
7492 templateDirective: templateDirective,
7493 nonTlbTranscludeDirective: nonTlbTranscludeDirective
7495 ii = directives.length;
7496 } else if (directive.compile) {
7498 linkFn = directive.compile($compileNode, templateAttrs, childTranscludeFn);
7499 if (isFunction(linkFn)) {
7500 addLinkFns(null, linkFn, attrStart, attrEnd);
7501 } else if (linkFn) {
7502 addLinkFns(linkFn.pre, linkFn.post, attrStart, attrEnd);
7505 $exceptionHandler(e, startingTag($compileNode));
7509 if (directive.terminal) {
7510 nodeLinkFn.terminal = true;
7511 terminalPriority = Math.max(terminalPriority, directive.priority);
7516 nodeLinkFn.scope = newScopeDirective && newScopeDirective.scope === true;
7517 nodeLinkFn.transcludeOnThisElement = hasTranscludeDirective;
7518 nodeLinkFn.elementTranscludeOnThisElement = hasElementTranscludeDirective;
7519 nodeLinkFn.templateOnThisElement = hasTemplate;
7520 nodeLinkFn.transclude = childTranscludeFn;
7522 previousCompileContext.hasElementTranscludeDirective = hasElementTranscludeDirective;
7524 // might be normal or delayed nodeLinkFn depending on if templateUrl is present
7527 ////////////////////
7529 function addLinkFns(pre, post, attrStart, attrEnd) {
7531 if (attrStart) pre = groupElementsLinkFnWrapper(pre, attrStart, attrEnd);
7532 pre.require = directive.require;
7533 pre.directiveName = directiveName;
7534 if (newIsolateScopeDirective === directive || directive.$$isolateScope) {
7535 pre = cloneAndAnnotateFn(pre, {isolateScope: true});
7537 preLinkFns.push(pre);
7540 if (attrStart) post = groupElementsLinkFnWrapper(post, attrStart, attrEnd);
7541 post.require = directive.require;
7542 post.directiveName = directiveName;
7543 if (newIsolateScopeDirective === directive || directive.$$isolateScope) {
7544 post = cloneAndAnnotateFn(post, {isolateScope: true});
7546 postLinkFns.push(post);
7551 function getControllers(directiveName, require, $element, elementControllers) {
7552 var value, retrievalMethod = 'data', optional = false;
7553 var $searchElement = $element;
7555 if (isString(require)) {
7556 match = require.match(REQUIRE_PREFIX_REGEXP);
7557 require = require.substring(match[0].length);
7560 if (match[1]) match[3] = null;
7561 else match[1] = match[3];
7563 if (match[1] === '^') {
7564 retrievalMethod = 'inheritedData';
7565 } else if (match[1] === '^^') {
7566 retrievalMethod = 'inheritedData';
7567 $searchElement = $element.parent();
7569 if (match[2] === '?') {
7575 if (elementControllers && retrievalMethod === 'data') {
7576 if (value = elementControllers[require]) {
7577 value = value.instance;
7580 value = value || $searchElement[retrievalMethod]('$' + require + 'Controller');
7582 if (!value && !optional) {
7583 throw $compileMinErr('ctreq',
7584 "Controller '{0}', required by directive '{1}', can't be found!",
7585 require, directiveName);
7587 return value || null;
7588 } else if (isArray(require)) {
7590 forEach(require, function(require) {
7591 value.push(getControllers(directiveName, require, $element, elementControllers));
7598 function nodeLinkFn(childLinkFn, scope, linkNode, $rootElement, boundTranscludeFn) {
7599 var i, ii, linkFn, controller, isolateScope, elementControllers, transcludeFn, $element,
7602 if (compileNode === linkNode) {
7603 attrs = templateAttrs;
7604 $element = templateAttrs.$$element;
7606 $element = jqLite(linkNode);
7607 attrs = new Attributes($element, templateAttrs);
7610 if (newIsolateScopeDirective) {
7611 isolateScope = scope.$new(true);
7614 if (boundTranscludeFn) {
7615 // track `boundTranscludeFn` so it can be unwrapped if `transcludeFn`
7616 // is later passed as `parentBoundTranscludeFn` to `publicLinkFn`
7617 transcludeFn = controllersBoundTransclude;
7618 transcludeFn.$$boundTransclude = boundTranscludeFn;
7621 if (controllerDirectives) {
7622 // TODO: merge `controllers` and `elementControllers` into single object.
7624 elementControllers = {};
7625 forEach(controllerDirectives, function(directive) {
7627 $scope: directive === newIsolateScopeDirective || directive.$$isolateScope ? isolateScope : scope,
7630 $transclude: transcludeFn
7631 }, controllerInstance;
7633 controller = directive.controller;
7634 if (controller == '@') {
7635 controller = attrs[directive.name];
7638 controllerInstance = $controller(controller, locals, true, directive.controllerAs);
7640 // For directives with element transclusion the element is a comment,
7641 // but jQuery .data doesn't support attaching data to comment nodes as it's hard to
7642 // clean up (http://bugs.jquery.com/ticket/8335).
7643 // Instead, we save the controllers for the element in a local hash and attach to .data
7644 // later, once we have the actual element.
7645 elementControllers[directive.name] = controllerInstance;
7646 if (!hasElementTranscludeDirective) {
7647 $element.data('$' + directive.name + 'Controller', controllerInstance.instance);
7650 controllers[directive.name] = controllerInstance;
7654 if (newIsolateScopeDirective) {
7655 compile.$$addScopeInfo($element, isolateScope, true, !(templateDirective && (templateDirective === newIsolateScopeDirective ||
7656 templateDirective === newIsolateScopeDirective.$$originalDirective)));
7657 compile.$$addScopeClass($element, true);
7659 var isolateScopeController = controllers && controllers[newIsolateScopeDirective.name];
7660 var isolateBindingContext = isolateScope;
7661 if (isolateScopeController && isolateScopeController.identifier &&
7662 newIsolateScopeDirective.bindToController === true) {
7663 isolateBindingContext = isolateScopeController.instance;
7666 forEach(isolateScope.$$isolateBindings = newIsolateScopeDirective.$$isolateBindings, function(definition, scopeName) {
7667 var attrName = definition.attrName,
7668 optional = definition.optional,
7669 mode = definition.mode, // @, =, or &
7671 parentGet, parentSet, compare;
7676 attrs.$observe(attrName, function(value) {
7677 isolateBindingContext[scopeName] = value;
7679 attrs.$$observers[attrName].$$scope = scope;
7680 if (attrs[attrName]) {
7681 // If the attribute has been provided then we trigger an interpolation to ensure
7682 // the value is there for use in the link fn
7683 isolateBindingContext[scopeName] = $interpolate(attrs[attrName])(scope);
7688 if (optional && !attrs[attrName]) {
7691 parentGet = $parse(attrs[attrName]);
7692 if (parentGet.literal) {
7695 compare = function(a, b) { return a === b || (a !== a && b !== b); };
7697 parentSet = parentGet.assign || function() {
7698 // reset the change, or we will throw this exception on every $digest
7699 lastValue = isolateBindingContext[scopeName] = parentGet(scope);
7700 throw $compileMinErr('nonassign',
7701 "Expression '{0}' used with directive '{1}' is non-assignable!",
7702 attrs[attrName], newIsolateScopeDirective.name);
7704 lastValue = isolateBindingContext[scopeName] = parentGet(scope);
7705 var parentValueWatch = function parentValueWatch(parentValue) {
7706 if (!compare(parentValue, isolateBindingContext[scopeName])) {
7707 // we are out of sync and need to copy
7708 if (!compare(parentValue, lastValue)) {
7709 // parent changed and it has precedence
7710 isolateBindingContext[scopeName] = parentValue;
7712 // if the parent can be assigned then do so
7713 parentSet(scope, parentValue = isolateBindingContext[scopeName]);
7716 return lastValue = parentValue;
7718 parentValueWatch.$stateful = true;
7720 if (definition.collection) {
7721 unwatch = scope.$watchCollection(attrs[attrName], parentValueWatch);
7723 unwatch = scope.$watch($parse(attrs[attrName], parentValueWatch), null, parentGet.literal);
7725 isolateScope.$on('$destroy', unwatch);
7729 parentGet = $parse(attrs[attrName]);
7730 isolateBindingContext[scopeName] = function(locals) {
7731 return parentGet(scope, locals);
7738 forEach(controllers, function(controller) {
7745 for (i = 0, ii = preLinkFns.length; i < ii; i++) {
7746 linkFn = preLinkFns[i];
7747 invokeLinkFn(linkFn,
7748 linkFn.isolateScope ? isolateScope : scope,
7751 linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers),
7757 // We only pass the isolate scope, if the isolate directive has a template,
7758 // otherwise the child elements do not belong to the isolate directive.
7759 var scopeToChild = scope;
7760 if (newIsolateScopeDirective && (newIsolateScopeDirective.template || newIsolateScopeDirective.templateUrl === null)) {
7761 scopeToChild = isolateScope;
7763 childLinkFn && childLinkFn(scopeToChild, linkNode.childNodes, undefined, boundTranscludeFn);
7766 for (i = postLinkFns.length - 1; i >= 0; i--) {
7767 linkFn = postLinkFns[i];
7768 invokeLinkFn(linkFn,
7769 linkFn.isolateScope ? isolateScope : scope,
7772 linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers),
7777 // This is the function that is injected as `$transclude`.
7778 // Note: all arguments are optional!
7779 function controllersBoundTransclude(scope, cloneAttachFn, futureParentElement) {
7780 var transcludeControllers;
7782 // No scope passed in:
7783 if (!isScope(scope)) {
7784 futureParentElement = cloneAttachFn;
7785 cloneAttachFn = scope;
7789 if (hasElementTranscludeDirective) {
7790 transcludeControllers = elementControllers;
7792 if (!futureParentElement) {
7793 futureParentElement = hasElementTranscludeDirective ? $element.parent() : $element;
7795 return boundTranscludeFn(scope, cloneAttachFn, transcludeControllers, futureParentElement, scopeToChild);
7800 function markDirectivesAsIsolate(directives) {
7801 // mark all directives as needing isolate scope.
7802 for (var j = 0, jj = directives.length; j < jj; j++) {
7803 directives[j] = inherit(directives[j], {$$isolateScope: true});
7808 * looks up the directive and decorates it with exception handling and proper parameters. We
7809 * call this the boundDirective.
7811 * @param {string} name name of the directive to look up.
7812 * @param {string} location The directive must be found in specific format.
7813 * String containing any of theses characters:
7815 * * `E`: element name
7819 * @returns {boolean} true if directive was added.
7821 function addDirective(tDirectives, name, location, maxPriority, ignoreDirective, startAttrName,
7823 if (name === ignoreDirective) return null;
7825 if (hasDirectives.hasOwnProperty(name)) {
7826 for (var directive, directives = $injector.get(name + Suffix),
7827 i = 0, ii = directives.length; i < ii; i++) {
7829 directive = directives[i];
7830 if ((maxPriority === undefined || maxPriority > directive.priority) &&
7831 directive.restrict.indexOf(location) != -1) {
7832 if (startAttrName) {
7833 directive = inherit(directive, {$$start: startAttrName, $$end: endAttrName});
7835 tDirectives.push(directive);
7838 } catch (e) { $exceptionHandler(e); }
7846 * looks up the directive and returns true if it is a multi-element directive,
7847 * and therefore requires DOM nodes between -start and -end markers to be grouped
7850 * @param {string} name name of the directive to look up.
7851 * @returns true if directive was registered as multi-element.
7853 function directiveIsMultiElement(name) {
7854 if (hasDirectives.hasOwnProperty(name)) {
7855 for (var directive, directives = $injector.get(name + Suffix),
7856 i = 0, ii = directives.length; i < ii; i++) {
7857 directive = directives[i];
7858 if (directive.multiElement) {
7867 * When the element is replaced with HTML template then the new attributes
7868 * on the template need to be merged with the existing attributes in the DOM.
7869 * The desired effect is to have both of the attributes present.
7871 * @param {object} dst destination attributes (original DOM)
7872 * @param {object} src source attributes (from the directive template)
7874 function mergeTemplateAttributes(dst, src) {
7875 var srcAttr = src.$attr,
7876 dstAttr = dst.$attr,
7877 $element = dst.$$element;
7879 // reapply the old attributes to the new element
7880 forEach(dst, function(value, key) {
7881 if (key.charAt(0) != '$') {
7882 if (src[key] && src[key] !== value) {
7883 value += (key === 'style' ? ';' : ' ') + src[key];
7885 dst.$set(key, value, true, srcAttr[key]);
7889 // copy the new attributes on the old attrs object
7890 forEach(src, function(value, key) {
7891 if (key == 'class') {
7892 safeAddClass($element, value);
7893 dst['class'] = (dst['class'] ? dst['class'] + ' ' : '') + value;
7894 } else if (key == 'style') {
7895 $element.attr('style', $element.attr('style') + ';' + value);
7896 dst['style'] = (dst['style'] ? dst['style'] + ';' : '') + value;
7897 // `dst` will never contain hasOwnProperty as DOM parser won't let it.
7898 // You will get an "InvalidCharacterError: DOM Exception 5" error if you
7899 // have an attribute like "has-own-property" or "data-has-own-property", etc.
7900 } else if (key.charAt(0) != '$' && !dst.hasOwnProperty(key)) {
7902 dstAttr[key] = srcAttr[key];
7908 function compileTemplateUrl(directives, $compileNode, tAttrs,
7909 $rootElement, childTranscludeFn, preLinkFns, postLinkFns, previousCompileContext) {
7911 afterTemplateNodeLinkFn,
7912 afterTemplateChildLinkFn,
7913 beforeTemplateCompileNode = $compileNode[0],
7914 origAsyncDirective = directives.shift(),
7915 derivedSyncDirective = inherit(origAsyncDirective, {
7916 templateUrl: null, transclude: null, replace: null, $$originalDirective: origAsyncDirective
7918 templateUrl = (isFunction(origAsyncDirective.templateUrl))
7919 ? origAsyncDirective.templateUrl($compileNode, tAttrs)
7920 : origAsyncDirective.templateUrl,
7921 templateNamespace = origAsyncDirective.templateNamespace;
7923 $compileNode.empty();
7925 $templateRequest($sce.getTrustedResourceUrl(templateUrl))
7926 .then(function(content) {
7927 var compileNode, tempTemplateAttrs, $template, childBoundTranscludeFn;
7929 content = denormalizeTemplate(content);
7931 if (origAsyncDirective.replace) {
7932 if (jqLiteIsTextNode(content)) {
7935 $template = removeComments(wrapTemplate(templateNamespace, trim(content)));
7937 compileNode = $template[0];
7939 if ($template.length != 1 || compileNode.nodeType !== NODE_TYPE_ELEMENT) {
7940 throw $compileMinErr('tplrt',
7941 "Template for directive '{0}' must have exactly one root element. {1}",
7942 origAsyncDirective.name, templateUrl);
7945 tempTemplateAttrs = {$attr: {}};
7946 replaceWith($rootElement, $compileNode, compileNode);
7947 var templateDirectives = collectDirectives(compileNode, [], tempTemplateAttrs);
7949 if (isObject(origAsyncDirective.scope)) {
7950 markDirectivesAsIsolate(templateDirectives);
7952 directives = templateDirectives.concat(directives);
7953 mergeTemplateAttributes(tAttrs, tempTemplateAttrs);
7955 compileNode = beforeTemplateCompileNode;
7956 $compileNode.html(content);
7959 directives.unshift(derivedSyncDirective);
7961 afterTemplateNodeLinkFn = applyDirectivesToNode(directives, compileNode, tAttrs,
7962 childTranscludeFn, $compileNode, origAsyncDirective, preLinkFns, postLinkFns,
7963 previousCompileContext);
7964 forEach($rootElement, function(node, i) {
7965 if (node == compileNode) {
7966 $rootElement[i] = $compileNode[0];
7969 afterTemplateChildLinkFn = compileNodes($compileNode[0].childNodes, childTranscludeFn);
7971 while (linkQueue.length) {
7972 var scope = linkQueue.shift(),
7973 beforeTemplateLinkNode = linkQueue.shift(),
7974 linkRootElement = linkQueue.shift(),
7975 boundTranscludeFn = linkQueue.shift(),
7976 linkNode = $compileNode[0];
7978 if (scope.$$destroyed) continue;
7980 if (beforeTemplateLinkNode !== beforeTemplateCompileNode) {
7981 var oldClasses = beforeTemplateLinkNode.className;
7983 if (!(previousCompileContext.hasElementTranscludeDirective &&
7984 origAsyncDirective.replace)) {
7985 // it was cloned therefore we have to clone as well.
7986 linkNode = jqLiteClone(compileNode);
7988 replaceWith(linkRootElement, jqLite(beforeTemplateLinkNode), linkNode);
7990 // Copy in CSS classes from original node
7991 safeAddClass(jqLite(linkNode), oldClasses);
7993 if (afterTemplateNodeLinkFn.transcludeOnThisElement) {
7994 childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn);
7996 childBoundTranscludeFn = boundTranscludeFn;
7998 afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, linkNode, $rootElement,
7999 childBoundTranscludeFn);
8004 return function delayedNodeLinkFn(ignoreChildLinkFn, scope, node, rootElement, boundTranscludeFn) {
8005 var childBoundTranscludeFn = boundTranscludeFn;
8006 if (scope.$$destroyed) return;
8008 linkQueue.push(scope,
8011 childBoundTranscludeFn);
8013 if (afterTemplateNodeLinkFn.transcludeOnThisElement) {
8014 childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn);
8016 afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, node, rootElement, childBoundTranscludeFn);
8023 * Sorting function for bound directives.
8025 function byPriority(a, b) {
8026 var diff = b.priority - a.priority;
8027 if (diff !== 0) return diff;
8028 if (a.name !== b.name) return (a.name < b.name) ? -1 : 1;
8029 return a.index - b.index;
8033 function assertNoDuplicate(what, previousDirective, directive, element) {
8034 if (previousDirective) {
8035 throw $compileMinErr('multidir', 'Multiple directives [{0}, {1}] asking for {2} on: {3}',
8036 previousDirective.name, directive.name, what, startingTag(element));
8041 function addTextInterpolateDirective(directives, text) {
8042 var interpolateFn = $interpolate(text, true);
8043 if (interpolateFn) {
8046 compile: function textInterpolateCompileFn(templateNode) {
8047 var templateNodeParent = templateNode.parent(),
8048 hasCompileParent = !!templateNodeParent.length;
8050 // When transcluding a template that has bindings in the root
8051 // we don't have a parent and thus need to add the class during linking fn.
8052 if (hasCompileParent) compile.$$addBindingClass(templateNodeParent);
8054 return function textInterpolateLinkFn(scope, node) {
8055 var parent = node.parent();
8056 if (!hasCompileParent) compile.$$addBindingClass(parent);
8057 compile.$$addBindingInfo(parent, interpolateFn.expressions);
8058 scope.$watch(interpolateFn, function interpolateFnWatchAction(value) {
8059 node[0].nodeValue = value;
8068 function wrapTemplate(type, template) {
8069 type = lowercase(type || 'html');
8073 var wrapper = document.createElement('div');
8074 wrapper.innerHTML = '<' + type + '>' + template + '</' + type + '>';
8075 return wrapper.childNodes[0].childNodes;
8082 function getTrustedContext(node, attrNormalizedName) {
8083 if (attrNormalizedName == "srcdoc") {
8086 var tag = nodeName_(node);
8087 // maction[xlink:href] can source SVG. It's not limited to <maction>.
8088 if (attrNormalizedName == "xlinkHref" ||
8089 (tag == "form" && attrNormalizedName == "action") ||
8090 (tag != "img" && (attrNormalizedName == "src" ||
8091 attrNormalizedName == "ngSrc"))) {
8092 return $sce.RESOURCE_URL;
8097 function addAttrInterpolateDirective(node, directives, value, name, allOrNothing) {
8098 var trustedContext = getTrustedContext(node, name);
8099 allOrNothing = ALL_OR_NOTHING_ATTRS[name] || allOrNothing;
8101 var interpolateFn = $interpolate(value, true, trustedContext, allOrNothing);
8103 // no interpolation found -> ignore
8104 if (!interpolateFn) return;
8107 if (name === "multiple" && nodeName_(node) === "select") {
8108 throw $compileMinErr("selmulti",
8109 "Binding to the 'multiple' attribute is not supported. Element: {0}",
8115 compile: function() {
8117 pre: function attrInterpolatePreLinkFn(scope, element, attr) {
8118 var $$observers = (attr.$$observers || (attr.$$observers = {}));
8120 if (EVENT_HANDLER_ATTR_REGEXP.test(name)) {
8121 throw $compileMinErr('nodomevents',
8122 "Interpolations for HTML DOM event attributes are disallowed. Please use the " +
8123 "ng- versions (such as ng-click instead of onclick) instead.");
8126 // If the attribute has changed since last $interpolate()ed
8127 var newValue = attr[name];
8128 if (newValue !== value) {
8129 // we need to interpolate again since the attribute value has been updated
8130 // (e.g. by another directive's compile function)
8131 // ensure unset/empty values make interpolateFn falsy
8132 interpolateFn = newValue && $interpolate(newValue, true, trustedContext, allOrNothing);
8136 // if attribute was updated so that there is no interpolation going on we don't want to
8137 // register any observers
8138 if (!interpolateFn) return;
8140 // initialize attr object so that it's ready in case we need the value for isolate
8141 // scope initialization, otherwise the value would not be available from isolate
8142 // directive's linking fn during linking phase
8143 attr[name] = interpolateFn(scope);
8145 ($$observers[name] || ($$observers[name] = [])).$$inter = true;
8146 (attr.$$observers && attr.$$observers[name].$$scope || scope).
8147 $watch(interpolateFn, function interpolateFnWatchAction(newValue, oldValue) {
8148 //special case for class attribute addition + removal
8149 //so that class changes can tap into the animation
8150 //hooks provided by the $animate service. Be sure to
8151 //skip animations when the first digest occurs (when
8152 //both the new and the old values are the same) since
8153 //the CSS classes are the non-interpolated values
8154 if (name === 'class' && newValue != oldValue) {
8155 attr.$updateClass(newValue, oldValue);
8157 attr.$set(name, newValue);
8168 * This is a special jqLite.replaceWith, which can replace items which
8169 * have no parents, provided that the containing jqLite collection is provided.
8171 * @param {JqLite=} $rootElement The root of the compile tree. Used so that we can replace nodes
8172 * in the root of the tree.
8173 * @param {JqLite} elementsToRemove The jqLite element which we are going to replace. We keep
8174 * the shell, but replace its DOM node reference.
8175 * @param {Node} newNode The new DOM node.
8177 function replaceWith($rootElement, elementsToRemove, newNode) {
8178 var firstElementToRemove = elementsToRemove[0],
8179 removeCount = elementsToRemove.length,
8180 parent = firstElementToRemove.parentNode,
8184 for (i = 0, ii = $rootElement.length; i < ii; i++) {
8185 if ($rootElement[i] == firstElementToRemove) {
8186 $rootElement[i++] = newNode;
8187 for (var j = i, j2 = j + removeCount - 1,
8188 jj = $rootElement.length;
8189 j < jj; j++, j2++) {
8191 $rootElement[j] = $rootElement[j2];
8193 delete $rootElement[j];
8196 $rootElement.length -= removeCount - 1;
8198 // If the replaced element is also the jQuery .context then replace it
8199 // .context is a deprecated jQuery api, so we should set it only when jQuery set it
8200 // http://api.jquery.com/context/
8201 if ($rootElement.context === firstElementToRemove) {
8202 $rootElement.context = newNode;
8210 parent.replaceChild(newNode, firstElementToRemove);
8213 // TODO(perf): what's this document fragment for? is it needed? can we at least reuse it?
8214 var fragment = document.createDocumentFragment();
8215 fragment.appendChild(firstElementToRemove);
8217 // Copy over user data (that includes Angular's $scope etc.). Don't copy private
8218 // data here because there's no public interface in jQuery to do that and copying over
8219 // event listeners (which is the main use of private data) wouldn't work anyway.
8220 jqLite(newNode).data(jqLite(firstElementToRemove).data());
8222 // Remove data of the replaced element. We cannot just call .remove()
8223 // on the element it since that would deallocate scope that is needed
8224 // for the new node. Instead, remove the data "manually".
8226 delete jqLite.cache[firstElementToRemove[jqLite.expando]];
8228 // jQuery 2.x doesn't expose the data storage. Use jQuery.cleanData to clean up after
8229 // the replaced element. The cleanData version monkey-patched by Angular would cause
8230 // the scope to be trashed and we do need the very same scope to work with the new
8231 // element. However, we cannot just cache the non-patched version and use it here as
8232 // that would break if another library patches the method after Angular does (one
8233 // example is jQuery UI). Instead, set a flag indicating scope destroying should be
8234 // skipped this one time.
8235 skipDestroyOnNextJQueryCleanData = true;
8236 jQuery.cleanData([firstElementToRemove]);
8239 for (var k = 1, kk = elementsToRemove.length; k < kk; k++) {
8240 var element = elementsToRemove[k];
8241 jqLite(element).remove(); // must do this way to clean up expando
8242 fragment.appendChild(element);
8243 delete elementsToRemove[k];
8246 elementsToRemove[0] = newNode;
8247 elementsToRemove.length = 1;
8251 function cloneAndAnnotateFn(fn, annotation) {
8252 return extend(function() { return fn.apply(null, arguments); }, fn, annotation);
8256 function invokeLinkFn(linkFn, scope, $element, attrs, controllers, transcludeFn) {
8258 linkFn(scope, $element, attrs, controllers, transcludeFn);
8260 $exceptionHandler(e, startingTag($element));
8266 var PREFIX_REGEXP = /^((?:x|data)[\:\-_])/i;
8268 * Converts all accepted directives format into proper directive name.
8269 * @param name Name to normalize
8271 function directiveNormalize(name) {
8272 return camelCase(name.replace(PREFIX_REGEXP, ''));
8277 * @name $compile.directive.Attributes
8280 * A shared object between directive compile / linking functions which contains normalized DOM
8281 * element attributes. The values reflect current binding state `{{ }}`. The normalization is
8282 * needed since all of these are treated as equivalent in Angular:
8285 * <span ng:bind="a" ng-bind="a" data-ng-bind="a" x-ng-bind="a">
8291 * @name $compile.directive.Attributes#$attr
8294 * A map of DOM element attribute names to the normalized name. This is
8295 * needed to do reverse lookup from normalized name back to actual name.
8301 * @name $compile.directive.Attributes#$set
8305 * Set DOM element attribute value.
8308 * @param {string} name Normalized element attribute name of the property to modify. The name is
8309 * reverse-translated using the {@link ng.$compile.directive.Attributes#$attr $attr}
8310 * property to the original name.
8311 * @param {string} value Value to set the attribute to. The value can be an interpolated string.
8317 * Closure compiler type information
8320 function nodesetLinkingFn(
8321 /* angular.Scope */ scope,
8322 /* NodeList */ nodeList,
8323 /* Element */ rootElement,
8324 /* function(Function) */ boundTranscludeFn
8327 function directiveLinkingFn(
8328 /* nodesetLinkingFn */ nodesetLinkingFn,
8329 /* angular.Scope */ scope,
8331 /* Element */ rootElement,
8332 /* function(Function) */ boundTranscludeFn
8335 function tokenDifference(str1, str2) {
8337 tokens1 = str1.split(/\s+/),
8338 tokens2 = str2.split(/\s+/);
8341 for (var i = 0; i < tokens1.length; i++) {
8342 var token = tokens1[i];
8343 for (var j = 0; j < tokens2.length; j++) {
8344 if (token == tokens2[j]) continue outer;
8346 values += (values.length > 0 ? ' ' : '') + token;
8351 function removeComments(jqNodes) {
8352 jqNodes = jqLite(jqNodes);
8353 var i = jqNodes.length;
8360 var node = jqNodes[i];
8361 if (node.nodeType === NODE_TYPE_COMMENT) {
8362 splice.call(jqNodes, i, 1);
8368 var $controllerMinErr = minErr('$controller');
8372 * @name $controllerProvider
8374 * The {@link ng.$controller $controller service} is used by Angular to create new
8377 * This provider allows controller registration via the
8378 * {@link ng.$controllerProvider#register register} method.
8380 function $ControllerProvider() {
8381 var controllers = {},
8383 CNTRL_REG = /^(\S+)(\s+as\s+(\w+))?$/;
8388 * @name $controllerProvider#register
8389 * @param {string|Object} name Controller name, or an object map of controllers where the keys are
8390 * the names and the values are the constructors.
8391 * @param {Function|Array} constructor Controller constructor fn (optionally decorated with DI
8392 * annotations in the array notation).
8394 this.register = function(name, constructor) {
8395 assertNotHasOwnProperty(name, 'controller');
8396 if (isObject(name)) {
8397 extend(controllers, name);
8399 controllers[name] = constructor;
8405 * @name $controllerProvider#allowGlobals
8406 * @description If called, allows `$controller` to find controller constructors on `window`
8408 this.allowGlobals = function() {
8413 this.$get = ['$injector', '$window', function($injector, $window) {
8418 * @requires $injector
8420 * @param {Function|string} constructor If called with a function then it's considered to be the
8421 * controller constructor function. Otherwise it's considered to be a string which is used
8422 * to retrieve the controller constructor using the following steps:
8424 * * check if a controller with given name is registered via `$controllerProvider`
8425 * * check if evaluating the string on the current scope returns a constructor
8426 * * if $controllerProvider#allowGlobals, check `window[constructor]` on the global
8427 * `window` object (not recommended)
8429 * The string can use the `controller as property` syntax, where the controller instance is published
8430 * as the specified property on the `scope`; the `scope` must be injected into `locals` param for this
8431 * to work correctly.
8433 * @param {Object} locals Injection locals for Controller.
8434 * @return {Object} Instance of given controller.
8437 * `$controller` service is responsible for instantiating controllers.
8439 * It's just a simple call to {@link auto.$injector $injector}, but extracted into
8440 * a service, so that one can override this service with [BC version](https://gist.github.com/1649788).
8442 return function(expression, locals, later, ident) {
8444 // param `later` --- indicates that the controller's constructor is invoked at a later time.
8445 // If true, $controller will allocate the object with the correct
8446 // prototype chain, but will not invoke the controller until a returned
8447 // callback is invoked.
8448 // param `ident` --- An optional label which overrides the label parsed from the controller
8449 // expression, if any.
8450 var instance, match, constructor, identifier;
8451 later = later === true;
8452 if (ident && isString(ident)) {
8456 if (isString(expression)) {
8457 match = expression.match(CNTRL_REG);
8459 throw $controllerMinErr('ctrlfmt',
8460 "Badly formed controller string '{0}'. " +
8461 "Must match `__name__ as __id__` or `__name__`.", expression);
8463 constructor = match[1],
8464 identifier = identifier || match[3];
8465 expression = controllers.hasOwnProperty(constructor)
8466 ? controllers[constructor]
8467 : getter(locals.$scope, constructor, true) ||
8468 (globals ? getter($window, constructor, true) : undefined);
8470 assertArgFn(expression, constructor, true);
8474 // Instantiate controller later:
8475 // This machinery is used to create an instance of the object before calling the
8476 // controller's constructor itself.
8478 // This allows properties to be added to the controller before the constructor is
8479 // invoked. Primarily, this is used for isolate scope bindings in $compile.
8481 // This feature is not intended for use by applications, and is thus not documented
8483 // Object creation: http://jsperf.com/create-constructor/2
8484 var controllerPrototype = (isArray(expression) ?
8485 expression[expression.length - 1] : expression).prototype;
8486 instance = Object.create(controllerPrototype || null);
8489 addIdentifier(locals, identifier, instance, constructor || expression.name);
8492 return extend(function() {
8493 $injector.invoke(expression, instance, locals, constructor);
8497 identifier: identifier
8501 instance = $injector.instantiate(expression, locals, constructor);
8504 addIdentifier(locals, identifier, instance, constructor || expression.name);
8510 function addIdentifier(locals, identifier, instance, name) {
8511 if (!(locals && isObject(locals.$scope))) {
8512 throw minErr('$controller')('noscp',
8513 "Cannot export controller '{0}' as '{1}'! No $scope object provided via `locals`.",
8517 locals.$scope[identifier] = instance;
8528 * A {@link angular.element jQuery or jqLite} wrapper for the browser's `window.document` object.
8531 <example module="documentExample">
8532 <file name="index.html">
8533 <div ng-controller="ExampleController">
8534 <p>$document title: <b ng-bind="title"></b></p>
8535 <p>window.document title: <b ng-bind="windowTitle"></b></p>
8538 <file name="script.js">
8539 angular.module('documentExample', [])
8540 .controller('ExampleController', ['$scope', '$document', function($scope, $document) {
8541 $scope.title = $document[0].title;
8542 $scope.windowTitle = angular.element(window.document)[0].title;
8547 function $DocumentProvider() {
8548 this.$get = ['$window', function(window) {
8549 return jqLite(window.document);
8555 * @name $exceptionHandler
8559 * Any uncaught exception in angular expressions is delegated to this service.
8560 * The default implementation simply delegates to `$log.error` which logs it into
8561 * the browser console.
8563 * In unit tests, if `angular-mocks.js` is loaded, this service is overridden by
8564 * {@link ngMock.$exceptionHandler mock $exceptionHandler} which aids in testing.
8569 * angular.module('exceptionOverride', []).factory('$exceptionHandler', function() {
8570 * return function(exception, cause) {
8571 * exception.message += ' (caused by "' + cause + '")';
8577 * This example will override the normal action of `$exceptionHandler`, to make angular
8578 * exceptions fail hard when they happen, instead of just logging to the console.
8581 * Note, that code executed in event-listeners (even those registered using jqLite's `on`/`bind`
8582 * methods) does not delegate exceptions to the {@link ng.$exceptionHandler $exceptionHandler}
8583 * (unless executed during a digest).
8585 * If you wish, you can manually delegate exceptions, e.g.
8586 * `try { ... } catch(e) { $exceptionHandler(e); }`
8588 * @param {Error} exception Exception associated with the error.
8589 * @param {string=} cause optional information about the context in which
8590 * the error was thrown.
8593 function $ExceptionHandlerProvider() {
8594 this.$get = ['$log', function($log) {
8595 return function(exception, cause) {
8596 $log.error.apply($log, arguments);
8601 var APPLICATION_JSON = 'application/json';
8602 var CONTENT_TYPE_APPLICATION_JSON = {'Content-Type': APPLICATION_JSON + ';charset=utf-8'};
8603 var JSON_START = /^\[|^\{(?!\{)/;
8608 var JSON_PROTECTION_PREFIX = /^\)\]\}',?\n/;
8610 function defaultHttpResponseTransform(data, headers) {
8611 if (isString(data)) {
8612 // Strip json vulnerability protection prefix and trim whitespace
8613 var tempData = data.replace(JSON_PROTECTION_PREFIX, '').trim();
8616 var contentType = headers('Content-Type');
8617 if ((contentType && (contentType.indexOf(APPLICATION_JSON) === 0)) || isJsonLike(tempData)) {
8618 data = fromJson(tempData);
8626 function isJsonLike(str) {
8627 var jsonStart = str.match(JSON_START);
8628 return jsonStart && JSON_ENDS[jsonStart[0]].test(str);
8632 * Parse headers into key value object
8634 * @param {string} headers Raw headers as a string
8635 * @returns {Object} Parsed headers as key value object
8637 function parseHeaders(headers) {
8638 var parsed = createMap(), key, val, i;
8640 if (!headers) return parsed;
8642 forEach(headers.split('\n'), function(line) {
8643 i = line.indexOf(':');
8644 key = lowercase(trim(line.substr(0, i)));
8645 val = trim(line.substr(i + 1));
8648 parsed[key] = parsed[key] ? parsed[key] + ', ' + val : val;
8657 * Returns a function that provides access to parsed headers.
8659 * Headers are lazy parsed when first requested.
8662 * @param {(string|Object)} headers Headers to provide access to.
8663 * @returns {function(string=)} Returns a getter function which if called with:
8665 * - if called with single an argument returns a single header value or null
8666 * - if called with no arguments returns an object containing all headers.
8668 function headersGetter(headers) {
8669 var headersObj = isObject(headers) ? headers : undefined;
8671 return function(name) {
8672 if (!headersObj) headersObj = parseHeaders(headers);
8675 var value = headersObj[lowercase(name)];
8676 if (value === void 0) {
8688 * Chain all given functions
8690 * This function is used for both request and response transforming
8692 * @param {*} data Data to transform.
8693 * @param {function(string=)} headers HTTP headers getter fn.
8694 * @param {number} status HTTP status code of the response.
8695 * @param {(Function|Array.<Function>)} fns Function or an array of functions.
8696 * @returns {*} Transformed data.
8698 function transformData(data, headers, status, fns) {
8699 if (isFunction(fns))
8700 return fns(data, headers, status);
8702 forEach(fns, function(fn) {
8703 data = fn(data, headers, status);
8710 function isSuccess(status) {
8711 return 200 <= status && status < 300;
8717 * @name $httpProvider
8719 * Use `$httpProvider` to change the default behavior of the {@link ng.$http $http} service.
8721 function $HttpProvider() {
8724 * @name $httpProvider#defaults
8727 * Object containing default values for all {@link ng.$http $http} requests.
8729 * - **`defaults.cache`** - {Object} - an object built with {@link ng.$cacheFactory `$cacheFactory`}
8730 * that will provide the cache for all requests who set their `cache` property to `true`.
8731 * If you set the `default.cache = false` then only requests that specify their own custom
8732 * cache object will be cached. See {@link $http#caching $http Caching} for more information.
8734 * - **`defaults.xsrfCookieName`** - {string} - Name of cookie containing the XSRF token.
8735 * Defaults value is `'XSRF-TOKEN'`.
8737 * - **`defaults.xsrfHeaderName`** - {string} - Name of HTTP header to populate with the
8738 * XSRF token. Defaults value is `'X-XSRF-TOKEN'`.
8740 * - **`defaults.headers`** - {Object} - Default headers for all $http requests.
8741 * Refer to {@link ng.$http#setting-http-headers $http} for documentation on
8742 * setting default headers.
8743 * - **`defaults.headers.common`**
8744 * - **`defaults.headers.post`**
8745 * - **`defaults.headers.put`**
8746 * - **`defaults.headers.patch`**
8749 var defaults = this.defaults = {
8750 // transform incoming response data
8751 transformResponse: [defaultHttpResponseTransform],
8753 // transform outgoing request data
8754 transformRequest: [function(d) {
8755 return isObject(d) && !isFile(d) && !isBlob(d) && !isFormData(d) ? toJson(d) : d;
8761 'Accept': 'application/json, text/plain, */*'
8763 post: shallowCopy(CONTENT_TYPE_APPLICATION_JSON),
8764 put: shallowCopy(CONTENT_TYPE_APPLICATION_JSON),
8765 patch: shallowCopy(CONTENT_TYPE_APPLICATION_JSON)
8768 xsrfCookieName: 'XSRF-TOKEN',
8769 xsrfHeaderName: 'X-XSRF-TOKEN'
8772 var useApplyAsync = false;
8775 * @name $httpProvider#useApplyAsync
8778 * Configure $http service to combine processing of multiple http responses received at around
8779 * the same time via {@link ng.$rootScope.Scope#$applyAsync $rootScope.$applyAsync}. This can result in
8780 * significant performance improvement for bigger applications that make many HTTP requests
8781 * concurrently (common during application bootstrap).
8783 * Defaults to false. If no value is specifed, returns the current configured value.
8785 * @param {boolean=} value If true, when requests are loaded, they will schedule a deferred
8786 * "apply" on the next tick, giving time for subsequent requests in a roughly ~10ms window
8787 * to load and share the same digest cycle.
8789 * @returns {boolean|Object} If a value is specified, returns the $httpProvider for chaining.
8790 * otherwise, returns the current configured value.
8792 this.useApplyAsync = function(value) {
8793 if (isDefined(value)) {
8794 useApplyAsync = !!value;
8797 return useApplyAsync;
8802 * @name $httpProvider#interceptors
8805 * Array containing service factories for all synchronous or asynchronous {@link ng.$http $http}
8806 * pre-processing of request or postprocessing of responses.
8808 * These service factories are ordered by request, i.e. they are applied in the same order as the
8809 * array, on request, but reverse order, on response.
8811 * {@link ng.$http#interceptors Interceptors detailed info}
8813 var interceptorFactories = this.interceptors = [];
8815 this.$get = ['$httpBackend', '$browser', '$cacheFactory', '$rootScope', '$q', '$injector',
8816 function($httpBackend, $browser, $cacheFactory, $rootScope, $q, $injector) {
8818 var defaultCache = $cacheFactory('$http');
8821 * Interceptors stored in reverse order. Inner interceptors before outer interceptors.
8822 * The reversal is needed so that we can build up the interception chain around the
8825 var reversedInterceptors = [];
8827 forEach(interceptorFactories, function(interceptorFactory) {
8828 reversedInterceptors.unshift(isString(interceptorFactory)
8829 ? $injector.get(interceptorFactory) : $injector.invoke(interceptorFactory));
8836 * @requires ng.$httpBackend
8837 * @requires $cacheFactory
8838 * @requires $rootScope
8840 * @requires $injector
8843 * The `$http` service is a core Angular service that facilitates communication with the remote
8844 * HTTP servers via the browser's [XMLHttpRequest](https://developer.mozilla.org/en/xmlhttprequest)
8845 * object or via [JSONP](http://en.wikipedia.org/wiki/JSONP).
8847 * For unit testing applications that use `$http` service, see
8848 * {@link ngMock.$httpBackend $httpBackend mock}.
8850 * For a higher level of abstraction, please check out the {@link ngResource.$resource
8851 * $resource} service.
8853 * The $http API is based on the {@link ng.$q deferred/promise APIs} exposed by
8854 * the $q service. While for simple usage patterns this doesn't matter much, for advanced usage
8855 * it is important to familiarize yourself with these APIs and the guarantees they provide.
8859 * The `$http` service is a function which takes a single argument — a configuration object —
8860 * that is used to generate an HTTP request and returns a {@link ng.$q promise}
8861 * with two $http specific methods: `success` and `error`.
8864 * // Simple GET request example :
8865 * $http.get('/someUrl').
8866 * success(function(data, status, headers, config) {
8867 * // this callback will be called asynchronously
8868 * // when the response is available
8870 * error(function(data, status, headers, config) {
8871 * // called asynchronously if an error occurs
8872 * // or server returns response with an error status.
8877 * // Simple POST request example (passing data) :
8878 * $http.post('/someUrl', {msg:'hello word!'}).
8879 * success(function(data, status, headers, config) {
8880 * // this callback will be called asynchronously
8881 * // when the response is available
8883 * error(function(data, status, headers, config) {
8884 * // called asynchronously if an error occurs
8885 * // or server returns response with an error status.
8890 * Since the returned value of calling the $http function is a `promise`, you can also use
8891 * the `then` method to register callbacks, and these callbacks will receive a single argument –
8892 * an object representing the response. See the API signature and type info below for more
8895 * A response status code between 200 and 299 is considered a success status and
8896 * will result in the success callback being called. Note that if the response is a redirect,
8897 * XMLHttpRequest will transparently follow it, meaning that the error callback will not be
8898 * called for such responses.
8900 * ## Writing Unit Tests that use $http
8901 * When unit testing (using {@link ngMock ngMock}), it is necessary to call
8902 * {@link ngMock.$httpBackend#flush $httpBackend.flush()} to flush each pending
8903 * request using trained responses.
8906 * $httpBackend.expectGET(...);
8908 * $httpBackend.flush();
8911 * ## Shortcut methods
8913 * Shortcut methods are also available. All shortcut methods require passing in the URL, and
8914 * request data must be passed in for POST/PUT requests.
8917 * $http.get('/someUrl').success(successCallback);
8918 * $http.post('/someUrl', data).success(successCallback);
8921 * Complete list of shortcut methods:
8923 * - {@link ng.$http#get $http.get}
8924 * - {@link ng.$http#head $http.head}
8925 * - {@link ng.$http#post $http.post}
8926 * - {@link ng.$http#put $http.put}
8927 * - {@link ng.$http#delete $http.delete}
8928 * - {@link ng.$http#jsonp $http.jsonp}
8929 * - {@link ng.$http#patch $http.patch}
8932 * ## Setting HTTP Headers
8934 * The $http service will automatically add certain HTTP headers to all requests. These defaults
8935 * can be fully configured by accessing the `$httpProvider.defaults.headers` configuration
8936 * object, which currently contains this default configuration:
8938 * - `$httpProvider.defaults.headers.common` (headers that are common for all requests):
8939 * - `Accept: application/json, text/plain, * / *`
8940 * - `$httpProvider.defaults.headers.post`: (header defaults for POST requests)
8941 * - `Content-Type: application/json`
8942 * - `$httpProvider.defaults.headers.put` (header defaults for PUT requests)
8943 * - `Content-Type: application/json`
8945 * To add or overwrite these defaults, simply add or remove a property from these configuration
8946 * objects. To add headers for an HTTP method other than POST or PUT, simply add a new object
8947 * with the lowercased HTTP method name as the key, e.g.
8948 * `$httpProvider.defaults.headers.get = { 'My-Header' : 'value' }.
8950 * The defaults can also be set at runtime via the `$http.defaults` object in the same
8951 * fashion. For example:
8954 * module.run(function($http) {
8955 * $http.defaults.headers.common.Authorization = 'Basic YmVlcDpib29w'
8959 * In addition, you can supply a `headers` property in the config object passed when
8960 * calling `$http(config)`, which overrides the defaults without changing them globally.
8962 * To explicitly remove a header automatically added via $httpProvider.defaults.headers on a per request basis,
8963 * Use the `headers` property, setting the desired header to `undefined`. For example:
8968 * url: 'http://example.com',
8970 * 'Content-Type': undefined
8972 * data: { test: 'test' },
8975 * $http(req).success(function(){...}).error(function(){...});
8978 * ## Transforming Requests and Responses
8980 * Both requests and responses can be transformed using transformation functions: `transformRequest`
8981 * and `transformResponse`. These properties can be a single function that returns
8982 * the transformed value (`function(data, headersGetter, status)`) or an array of such transformation functions,
8983 * which allows you to `push` or `unshift` a new transformation function into the transformation chain.
8985 * ### Default Transformations
8987 * The `$httpProvider` provider and `$http` service expose `defaults.transformRequest` and
8988 * `defaults.transformResponse` properties. If a request does not provide its own transformations
8989 * then these will be applied.
8991 * You can augment or replace the default transformations by modifying these properties by adding to or
8992 * replacing the array.
8994 * Angular provides the following default transformations:
8996 * Request transformations (`$httpProvider.defaults.transformRequest` and `$http.defaults.transformRequest`):
8998 * - If the `data` property of the request configuration object contains an object, serialize it
9001 * Response transformations (`$httpProvider.defaults.transformResponse` and `$http.defaults.transformResponse`):
9003 * - If XSRF prefix is detected, strip it (see Security Considerations section below).
9004 * - If JSON response is detected, deserialize it using a JSON parser.
9007 * ### Overriding the Default Transformations Per Request
9009 * If you wish override the request/response transformations only for a single request then provide
9010 * `transformRequest` and/or `transformResponse` properties on the configuration object passed
9013 * Note that if you provide these properties on the config object the default transformations will be
9014 * overwritten. If you wish to augment the default transformations then you must include them in your
9015 * local transformation array.
9017 * The following code demonstrates adding a new response transformation to be run after the default response
9018 * transformations have been run.
9021 * function appendTransform(defaults, transform) {
9023 * // We can't guarantee that the default transformation is an array
9024 * defaults = angular.isArray(defaults) ? defaults : [defaults];
9026 * // Append the new transformation to the defaults
9027 * return defaults.concat(transform);
9033 * transformResponse: appendTransform($http.defaults.transformResponse, function(value) {
9034 * return doTransform(value);
9042 * To enable caching, set the request configuration `cache` property to `true` (to use default
9043 * cache) or to a custom cache object (built with {@link ng.$cacheFactory `$cacheFactory`}).
9044 * When the cache is enabled, `$http` stores the response from the server in the specified
9045 * cache. The next time the same request is made, the response is served from the cache without
9046 * sending a request to the server.
9048 * Note that even if the response is served from cache, delivery of the data is asynchronous in
9049 * the same way that real requests are.
9051 * If there are multiple GET requests for the same URL that should be cached using the same
9052 * cache, but the cache is not populated yet, only one request to the server will be made and
9053 * the remaining requests will be fulfilled using the response from the first request.
9055 * You can change the default cache to a new object (built with
9056 * {@link ng.$cacheFactory `$cacheFactory`}) by updating the
9057 * {@link ng.$http#defaults `$http.defaults.cache`} property. All requests who set
9058 * their `cache` property to `true` will now use this cache object.
9060 * If you set the default cache to `false` then only requests that specify their own custom
9061 * cache object will be cached.
9065 * Before you start creating interceptors, be sure to understand the
9066 * {@link ng.$q $q and deferred/promise APIs}.
9068 * For purposes of global error handling, authentication, or any kind of synchronous or
9069 * asynchronous pre-processing of request or postprocessing of responses, it is desirable to be
9070 * able to intercept requests before they are handed to the server and
9071 * responses before they are handed over to the application code that
9072 * initiated these requests. The interceptors leverage the {@link ng.$q
9073 * promise APIs} to fulfill this need for both synchronous and asynchronous pre-processing.
9075 * The interceptors are service factories that are registered with the `$httpProvider` by
9076 * adding them to the `$httpProvider.interceptors` array. The factory is called and
9077 * injected with dependencies (if specified) and returns the interceptor.
9079 * There are two kinds of interceptors (and two kinds of rejection interceptors):
9081 * * `request`: interceptors get called with a http `config` object. The function is free to
9082 * modify the `config` object or create a new one. The function needs to return the `config`
9083 * object directly, or a promise containing the `config` or a new `config` object.
9084 * * `requestError`: interceptor gets called when a previous interceptor threw an error or
9085 * resolved with a rejection.
9086 * * `response`: interceptors get called with http `response` object. The function is free to
9087 * modify the `response` object or create a new one. The function needs to return the `response`
9088 * object directly, or as a promise containing the `response` or a new `response` object.
9089 * * `responseError`: interceptor gets called when a previous interceptor threw an error or
9090 * resolved with a rejection.
9094 * // register the interceptor as a service
9095 * $provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) {
9097 * // optional method
9098 * 'request': function(config) {
9099 * // do something on success
9103 * // optional method
9104 * 'requestError': function(rejection) {
9105 * // do something on error
9106 * if (canRecover(rejection)) {
9107 * return responseOrNewPromise
9109 * return $q.reject(rejection);
9114 * // optional method
9115 * 'response': function(response) {
9116 * // do something on success
9120 * // optional method
9121 * 'responseError': function(rejection) {
9122 * // do something on error
9123 * if (canRecover(rejection)) {
9124 * return responseOrNewPromise
9126 * return $q.reject(rejection);
9131 * $httpProvider.interceptors.push('myHttpInterceptor');
9134 * // alternatively, register the interceptor via an anonymous factory
9135 * $httpProvider.interceptors.push(function($q, dependency1, dependency2) {
9137 * 'request': function(config) {
9141 * 'response': function(response) {
9148 * ## Security Considerations
9150 * When designing web applications, consider security threats from:
9152 * - [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx)
9153 * - [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery)
9155 * Both server and the client must cooperate in order to eliminate these threats. Angular comes
9156 * pre-configured with strategies that address these issues, but for this to work backend server
9157 * cooperation is required.
9159 * ### JSON Vulnerability Protection
9161 * A [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx)
9162 * allows third party website to turn your JSON resource URL into
9163 * [JSONP](http://en.wikipedia.org/wiki/JSONP) request under some conditions. To
9164 * counter this your server can prefix all JSON requests with following string `")]}',\n"`.
9165 * Angular will automatically strip the prefix before processing it as JSON.
9167 * For example if your server needs to return:
9172 * which is vulnerable to attack, your server can return:
9178 * Angular will strip the prefix, before processing the JSON.
9181 * ### Cross Site Request Forgery (XSRF) Protection
9183 * [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery) is a technique by which
9184 * an unauthorized site can gain your user's private data. Angular provides a mechanism
9185 * to counter XSRF. When performing XHR requests, the $http service reads a token from a cookie
9186 * (by default, `XSRF-TOKEN`) and sets it as an HTTP header (`X-XSRF-TOKEN`). Since only
9187 * JavaScript that runs on your domain could read the cookie, your server can be assured that
9188 * the XHR came from JavaScript running on your domain. The header will not be set for
9189 * cross-domain requests.
9191 * To take advantage of this, your server needs to set a token in a JavaScript readable session
9192 * cookie called `XSRF-TOKEN` on the first HTTP GET request. On subsequent XHR requests the
9193 * server can verify that the cookie matches `X-XSRF-TOKEN` HTTP header, and therefore be sure
9194 * that only JavaScript running on your domain could have sent the request. The token must be
9195 * unique for each user and must be verifiable by the server (to prevent the JavaScript from
9196 * making up its own tokens). We recommend that the token is a digest of your site's
9197 * authentication cookie with a [salt](https://en.wikipedia.org/wiki/Salt_(cryptography))
9198 * for added security.
9200 * The name of the headers can be specified using the xsrfHeaderName and xsrfCookieName
9201 * properties of either $httpProvider.defaults at config-time, $http.defaults at run-time,
9202 * or the per-request config object.
9205 * @param {object} config Object describing the request to be made and how it should be
9206 * processed. The object has following properties:
9208 * - **method** – `{string}` – HTTP method (e.g. 'GET', 'POST', etc)
9209 * - **url** – `{string}` – Absolute or relative URL of the resource that is being requested.
9210 * - **params** – `{Object.<string|Object>}` – Map of strings or objects which will be turned
9211 * to `?key1=value1&key2=value2` after the url. If the value is not a string, it will be
9213 * - **data** – `{string|Object}` – Data to be sent as the request message data.
9214 * - **headers** – `{Object}` – Map of strings or functions which return strings representing
9215 * HTTP headers to send to the server. If the return value of a function is null, the
9216 * header will not be sent.
9217 * - **xsrfHeaderName** – `{string}` – Name of HTTP header to populate with the XSRF token.
9218 * - **xsrfCookieName** – `{string}` – Name of cookie containing the XSRF token.
9219 * - **transformRequest** –
9220 * `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` –
9221 * transform function or an array of such functions. The transform function takes the http
9222 * request body and headers and returns its transformed (typically serialized) version.
9223 * See {@link ng.$http#overriding-the-default-transformations-per-request
9224 * Overriding the Default Transformations}
9225 * - **transformResponse** –
9226 * `{function(data, headersGetter, status)|Array.<function(data, headersGetter, status)>}` –
9227 * transform function or an array of such functions. The transform function takes the http
9228 * response body, headers and status and returns its transformed (typically deserialized) version.
9229 * See {@link ng.$http#overriding-the-default-transformations-per-request
9230 * Overriding the Default Transformations}
9231 * - **cache** – `{boolean|Cache}` – If true, a default $http cache will be used to cache the
9232 * GET request, otherwise if a cache instance built with
9233 * {@link ng.$cacheFactory $cacheFactory}, this cache will be used for
9235 * - **timeout** – `{number|Promise}` – timeout in milliseconds, or {@link ng.$q promise}
9236 * that should abort the request when resolved.
9237 * - **withCredentials** - `{boolean}` - whether to set the `withCredentials` flag on the
9238 * XHR object. See [requests with credentials](https://developer.mozilla.org/docs/Web/HTTP/Access_control_CORS#Requests_with_credentials)
9239 * for more information.
9240 * - **responseType** - `{string}` - see
9241 * [requestType](https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest#responseType).
9243 * @returns {HttpPromise} Returns a {@link ng.$q promise} object with the
9244 * standard `then` method and two http specific methods: `success` and `error`. The `then`
9245 * method takes two arguments a success and an error callback which will be called with a
9246 * response object. The `success` and `error` methods take a single argument - a function that
9247 * will be called when the request succeeds or fails respectively. The arguments passed into
9248 * these functions are destructured representation of the response object passed into the
9249 * `then` method. The response object has these properties:
9251 * - **data** – `{string|Object}` – The response body transformed with the transform
9253 * - **status** – `{number}` – HTTP status code of the response.
9254 * - **headers** – `{function([headerName])}` – Header getter function.
9255 * - **config** – `{Object}` – The configuration object that was used to generate the request.
9256 * - **statusText** – `{string}` – HTTP status text of the response.
9258 * @property {Array.<Object>} pendingRequests Array of config objects for currently pending
9259 * requests. This is primarily meant to be used for debugging purposes.
9263 <example module="httpExample">
9264 <file name="index.html">
9265 <div ng-controller="FetchController">
9266 <select ng-model="method">
9267 <option>GET</option>
9268 <option>JSONP</option>
9270 <input type="text" ng-model="url" size="80"/>
9271 <button id="fetchbtn" ng-click="fetch()">fetch</button><br>
9272 <button id="samplegetbtn" ng-click="updateModel('GET', 'http-hello.html')">Sample GET</button>
9273 <button id="samplejsonpbtn"
9274 ng-click="updateModel('JSONP',
9275 'https://angularjs.org/greet.php?callback=JSON_CALLBACK&name=Super%20Hero')">
9278 <button id="invalidjsonpbtn"
9279 ng-click="updateModel('JSONP', 'https://angularjs.org/doesntexist&callback=JSON_CALLBACK')">
9282 <pre>http status code: {{status}}</pre>
9283 <pre>http response data: {{data}}</pre>
9286 <file name="script.js">
9287 angular.module('httpExample', [])
9288 .controller('FetchController', ['$scope', '$http', '$templateCache',
9289 function($scope, $http, $templateCache) {
9290 $scope.method = 'GET';
9291 $scope.url = 'http-hello.html';
9293 $scope.fetch = function() {
9295 $scope.response = null;
9297 $http({method: $scope.method, url: $scope.url, cache: $templateCache}).
9298 success(function(data, status) {
9299 $scope.status = status;
9302 error(function(data, status) {
9303 $scope.data = data || "Request failed";
9304 $scope.status = status;
9308 $scope.updateModel = function(method, url) {
9309 $scope.method = method;
9314 <file name="http-hello.html">
9317 <file name="protractor.js" type="protractor">
9318 var status = element(by.binding('status'));
9319 var data = element(by.binding('data'));
9320 var fetchBtn = element(by.id('fetchbtn'));
9321 var sampleGetBtn = element(by.id('samplegetbtn'));
9322 var sampleJsonpBtn = element(by.id('samplejsonpbtn'));
9323 var invalidJsonpBtn = element(by.id('invalidjsonpbtn'));
9325 it('should make an xhr GET request', function() {
9326 sampleGetBtn.click();
9328 expect(status.getText()).toMatch('200');
9329 expect(data.getText()).toMatch(/Hello, \$http!/);
9332 // Commented out due to flakes. See https://github.com/angular/angular.js/issues/9185
9333 // it('should make a JSONP request to angularjs.org', function() {
9334 // sampleJsonpBtn.click();
9335 // fetchBtn.click();
9336 // expect(status.getText()).toMatch('200');
9337 // expect(data.getText()).toMatch(/Super Hero!/);
9340 it('should make JSONP request to invalid URL and invoke the error handler',
9342 invalidJsonpBtn.click();
9344 expect(status.getText()).toMatch('0');
9345 expect(data.getText()).toMatch('Request failed');
9350 function $http(requestConfig) {
9352 if (!angular.isObject(requestConfig)) {
9353 throw minErr('$http')('badreq', 'Http request configuration must be an object. Received: {0}', requestConfig);
9356 var config = extend({
9358 transformRequest: defaults.transformRequest,
9359 transformResponse: defaults.transformResponse
9362 config.headers = mergeHeaders(requestConfig);
9363 config.method = uppercase(config.method);
9365 var serverRequest = function(config) {
9366 var headers = config.headers;
9367 var reqData = transformData(config.data, headersGetter(headers), undefined, config.transformRequest);
9369 // strip content-type if data is undefined
9370 if (isUndefined(reqData)) {
9371 forEach(headers, function(value, header) {
9372 if (lowercase(header) === 'content-type') {
9373 delete headers[header];
9378 if (isUndefined(config.withCredentials) && !isUndefined(defaults.withCredentials)) {
9379 config.withCredentials = defaults.withCredentials;
9383 return sendReq(config, reqData).then(transformResponse, transformResponse);
9386 var chain = [serverRequest, undefined];
9387 var promise = $q.when(config);
9389 // apply interceptors
9390 forEach(reversedInterceptors, function(interceptor) {
9391 if (interceptor.request || interceptor.requestError) {
9392 chain.unshift(interceptor.request, interceptor.requestError);
9394 if (interceptor.response || interceptor.responseError) {
9395 chain.push(interceptor.response, interceptor.responseError);
9399 while (chain.length) {
9400 var thenFn = chain.shift();
9401 var rejectFn = chain.shift();
9403 promise = promise.then(thenFn, rejectFn);
9406 promise.success = function(fn) {
9407 promise.then(function(response) {
9408 fn(response.data, response.status, response.headers, config);
9413 promise.error = function(fn) {
9414 promise.then(null, function(response) {
9415 fn(response.data, response.status, response.headers, config);
9422 function transformResponse(response) {
9423 // make a copy since the response must be cacheable
9424 var resp = extend({}, response);
9425 if (!response.data) {
9426 resp.data = response.data;
9428 resp.data = transformData(response.data, response.headers, response.status, config.transformResponse);
9430 return (isSuccess(response.status))
9435 function executeHeaderFns(headers) {
9436 var headerContent, processedHeaders = {};
9438 forEach(headers, function(headerFn, header) {
9439 if (isFunction(headerFn)) {
9440 headerContent = headerFn();
9441 if (headerContent != null) {
9442 processedHeaders[header] = headerContent;
9445 processedHeaders[header] = headerFn;
9449 return processedHeaders;
9452 function mergeHeaders(config) {
9453 var defHeaders = defaults.headers,
9454 reqHeaders = extend({}, config.headers),
9455 defHeaderName, lowercaseDefHeaderName, reqHeaderName;
9457 defHeaders = extend({}, defHeaders.common, defHeaders[lowercase(config.method)]);
9459 // using for-in instead of forEach to avoid unecessary iteration after header has been found
9460 defaultHeadersIteration:
9461 for (defHeaderName in defHeaders) {
9462 lowercaseDefHeaderName = lowercase(defHeaderName);
9464 for (reqHeaderName in reqHeaders) {
9465 if (lowercase(reqHeaderName) === lowercaseDefHeaderName) {
9466 continue defaultHeadersIteration;
9470 reqHeaders[defHeaderName] = defHeaders[defHeaderName];
9473 // execute if header value is a function for merged headers
9474 return executeHeaderFns(reqHeaders);
9478 $http.pendingRequests = [];
9485 * Shortcut method to perform `GET` request.
9487 * @param {string} url Relative or absolute URL specifying the destination of the request
9488 * @param {Object=} config Optional configuration object
9489 * @returns {HttpPromise} Future object
9494 * @name $http#delete
9497 * Shortcut method to perform `DELETE` request.
9499 * @param {string} url Relative or absolute URL specifying the destination of the request
9500 * @param {Object=} config Optional configuration object
9501 * @returns {HttpPromise} Future object
9509 * Shortcut method to perform `HEAD` request.
9511 * @param {string} url Relative or absolute URL specifying the destination of the request
9512 * @param {Object=} config Optional configuration object
9513 * @returns {HttpPromise} Future object
9521 * Shortcut method to perform `JSONP` request.
9523 * @param {string} url Relative or absolute URL specifying the destination of the request.
9524 * The name of the callback should be the string `JSON_CALLBACK`.
9525 * @param {Object=} config Optional configuration object
9526 * @returns {HttpPromise} Future object
9528 createShortMethods('get', 'delete', 'head', 'jsonp');
9535 * Shortcut method to perform `POST` request.
9537 * @param {string} url Relative or absolute URL specifying the destination of the request
9538 * @param {*} data Request content
9539 * @param {Object=} config Optional configuration object
9540 * @returns {HttpPromise} Future object
9548 * Shortcut method to perform `PUT` request.
9550 * @param {string} url Relative or absolute URL specifying the destination of the request
9551 * @param {*} data Request content
9552 * @param {Object=} config Optional configuration object
9553 * @returns {HttpPromise} Future object
9561 * Shortcut method to perform `PATCH` request.
9563 * @param {string} url Relative or absolute URL specifying the destination of the request
9564 * @param {*} data Request content
9565 * @param {Object=} config Optional configuration object
9566 * @returns {HttpPromise} Future object
9568 createShortMethodsWithData('post', 'put', 'patch');
9572 * @name $http#defaults
9575 * Runtime equivalent of the `$httpProvider.defaults` property. Allows configuration of
9576 * default headers, withCredentials as well as request and response transformations.
9578 * See "Setting HTTP Headers" and "Transforming Requests and Responses" sections above.
9580 $http.defaults = defaults;
9586 function createShortMethods(names) {
9587 forEach(arguments, function(name) {
9588 $http[name] = function(url, config) {
9589 return $http(extend(config || {}, {
9598 function createShortMethodsWithData(name) {
9599 forEach(arguments, function(name) {
9600 $http[name] = function(url, data, config) {
9601 return $http(extend(config || {}, {
9612 * Makes the request.
9614 * !!! ACCESSES CLOSURE VARS:
9615 * $httpBackend, defaults, $log, $rootScope, defaultCache, $http.pendingRequests
9617 function sendReq(config, reqData) {
9618 var deferred = $q.defer(),
9619 promise = deferred.promise,
9622 reqHeaders = config.headers,
9623 url = buildUrl(config.url, config.params);
9625 $http.pendingRequests.push(config);
9626 promise.then(removePendingReq, removePendingReq);
9629 if ((config.cache || defaults.cache) && config.cache !== false &&
9630 (config.method === 'GET' || config.method === 'JSONP')) {
9631 cache = isObject(config.cache) ? config.cache
9632 : isObject(defaults.cache) ? defaults.cache
9637 cachedResp = cache.get(url);
9638 if (isDefined(cachedResp)) {
9639 if (isPromiseLike(cachedResp)) {
9640 // cached request has already been sent, but there is no response yet
9641 cachedResp.then(resolvePromiseWithResult, resolvePromiseWithResult);
9643 // serving from cache
9644 if (isArray(cachedResp)) {
9645 resolvePromise(cachedResp[1], cachedResp[0], shallowCopy(cachedResp[2]), cachedResp[3]);
9647 resolvePromise(cachedResp, 200, {}, 'OK');
9651 // put the promise for the non-transformed response into cache as a placeholder
9652 cache.put(url, promise);
9657 // if we won't have the response in cache, set the xsrf headers and
9658 // send the request to the backend
9659 if (isUndefined(cachedResp)) {
9660 var xsrfValue = urlIsSameOrigin(config.url)
9661 ? $browser.cookies()[config.xsrfCookieName || defaults.xsrfCookieName]
9664 reqHeaders[(config.xsrfHeaderName || defaults.xsrfHeaderName)] = xsrfValue;
9667 $httpBackend(config.method, url, reqData, done, reqHeaders, config.timeout,
9668 config.withCredentials, config.responseType);
9675 * Callback registered to $httpBackend():
9676 * - caches the response if desired
9677 * - resolves the raw $http promise
9680 function done(status, response, headersString, statusText) {
9682 if (isSuccess(status)) {
9683 cache.put(url, [status, response, parseHeaders(headersString), statusText]);
9685 // remove promise from the cache
9690 function resolveHttpPromise() {
9691 resolvePromise(response, status, headersString, statusText);
9694 if (useApplyAsync) {
9695 $rootScope.$applyAsync(resolveHttpPromise);
9697 resolveHttpPromise();
9698 if (!$rootScope.$$phase) $rootScope.$apply();
9704 * Resolves the raw $http promise.
9706 function resolvePromise(response, status, headers, statusText) {
9707 // normalize internal statuses to 0
9708 status = Math.max(status, 0);
9710 (isSuccess(status) ? deferred.resolve : deferred.reject)({
9713 headers: headersGetter(headers),
9715 statusText: statusText
9719 function resolvePromiseWithResult(result) {
9720 resolvePromise(result.data, result.status, shallowCopy(result.headers()), result.statusText);
9723 function removePendingReq() {
9724 var idx = $http.pendingRequests.indexOf(config);
9725 if (idx !== -1) $http.pendingRequests.splice(idx, 1);
9730 function buildUrl(url, params) {
9731 if (!params) return url;
9733 forEachSorted(params, function(value, key) {
9734 if (value === null || isUndefined(value)) return;
9735 if (!isArray(value)) value = [value];
9737 forEach(value, function(v) {
9740 v = v.toISOString();
9745 parts.push(encodeUriQuery(key) + '=' +
9749 if (parts.length > 0) {
9750 url += ((url.indexOf('?') == -1) ? '?' : '&') + parts.join('&');
9757 function createXhr() {
9758 return new window.XMLHttpRequest();
9763 * @name $httpBackend
9765 * @requires $document
9768 * HTTP backend used by the {@link ng.$http service} that delegates to
9769 * XMLHttpRequest object or JSONP and deals with browser incompatibilities.
9771 * You should never need to use this service directly, instead use the higher-level abstractions:
9772 * {@link ng.$http $http} or {@link ngResource.$resource $resource}.
9774 * During testing this implementation is swapped with {@link ngMock.$httpBackend mock
9775 * $httpBackend} which can be trained with responses.
9777 function $HttpBackendProvider() {
9778 this.$get = ['$browser', '$window', '$document', function($browser, $window, $document) {
9779 return createHttpBackend($browser, createXhr, $browser.defer, $window.angular.callbacks, $document[0]);
9783 function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDocument) {
9784 // TODO(vojta): fix the signature
9785 return function(method, url, post, callback, headers, timeout, withCredentials, responseType) {
9786 $browser.$$incOutstandingRequestCount();
9787 url = url || $browser.url();
9789 if (lowercase(method) == 'jsonp') {
9790 var callbackId = '_' + (callbacks.counter++).toString(36);
9791 callbacks[callbackId] = function(data) {
9792 callbacks[callbackId].data = data;
9793 callbacks[callbackId].called = true;
9796 var jsonpDone = jsonpReq(url.replace('JSON_CALLBACK', 'angular.callbacks.' + callbackId),
9797 callbackId, function(status, text) {
9798 completeRequest(callback, status, callbacks[callbackId].data, "", text);
9799 callbacks[callbackId] = noop;
9803 var xhr = createXhr();
9805 xhr.open(method, url, true);
9806 forEach(headers, function(value, key) {
9807 if (isDefined(value)) {
9808 xhr.setRequestHeader(key, value);
9812 xhr.onload = function requestLoaded() {
9813 var statusText = xhr.statusText || '';
9815 // responseText is the old-school way of retrieving response (supported by IE8 & 9)
9816 // response/responseType properties were introduced in XHR Level2 spec (supported by IE10)
9817 var response = ('response' in xhr) ? xhr.response : xhr.responseText;
9819 // normalize IE9 bug (http://bugs.jquery.com/ticket/1450)
9820 var status = xhr.status === 1223 ? 204 : xhr.status;
9822 // fix status code when it is 0 (0 status is undocumented).
9823 // Occurs when accessing file resources or on Android 4.1 stock browser
9824 // while retrieving files from application cache.
9826 status = response ? 200 : urlResolve(url).protocol == 'file' ? 404 : 0;
9829 completeRequest(callback,
9832 xhr.getAllResponseHeaders(),
9836 var requestError = function() {
9837 // The response is always empty
9838 // See https://xhr.spec.whatwg.org/#request-error-steps and https://fetch.spec.whatwg.org/#concept-network-error
9839 completeRequest(callback, -1, null, null, '');
9842 xhr.onerror = requestError;
9843 xhr.onabort = requestError;
9845 if (withCredentials) {
9846 xhr.withCredentials = true;
9851 xhr.responseType = responseType;
9853 // WebKit added support for the json responseType value on 09/03/2013
9854 // https://bugs.webkit.org/show_bug.cgi?id=73648. Versions of Safari prior to 7 are
9855 // known to throw when setting the value "json" as the response type. Other older
9856 // browsers implementing the responseType
9858 // The json response type can be ignored if not supported, because JSON payloads are
9859 // parsed on the client-side regardless.
9860 if (responseType !== 'json') {
9866 xhr.send(post || null);
9870 var timeoutId = $browserDefer(timeoutRequest, timeout);
9871 } else if (isPromiseLike(timeout)) {
9872 timeout.then(timeoutRequest);
9876 function timeoutRequest() {
9877 jsonpDone && jsonpDone();
9881 function completeRequest(callback, status, response, headersString, statusText) {
9882 // cancel timeout and subsequent timeout promise resolution
9883 if (timeoutId !== undefined) {
9884 $browserDefer.cancel(timeoutId);
9886 jsonpDone = xhr = null;
9888 callback(status, response, headersString, statusText);
9889 $browser.$$completeOutstandingRequest(noop);
9893 function jsonpReq(url, callbackId, done) {
9894 // we can't use jQuery/jqLite here because jQuery does crazy shit with script elements, e.g.:
9895 // - fetches local scripts via XHR and evals them
9896 // - adds and immediately removes script elements from the document
9897 var script = rawDocument.createElement('script'), callback = null;
9898 script.type = "text/javascript";
9900 script.async = true;
9902 callback = function(event) {
9903 removeEventListenerFn(script, "load", callback);
9904 removeEventListenerFn(script, "error", callback);
9905 rawDocument.body.removeChild(script);
9908 var text = "unknown";
9911 if (event.type === "load" && !callbacks[callbackId].called) {
9912 event = { type: "error" };
9915 status = event.type === "error" ? 404 : 200;
9923 addEventListenerFn(script, "load", callback);
9924 addEventListenerFn(script, "error", callback);
9925 rawDocument.body.appendChild(script);
9930 var $interpolateMinErr = minErr('$interpolate');
9934 * @name $interpolateProvider
9938 * Used for configuring the interpolation markup. Defaults to `{{` and `}}`.
9941 <example module="customInterpolationApp">
9942 <file name="index.html">
9944 var customInterpolationApp = angular.module('customInterpolationApp', []);
9946 customInterpolationApp.config(function($interpolateProvider) {
9947 $interpolateProvider.startSymbol('//');
9948 $interpolateProvider.endSymbol('//');
9952 customInterpolationApp.controller('DemoController', function() {
9953 this.label = "This binding is brought you by // interpolation symbols.";
9956 <div ng-app="App" ng-controller="DemoController as demo">
9960 <file name="protractor.js" type="protractor">
9961 it('should interpolate binding with custom symbols', function() {
9962 expect(element(by.binding('demo.label')).getText()).toBe('This binding is brought you by // interpolation symbols.');
9967 function $InterpolateProvider() {
9968 var startSymbol = '{{';
9969 var endSymbol = '}}';
9973 * @name $interpolateProvider#startSymbol
9975 * Symbol to denote start of expression in the interpolated string. Defaults to `{{`.
9977 * @param {string=} value new value to set the starting symbol to.
9978 * @returns {string|self} Returns the symbol when used as getter and self if used as setter.
9980 this.startSymbol = function(value) {
9982 startSymbol = value;
9991 * @name $interpolateProvider#endSymbol
9993 * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
9995 * @param {string=} value new value to set the ending symbol to.
9996 * @returns {string|self} Returns the symbol when used as getter and self if used as setter.
9998 this.endSymbol = function(value) {
10008 this.$get = ['$parse', '$exceptionHandler', '$sce', function($parse, $exceptionHandler, $sce) {
10009 var startSymbolLength = startSymbol.length,
10010 endSymbolLength = endSymbol.length,
10011 escapedStartRegexp = new RegExp(startSymbol.replace(/./g, escape), 'g'),
10012 escapedEndRegexp = new RegExp(endSymbol.replace(/./g, escape), 'g');
10014 function escape(ch) {
10015 return '\\\\\\' + ch;
10020 * @name $interpolate
10028 * Compiles a string with markup into an interpolation function. This service is used by the
10029 * HTML {@link ng.$compile $compile} service for data binding. See
10030 * {@link ng.$interpolateProvider $interpolateProvider} for configuring the
10031 * interpolation markup.
10035 * var $interpolate = ...; // injected
10036 * var exp = $interpolate('Hello {{name | uppercase}}!');
10037 * expect(exp({name:'Angular'}).toEqual('Hello ANGULAR!');
10040 * `$interpolate` takes an optional fourth argument, `allOrNothing`. If `allOrNothing` is
10041 * `true`, the interpolation function will return `undefined` unless all embedded expressions
10042 * evaluate to a value other than `undefined`.
10045 * var $interpolate = ...; // injected
10046 * var context = {greeting: 'Hello', name: undefined };
10048 * // default "forgiving" mode
10049 * var exp = $interpolate('{{greeting}} {{name}}!');
10050 * expect(exp(context)).toEqual('Hello !');
10052 * // "allOrNothing" mode
10053 * exp = $interpolate('{{greeting}} {{name}}!', false, null, true);
10054 * expect(exp(context)).toBeUndefined();
10055 * context.name = 'Angular';
10056 * expect(exp(context)).toEqual('Hello Angular!');
10059 * `allOrNothing` is useful for interpolating URLs. `ngSrc` and `ngSrcset` use this behavior.
10061 * ####Escaped Interpolation
10062 * $interpolate provides a mechanism for escaping interpolation markers. Start and end markers
10063 * can be escaped by preceding each of their characters with a REVERSE SOLIDUS U+005C (backslash).
10064 * It will be rendered as a regular start/end marker, and will not be interpreted as an expression
10067 * This enables web-servers to prevent script injection attacks and defacing attacks, to some
10068 * degree, while also enabling code examples to work without relying on the
10069 * {@link ng.directive:ngNonBindable ngNonBindable} directive.
10071 * **For security purposes, it is strongly encouraged that web servers escape user-supplied data,
10072 * replacing angle brackets (<, >) with &lt; and &gt; respectively, and replacing all
10073 * interpolation start/end markers with their escaped counterparts.**
10075 * Escaped interpolation markers are only replaced with the actual interpolation markers in rendered
10076 * output when the $interpolate service processes the text. So, for HTML elements interpolated
10077 * by {@link ng.$compile $compile}, or otherwise interpolated with the `mustHaveExpression` parameter
10078 * set to `true`, the interpolated text must contain an unescaped interpolation expression. As such,
10079 * this is typically useful only when user-data is used in rendering a template from the server, or
10080 * when otherwise untrusted data is used by a directive.
10083 * <file name="index.html">
10084 * <div ng-init="username='A user'">
10085 * <p ng-init="apptitle='Escaping demo'">{{apptitle}}: \{\{ username = "defaced value"; \}\}
10087 * <p><strong>{{username}}</strong> attempts to inject code which will deface the
10088 * application, but fails to accomplish their task, because the server has correctly
10089 * escaped the interpolation start/end markers with REVERSE SOLIDUS U+005C (backslash)
10091 * <p>Instead, the result of the attempted script injection is visible, and can be removed
10092 * from the database by an administrator.</p>
10097 * @param {string} text The text with markup to interpolate.
10098 * @param {boolean=} mustHaveExpression if set to true then the interpolation string must have
10099 * embedded expression in order to return an interpolation function. Strings with no
10100 * embedded expression will return null for the interpolation function.
10101 * @param {string=} trustedContext when provided, the returned function passes the interpolated
10102 * result through {@link ng.$sce#getTrusted $sce.getTrusted(interpolatedResult,
10103 * trustedContext)} before returning it. Refer to the {@link ng.$sce $sce} service that
10104 * provides Strict Contextual Escaping for details.
10105 * @param {boolean=} allOrNothing if `true`, then the returned function returns undefined
10106 * unless all embedded expressions evaluate to a value other than `undefined`.
10107 * @returns {function(context)} an interpolation function which is used to compute the
10108 * interpolated string. The function has these parameters:
10110 * - `context`: evaluation context for all expressions embedded in the interpolated text
10112 function $interpolate(text, mustHaveExpression, trustedContext, allOrNothing) {
10113 allOrNothing = !!allOrNothing;
10119 textLength = text.length,
10122 expressionPositions = [];
10124 while (index < textLength) {
10125 if (((startIndex = text.indexOf(startSymbol, index)) != -1) &&
10126 ((endIndex = text.indexOf(endSymbol, startIndex + startSymbolLength)) != -1)) {
10127 if (index !== startIndex) {
10128 concat.push(unescapeText(text.substring(index, startIndex)));
10130 exp = text.substring(startIndex + startSymbolLength, endIndex);
10131 expressions.push(exp);
10132 parseFns.push($parse(exp, parseStringifyInterceptor));
10133 index = endIndex + endSymbolLength;
10134 expressionPositions.push(concat.length);
10137 // we did not find an interpolation, so we have to add the remainder to the separators array
10138 if (index !== textLength) {
10139 concat.push(unescapeText(text.substring(index)));
10145 // Concatenating expressions makes it hard to reason about whether some combination of
10146 // concatenated values are unsafe to use and could easily lead to XSS. By requiring that a
10147 // single expression be used for iframe[src], object[src], etc., we ensure that the value
10148 // that's used is assigned or constructed by some JS code somewhere that is more testable or
10149 // make it obvious that you bound the value to some user controlled value. This helps reduce
10150 // the load when auditing for XSS issues.
10151 if (trustedContext && concat.length > 1) {
10152 throw $interpolateMinErr('noconcat',
10153 "Error while interpolating: {0}\nStrict Contextual Escaping disallows " +
10154 "interpolations that concatenate multiple expressions when a trusted value is " +
10155 "required. See http://docs.angularjs.org/api/ng.$sce", text);
10158 if (!mustHaveExpression || expressions.length) {
10159 var compute = function(values) {
10160 for (var i = 0, ii = expressions.length; i < ii; i++) {
10161 if (allOrNothing && isUndefined(values[i])) return;
10162 concat[expressionPositions[i]] = values[i];
10164 return concat.join('');
10167 var getValue = function(value) {
10168 return trustedContext ?
10169 $sce.getTrusted(trustedContext, value) :
10170 $sce.valueOf(value);
10173 var stringify = function(value) {
10174 if (value == null) { // null || undefined
10177 switch (typeof value) {
10181 value = '' + value;
10184 value = toJson(value);
10190 return extend(function interpolationFn(context) {
10192 var ii = expressions.length;
10193 var values = new Array(ii);
10196 for (; i < ii; i++) {
10197 values[i] = parseFns[i](context);
10200 return compute(values);
10202 var newErr = $interpolateMinErr('interr', "Can't interpolate: {0}\n{1}", text,
10204 $exceptionHandler(newErr);
10208 // all of these properties are undocumented for now
10209 exp: text, //just for compatibility with regular watchers created via $watch
10210 expressions: expressions,
10211 $$watchDelegate: function(scope, listener, objectEquality) {
10213 return scope.$watchGroup(parseFns, function interpolateFnWatcher(values, oldValues) {
10214 var currValue = compute(values);
10215 if (isFunction(listener)) {
10216 listener.call(this, currValue, values !== oldValues ? lastValue : currValue, scope);
10218 lastValue = currValue;
10219 }, objectEquality);
10224 function unescapeText(text) {
10225 return text.replace(escapedStartRegexp, startSymbol).
10226 replace(escapedEndRegexp, endSymbol);
10229 function parseStringifyInterceptor(value) {
10231 value = getValue(value);
10232 return allOrNothing && !isDefined(value) ? value : stringify(value);
10234 var newErr = $interpolateMinErr('interr', "Can't interpolate: {0}\n{1}", text,
10236 $exceptionHandler(newErr);
10244 * @name $interpolate#startSymbol
10246 * Symbol to denote the start of expression in the interpolated string. Defaults to `{{`.
10248 * Use {@link ng.$interpolateProvider#startSymbol `$interpolateProvider.startSymbol`} to change
10251 * @returns {string} start symbol.
10253 $interpolate.startSymbol = function() {
10254 return startSymbol;
10260 * @name $interpolate#endSymbol
10262 * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
10264 * Use {@link ng.$interpolateProvider#endSymbol `$interpolateProvider.endSymbol`} to change
10267 * @returns {string} end symbol.
10269 $interpolate.endSymbol = function() {
10273 return $interpolate;
10277 function $IntervalProvider() {
10278 this.$get = ['$rootScope', '$window', '$q', '$$q',
10279 function($rootScope, $window, $q, $$q) {
10280 var intervals = {};
10288 * Angular's wrapper for `window.setInterval`. The `fn` function is executed every `delay`
10291 * The return value of registering an interval function is a promise. This promise will be
10292 * notified upon each tick of the interval, and will be resolved after `count` iterations, or
10293 * run indefinitely if `count` is not defined. The value of the notification will be the
10294 * number of iterations that have run.
10295 * To cancel an interval, call `$interval.cancel(promise)`.
10297 * In tests you can use {@link ngMock.$interval#flush `$interval.flush(millis)`} to
10298 * move forward by `millis` milliseconds and trigger any functions scheduled to run in that
10301 * <div class="alert alert-warning">
10302 * **Note**: Intervals created by this service must be explicitly destroyed when you are finished
10303 * with them. In particular they are not automatically destroyed when a controller's scope or a
10304 * directive's element are destroyed.
10305 * You should take this into consideration and make sure to always cancel the interval at the
10306 * appropriate moment. See the example below for more details on how and when to do this.
10309 * @param {function()} fn A function that should be called repeatedly.
10310 * @param {number} delay Number of milliseconds between each function call.
10311 * @param {number=} [count=0] Number of times to repeat. If not set, or 0, will repeat
10313 * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
10314 * will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
10315 * @returns {promise} A promise which will be notified on each iteration.
10318 * <example module="intervalExample">
10319 * <file name="index.html">
10321 * angular.module('intervalExample', [])
10322 * .controller('ExampleController', ['$scope', '$interval',
10323 * function($scope, $interval) {
10324 * $scope.format = 'M/d/yy h:mm:ss a';
10325 * $scope.blood_1 = 100;
10326 * $scope.blood_2 = 120;
10329 * $scope.fight = function() {
10330 * // Don't start a new fight if we are already fighting
10331 * if ( angular.isDefined(stop) ) return;
10333 * stop = $interval(function() {
10334 * if ($scope.blood_1 > 0 && $scope.blood_2 > 0) {
10335 * $scope.blood_1 = $scope.blood_1 - 3;
10336 * $scope.blood_2 = $scope.blood_2 - 4;
10338 * $scope.stopFight();
10343 * $scope.stopFight = function() {
10344 * if (angular.isDefined(stop)) {
10345 * $interval.cancel(stop);
10346 * stop = undefined;
10350 * $scope.resetFight = function() {
10351 * $scope.blood_1 = 100;
10352 * $scope.blood_2 = 120;
10355 * $scope.$on('$destroy', function() {
10356 * // Make sure that the interval is destroyed too
10357 * $scope.stopFight();
10360 * // Register the 'myCurrentTime' directive factory method.
10361 * // We inject $interval and dateFilter service since the factory method is DI.
10362 * .directive('myCurrentTime', ['$interval', 'dateFilter',
10363 * function($interval, dateFilter) {
10364 * // return the directive link function. (compile function not needed)
10365 * return function(scope, element, attrs) {
10366 * var format, // date format
10367 * stopTime; // so that we can cancel the time updates
10369 * // used to update the UI
10370 * function updateTime() {
10371 * element.text(dateFilter(new Date(), format));
10374 * // watch the expression, and update the UI on change.
10375 * scope.$watch(attrs.myCurrentTime, function(value) {
10380 * stopTime = $interval(updateTime, 1000);
10382 * // listen on DOM destroy (removal) event, and cancel the next UI update
10383 * // to prevent updating time after the DOM element was removed.
10384 * element.on('$destroy', function() {
10385 * $interval.cancel(stopTime);
10392 * <div ng-controller="ExampleController">
10393 * Date format: <input ng-model="format"> <hr/>
10394 * Current time is: <span my-current-time="format"></span>
10396 * Blood 1 : <font color='red'>{{blood_1}}</font>
10397 * Blood 2 : <font color='red'>{{blood_2}}</font>
10398 * <button type="button" data-ng-click="fight()">Fight</button>
10399 * <button type="button" data-ng-click="stopFight()">StopFight</button>
10400 * <button type="button" data-ng-click="resetFight()">resetFight</button>
10407 function interval(fn, delay, count, invokeApply) {
10408 var setInterval = $window.setInterval,
10409 clearInterval = $window.clearInterval,
10411 skipApply = (isDefined(invokeApply) && !invokeApply),
10412 deferred = (skipApply ? $$q : $q).defer(),
10413 promise = deferred.promise;
10415 count = isDefined(count) ? count : 0;
10417 promise.then(null, null, fn);
10419 promise.$$intervalId = setInterval(function tick() {
10420 deferred.notify(iteration++);
10422 if (count > 0 && iteration >= count) {
10423 deferred.resolve(iteration);
10424 clearInterval(promise.$$intervalId);
10425 delete intervals[promise.$$intervalId];
10428 if (!skipApply) $rootScope.$apply();
10432 intervals[promise.$$intervalId] = deferred;
10440 * @name $interval#cancel
10443 * Cancels a task associated with the `promise`.
10445 * @param {promise} promise returned by the `$interval` function.
10446 * @returns {boolean} Returns `true` if the task was successfully canceled.
10448 interval.cancel = function(promise) {
10449 if (promise && promise.$$intervalId in intervals) {
10450 intervals[promise.$$intervalId].reject('canceled');
10451 $window.clearInterval(promise.$$intervalId);
10452 delete intervals[promise.$$intervalId];
10467 * $locale service provides localization rules for various Angular components. As of right now the
10468 * only public api is:
10470 * * `id` – `{string}` – locale id formatted as `languageId-countryId` (e.g. `en-us`)
10472 function $LocaleProvider() {
10473 this.$get = function() {
10481 { // Decimal Pattern
10491 },{ //Currency Pattern
10506 DATETIME_FORMATS: {
10508 'January,February,March,April,May,June,July,August,September,October,November,December'
10510 SHORTMONTH: 'Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec'.split(','),
10511 DAY: 'Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday'.split(','),
10512 SHORTDAY: 'Sun,Mon,Tue,Wed,Thu,Fri,Sat'.split(','),
10513 AMPMS: ['AM','PM'],
10514 medium: 'MMM d, y h:mm:ss a',
10515 'short': 'M/d/yy h:mm a',
10516 fullDate: 'EEEE, MMMM d, y',
10517 longDate: 'MMMM d, y',
10518 mediumDate: 'MMM d, y',
10519 shortDate: 'M/d/yy',
10520 mediumTime: 'h:mm:ss a',
10521 shortTime: 'h:mm a',
10532 pluralCat: function(num) {
10542 var PATH_MATCH = /^([^\?#]*)(\?([^#]*))?(#(.*))?$/,
10543 DEFAULT_PORTS = {'http': 80, 'https': 443, 'ftp': 21};
10544 var $locationMinErr = minErr('$location');
10548 * Encode path using encodeUriSegment, ignoring forward slashes
10550 * @param {string} path Path to encode
10551 * @returns {string}
10553 function encodePath(path) {
10554 var segments = path.split('/'),
10555 i = segments.length;
10558 segments[i] = encodeUriSegment(segments[i]);
10561 return segments.join('/');
10564 function parseAbsoluteUrl(absoluteUrl, locationObj) {
10565 var parsedUrl = urlResolve(absoluteUrl);
10567 locationObj.$$protocol = parsedUrl.protocol;
10568 locationObj.$$host = parsedUrl.hostname;
10569 locationObj.$$port = int(parsedUrl.port) || DEFAULT_PORTS[parsedUrl.protocol] || null;
10573 function parseAppUrl(relativeUrl, locationObj) {
10574 var prefixed = (relativeUrl.charAt(0) !== '/');
10576 relativeUrl = '/' + relativeUrl;
10578 var match = urlResolve(relativeUrl);
10579 locationObj.$$path = decodeURIComponent(prefixed && match.pathname.charAt(0) === '/' ?
10580 match.pathname.substring(1) : match.pathname);
10581 locationObj.$$search = parseKeyValue(match.search);
10582 locationObj.$$hash = decodeURIComponent(match.hash);
10584 // make sure path starts with '/';
10585 if (locationObj.$$path && locationObj.$$path.charAt(0) != '/') {
10586 locationObj.$$path = '/' + locationObj.$$path;
10593 * @param {string} begin
10594 * @param {string} whole
10595 * @returns {string} returns text from whole after begin or undefined if it does not begin with
10598 function beginsWith(begin, whole) {
10599 if (whole.indexOf(begin) === 0) {
10600 return whole.substr(begin.length);
10605 function stripHash(url) {
10606 var index = url.indexOf('#');
10607 return index == -1 ? url : url.substr(0, index);
10610 function trimEmptyHash(url) {
10611 return url.replace(/(#.+)|#$/, '$1');
10615 function stripFile(url) {
10616 return url.substr(0, stripHash(url).lastIndexOf('/') + 1);
10619 /* return the server only (scheme://host:port) */
10620 function serverBase(url) {
10621 return url.substring(0, url.indexOf('/', url.indexOf('//') + 2));
10626 * LocationHtml5Url represents an url
10627 * This object is exposed as $location service when HTML5 mode is enabled and supported
10630 * @param {string} appBase application base URL
10631 * @param {string} basePrefix url path prefix
10633 function LocationHtml5Url(appBase, basePrefix) {
10634 this.$$html5 = true;
10635 basePrefix = basePrefix || '';
10636 var appBaseNoFile = stripFile(appBase);
10637 parseAbsoluteUrl(appBase, this);
10641 * Parse given html5 (regular) url string into properties
10642 * @param {string} url HTML5 url
10645 this.$$parse = function(url) {
10646 var pathUrl = beginsWith(appBaseNoFile, url);
10647 if (!isString(pathUrl)) {
10648 throw $locationMinErr('ipthprfx', 'Invalid url "{0}", missing path prefix "{1}".', url,
10652 parseAppUrl(pathUrl, this);
10654 if (!this.$$path) {
10662 * Compose url and update `absUrl` property
10665 this.$$compose = function() {
10666 var search = toKeyValue(this.$$search),
10667 hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
10669 this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
10670 this.$$absUrl = appBaseNoFile + this.$$url.substr(1); // first char is always '/'
10673 this.$$parseLinkUrl = function(url, relHref) {
10674 if (relHref && relHref[0] === '#') {
10675 // special case for links to hash fragments:
10676 // keep the old url and only replace the hash fragment
10677 this.hash(relHref.slice(1));
10680 var appUrl, prevAppUrl;
10683 if ((appUrl = beginsWith(appBase, url)) !== undefined) {
10684 prevAppUrl = appUrl;
10685 if ((appUrl = beginsWith(basePrefix, appUrl)) !== undefined) {
10686 rewrittenUrl = appBaseNoFile + (beginsWith('/', appUrl) || appUrl);
10688 rewrittenUrl = appBase + prevAppUrl;
10690 } else if ((appUrl = beginsWith(appBaseNoFile, url)) !== undefined) {
10691 rewrittenUrl = appBaseNoFile + appUrl;
10692 } else if (appBaseNoFile == url + '/') {
10693 rewrittenUrl = appBaseNoFile;
10695 if (rewrittenUrl) {
10696 this.$$parse(rewrittenUrl);
10698 return !!rewrittenUrl;
10704 * LocationHashbangUrl represents url
10705 * This object is exposed as $location service when developer doesn't opt into html5 mode.
10706 * It also serves as the base class for html5 mode fallback on legacy browsers.
10709 * @param {string} appBase application base URL
10710 * @param {string} hashPrefix hashbang prefix
10712 function LocationHashbangUrl(appBase, hashPrefix) {
10713 var appBaseNoFile = stripFile(appBase);
10715 parseAbsoluteUrl(appBase, this);
10719 * Parse given hashbang url into properties
10720 * @param {string} url Hashbang url
10723 this.$$parse = function(url) {
10724 var withoutBaseUrl = beginsWith(appBase, url) || beginsWith(appBaseNoFile, url);
10725 var withoutHashUrl;
10727 if (withoutBaseUrl.charAt(0) === '#') {
10729 // The rest of the url starts with a hash so we have
10730 // got either a hashbang path or a plain hash fragment
10731 withoutHashUrl = beginsWith(hashPrefix, withoutBaseUrl);
10732 if (isUndefined(withoutHashUrl)) {
10733 // There was no hashbang prefix so we just have a hash fragment
10734 withoutHashUrl = withoutBaseUrl;
10738 // There was no hashbang path nor hash fragment:
10739 // If we are in HTML5 mode we use what is left as the path;
10740 // Otherwise we ignore what is left
10741 withoutHashUrl = this.$$html5 ? withoutBaseUrl : '';
10744 parseAppUrl(withoutHashUrl, this);
10746 this.$$path = removeWindowsDriveName(this.$$path, withoutHashUrl, appBase);
10751 * In Windows, on an anchor node on documents loaded from
10752 * the filesystem, the browser will return a pathname
10753 * prefixed with the drive name ('/C:/path') when a
10754 * pathname without a drive is set:
10755 * * a.setAttribute('href', '/foo')
10756 * * a.pathname === '/C:/foo' //true
10758 * Inside of Angular, we're always using pathnames that
10759 * do not include drive names for routing.
10761 function removeWindowsDriveName(path, url, base) {
10763 Matches paths for file protocol on windows,
10764 such as /C:/foo/bar, and captures only /foo/bar.
10766 var windowsFilePathExp = /^\/[A-Z]:(\/.*)/;
10768 var firstPathSegmentMatch;
10770 //Get the relative path from the input URL.
10771 if (url.indexOf(base) === 0) {
10772 url = url.replace(base, '');
10775 // The input URL intentionally contains a first path segment that ends with a colon.
10776 if (windowsFilePathExp.exec(url)) {
10780 firstPathSegmentMatch = windowsFilePathExp.exec(path);
10781 return firstPathSegmentMatch ? firstPathSegmentMatch[1] : path;
10786 * Compose hashbang url and update `absUrl` property
10789 this.$$compose = function() {
10790 var search = toKeyValue(this.$$search),
10791 hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
10793 this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
10794 this.$$absUrl = appBase + (this.$$url ? hashPrefix + this.$$url : '');
10797 this.$$parseLinkUrl = function(url, relHref) {
10798 if (stripHash(appBase) == stripHash(url)) {
10808 * LocationHashbangUrl represents url
10809 * This object is exposed as $location service when html5 history api is enabled but the browser
10810 * does not support it.
10813 * @param {string} appBase application base URL
10814 * @param {string} hashPrefix hashbang prefix
10816 function LocationHashbangInHtml5Url(appBase, hashPrefix) {
10817 this.$$html5 = true;
10818 LocationHashbangUrl.apply(this, arguments);
10820 var appBaseNoFile = stripFile(appBase);
10822 this.$$parseLinkUrl = function(url, relHref) {
10823 if (relHref && relHref[0] === '#') {
10824 // special case for links to hash fragments:
10825 // keep the old url and only replace the hash fragment
10826 this.hash(relHref.slice(1));
10833 if (appBase == stripHash(url)) {
10834 rewrittenUrl = url;
10835 } else if ((appUrl = beginsWith(appBaseNoFile, url))) {
10836 rewrittenUrl = appBase + hashPrefix + appUrl;
10837 } else if (appBaseNoFile === url + '/') {
10838 rewrittenUrl = appBaseNoFile;
10840 if (rewrittenUrl) {
10841 this.$$parse(rewrittenUrl);
10843 return !!rewrittenUrl;
10846 this.$$compose = function() {
10847 var search = toKeyValue(this.$$search),
10848 hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
10850 this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
10851 // include hashPrefix in $$absUrl when $$url is empty so IE8 & 9 do not reload page because of removal of '#'
10852 this.$$absUrl = appBase + hashPrefix + this.$$url;
10858 var locationPrototype = {
10861 * Are we in html5 mode?
10867 * Has any change been replacing?
10874 * @name $location#absUrl
10877 * This method is getter only.
10879 * Return full url representation with all segments encoded according to rules specified in
10880 * [RFC 3986](http://www.ietf.org/rfc/rfc3986.txt).
10884 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
10885 * var absUrl = $location.absUrl();
10886 * // => "http://example.com/#/some/path?foo=bar&baz=xoxo"
10889 * @return {string} full url
10891 absUrl: locationGetter('$$absUrl'),
10895 * @name $location#url
10898 * This method is getter / setter.
10900 * Return url (e.g. `/path?a=b#hash`) when called without any parameter.
10902 * Change path, search and hash, when called with parameter and return `$location`.
10906 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
10907 * var url = $location.url();
10908 * // => "/some/path?foo=bar&baz=xoxo"
10911 * @param {string=} url New url without base prefix (e.g. `/path?a=b#hash`)
10912 * @return {string} url
10914 url: function(url) {
10915 if (isUndefined(url))
10918 var match = PATH_MATCH.exec(url);
10919 if (match[1] || url === '') this.path(decodeURIComponent(match[1]));
10920 if (match[2] || match[1] || url === '') this.search(match[3] || '');
10921 this.hash(match[5] || '');
10928 * @name $location#protocol
10931 * This method is getter only.
10933 * Return protocol of current url.
10937 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
10938 * var protocol = $location.protocol();
10942 * @return {string} protocol of current url
10944 protocol: locationGetter('$$protocol'),
10948 * @name $location#host
10951 * This method is getter only.
10953 * Return host of current url.
10957 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
10958 * var host = $location.host();
10959 * // => "example.com"
10962 * @return {string} host of current url.
10964 host: locationGetter('$$host'),
10968 * @name $location#port
10971 * This method is getter only.
10973 * Return port of current url.
10977 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
10978 * var port = $location.port();
10982 * @return {Number} port
10984 port: locationGetter('$$port'),
10988 * @name $location#path
10991 * This method is getter / setter.
10993 * Return path of current url when called without any parameter.
10995 * Change path when called with parameter and return `$location`.
10997 * Note: Path should always begin with forward slash (/), this method will add the forward slash
10998 * if it is missing.
11002 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
11003 * var path = $location.path();
11004 * // => "/some/path"
11007 * @param {(string|number)=} path New path
11008 * @return {string} path
11010 path: locationGetterSetter('$$path', function(path) {
11011 path = path !== null ? path.toString() : '';
11012 return path.charAt(0) == '/' ? path : '/' + path;
11017 * @name $location#search
11020 * This method is getter / setter.
11022 * Return search part (as object) of current url when called without any parameter.
11024 * Change search part when called with parameter and return `$location`.
11028 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
11029 * var searchObject = $location.search();
11030 * // => {foo: 'bar', baz: 'xoxo'}
11032 * // set foo to 'yipee'
11033 * $location.search('foo', 'yipee');
11034 * // $location.search() => {foo: 'yipee', baz: 'xoxo'}
11037 * @param {string|Object.<string>|Object.<Array.<string>>} search New search params - string or
11040 * When called with a single argument the method acts as a setter, setting the `search` component
11041 * of `$location` to the specified value.
11043 * If the argument is a hash object containing an array of values, these values will be encoded
11044 * as duplicate search parameters in the url.
11046 * @param {(string|Number|Array<string>|boolean)=} paramValue If `search` is a string or number, then `paramValue`
11047 * will override only a single search property.
11049 * If `paramValue` is an array, it will override the property of the `search` component of
11050 * `$location` specified via the first argument.
11052 * If `paramValue` is `null`, the property specified via the first argument will be deleted.
11054 * If `paramValue` is `true`, the property specified via the first argument will be added with no
11055 * value nor trailing equal sign.
11057 * @return {Object} If called with no arguments returns the parsed `search` object. If called with
11058 * one or more arguments returns `$location` object itself.
11060 search: function(search, paramValue) {
11061 switch (arguments.length) {
11063 return this.$$search;
11065 if (isString(search) || isNumber(search)) {
11066 search = search.toString();
11067 this.$$search = parseKeyValue(search);
11068 } else if (isObject(search)) {
11069 search = copy(search, {});
11070 // remove object undefined or null properties
11071 forEach(search, function(value, key) {
11072 if (value == null) delete search[key];
11075 this.$$search = search;
11077 throw $locationMinErr('isrcharg',
11078 'The first argument of the `$location#search()` call must be a string or an object.');
11082 if (isUndefined(paramValue) || paramValue === null) {
11083 delete this.$$search[search];
11085 this.$$search[search] = paramValue;
11095 * @name $location#hash
11098 * This method is getter / setter.
11100 * Return hash fragment when called without any parameter.
11102 * Change hash fragment when called with parameter and return `$location`.
11106 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo#hashValue
11107 * var hash = $location.hash();
11108 * // => "hashValue"
11111 * @param {(string|number)=} hash New hash fragment
11112 * @return {string} hash
11114 hash: locationGetterSetter('$$hash', function(hash) {
11115 return hash !== null ? hash.toString() : '';
11120 * @name $location#replace
11123 * If called, all changes to $location during current `$digest` will be replacing current history
11124 * record, instead of adding new one.
11126 replace: function() {
11127 this.$$replace = true;
11132 forEach([LocationHashbangInHtml5Url, LocationHashbangUrl, LocationHtml5Url], function(Location) {
11133 Location.prototype = Object.create(locationPrototype);
11137 * @name $location#state
11140 * This method is getter / setter.
11142 * Return the history state object when called without any parameter.
11144 * Change the history state object when called with one parameter and return `$location`.
11145 * The state object is later passed to `pushState` or `replaceState`.
11147 * NOTE: This method is supported only in HTML5 mode and only in browsers supporting
11148 * the HTML5 History API (i.e. methods `pushState` and `replaceState`). If you need to support
11149 * older browsers (like IE9 or Android < 4.0), don't use this method.
11151 * @param {object=} state State object for pushState or replaceState
11152 * @return {object} state
11154 Location.prototype.state = function(state) {
11155 if (!arguments.length)
11156 return this.$$state;
11158 if (Location !== LocationHtml5Url || !this.$$html5) {
11159 throw $locationMinErr('nostate', 'History API state support is available only ' +
11160 'in HTML5 mode and only in browsers supporting HTML5 History API');
11162 // The user might modify `stateObject` after invoking `$location.state(stateObject)`
11163 // but we're changing the $$state reference to $browser.state() during the $digest
11164 // so the modification window is narrow.
11165 this.$$state = isUndefined(state) ? null : state;
11172 function locationGetter(property) {
11173 return function() {
11174 return this[property];
11179 function locationGetterSetter(property, preprocess) {
11180 return function(value) {
11181 if (isUndefined(value))
11182 return this[property];
11184 this[property] = preprocess(value);
11196 * @requires $rootElement
11199 * The $location service parses the URL in the browser address bar (based on the
11200 * [window.location](https://developer.mozilla.org/en/window.location)) and makes the URL
11201 * available to your application. Changes to the URL in the address bar are reflected into
11202 * $location service and changes to $location are reflected into the browser address bar.
11204 * **The $location service:**
11206 * - Exposes the current URL in the browser address bar, so you can
11207 * - Watch and observe the URL.
11208 * - Change the URL.
11209 * - Synchronizes the URL with the browser when the user
11210 * - Changes the address bar.
11211 * - Clicks the back or forward button (or clicks a History link).
11212 * - Clicks on a link.
11213 * - Represents the URL object as a set of methods (protocol, host, port, path, search, hash).
11215 * For more information see {@link guide/$location Developer Guide: Using $location}
11220 * @name $locationProvider
11222 * Use the `$locationProvider` to configure how the application deep linking paths are stored.
11224 function $LocationProvider() {
11225 var hashPrefix = '',
11234 * @name $locationProvider#hashPrefix
11236 * @param {string=} prefix Prefix for hash part (containing path and search)
11237 * @returns {*} current value if used as getter or itself (chaining) if used as setter
11239 this.hashPrefix = function(prefix) {
11240 if (isDefined(prefix)) {
11241 hashPrefix = prefix;
11250 * @name $locationProvider#html5Mode
11252 * @param {(boolean|Object)=} mode If boolean, sets `html5Mode.enabled` to value.
11253 * If object, sets `enabled`, `requireBase` and `rewriteLinks` to respective values. Supported
11255 * - **enabled** – `{boolean}` – (default: false) If true, will rely on `history.pushState` to
11256 * change urls where supported. Will fall back to hash-prefixed paths in browsers that do not
11257 * support `pushState`.
11258 * - **requireBase** - `{boolean}` - (default: `true`) When html5Mode is enabled, specifies
11259 * whether or not a <base> tag is required to be present. If `enabled` and `requireBase` are
11260 * true, and a base tag is not present, an error will be thrown when `$location` is injected.
11261 * See the {@link guide/$location $location guide for more information}
11262 * - **rewriteLinks** - `{boolean}` - (default: `true`) When html5Mode is enabled,
11263 * enables/disables url rewriting for relative links.
11265 * @returns {Object} html5Mode object if used as getter or itself (chaining) if used as setter
11267 this.html5Mode = function(mode) {
11268 if (isBoolean(mode)) {
11269 html5Mode.enabled = mode;
11271 } else if (isObject(mode)) {
11273 if (isBoolean(mode.enabled)) {
11274 html5Mode.enabled = mode.enabled;
11277 if (isBoolean(mode.requireBase)) {
11278 html5Mode.requireBase = mode.requireBase;
11281 if (isBoolean(mode.rewriteLinks)) {
11282 html5Mode.rewriteLinks = mode.rewriteLinks;
11293 * @name $location#$locationChangeStart
11294 * @eventType broadcast on root scope
11296 * Broadcasted before a URL will change.
11298 * This change can be prevented by calling
11299 * `preventDefault` method of the event. See {@link ng.$rootScope.Scope#$on} for more
11300 * details about event object. Upon successful change
11301 * {@link ng.$location#$locationChangeSuccess $locationChangeSuccess} is fired.
11303 * The `newState` and `oldState` parameters may be defined only in HTML5 mode and when
11304 * the browser supports the HTML5 History API.
11306 * @param {Object} angularEvent Synthetic event object.
11307 * @param {string} newUrl New URL
11308 * @param {string=} oldUrl URL that was before it was changed.
11309 * @param {string=} newState New history state object
11310 * @param {string=} oldState History state object that was before it was changed.
11315 * @name $location#$locationChangeSuccess
11316 * @eventType broadcast on root scope
11318 * Broadcasted after a URL was changed.
11320 * The `newState` and `oldState` parameters may be defined only in HTML5 mode and when
11321 * the browser supports the HTML5 History API.
11323 * @param {Object} angularEvent Synthetic event object.
11324 * @param {string} newUrl New URL
11325 * @param {string=} oldUrl URL that was before it was changed.
11326 * @param {string=} newState New history state object
11327 * @param {string=} oldState History state object that was before it was changed.
11330 this.$get = ['$rootScope', '$browser', '$sniffer', '$rootElement', '$window',
11331 function($rootScope, $browser, $sniffer, $rootElement, $window) {
11334 baseHref = $browser.baseHref(), // if base[href] is undefined, it defaults to ''
11335 initialUrl = $browser.url(),
11338 if (html5Mode.enabled) {
11339 if (!baseHref && html5Mode.requireBase) {
11340 throw $locationMinErr('nobase',
11341 "$location in HTML5 mode requires a <base> tag to be present!");
11343 appBase = serverBase(initialUrl) + (baseHref || '/');
11344 LocationMode = $sniffer.history ? LocationHtml5Url : LocationHashbangInHtml5Url;
11346 appBase = stripHash(initialUrl);
11347 LocationMode = LocationHashbangUrl;
11349 $location = new LocationMode(appBase, '#' + hashPrefix);
11350 $location.$$parseLinkUrl(initialUrl, initialUrl);
11352 $location.$$state = $browser.state();
11354 var IGNORE_URI_REGEXP = /^\s*(javascript|mailto):/i;
11356 function setBrowserUrlWithFallback(url, replace, state) {
11357 var oldUrl = $location.url();
11358 var oldState = $location.$$state;
11360 $browser.url(url, replace, state);
11362 // Make sure $location.state() returns referentially identical (not just deeply equal)
11363 // state object; this makes possible quick checking if the state changed in the digest
11364 // loop. Checking deep equality would be too expensive.
11365 $location.$$state = $browser.state();
11367 // Restore old values if pushState fails
11368 $location.url(oldUrl);
11369 $location.$$state = oldState;
11375 $rootElement.on('click', function(event) {
11376 // TODO(vojta): rewrite link when opening in new tab/window (in legacy browser)
11377 // currently we open nice url link and redirect then
11379 if (!html5Mode.rewriteLinks || event.ctrlKey || event.metaKey || event.shiftKey || event.which == 2 || event.button == 2) return;
11381 var elm = jqLite(event.target);
11383 // traverse the DOM up to find first A tag
11384 while (nodeName_(elm[0]) !== 'a') {
11385 // ignore rewriting if no A tag (reached root element, or no parent - removed from document)
11386 if (elm[0] === $rootElement[0] || !(elm = elm.parent())[0]) return;
11389 var absHref = elm.prop('href');
11390 // get the actual href attribute - see
11391 // http://msdn.microsoft.com/en-us/library/ie/dd347148(v=vs.85).aspx
11392 var relHref = elm.attr('href') || elm.attr('xlink:href');
11394 if (isObject(absHref) && absHref.toString() === '[object SVGAnimatedString]') {
11395 // SVGAnimatedString.animVal should be identical to SVGAnimatedString.baseVal, unless during
11397 absHref = urlResolve(absHref.animVal).href;
11400 // Ignore when url is started with javascript: or mailto:
11401 if (IGNORE_URI_REGEXP.test(absHref)) return;
11403 if (absHref && !elm.attr('target') && !event.isDefaultPrevented()) {
11404 if ($location.$$parseLinkUrl(absHref, relHref)) {
11405 // We do a preventDefault for all urls that are part of the angular application,
11406 // in html5mode and also without, so that we are able to abort navigation without
11407 // getting double entries in the location history.
11408 event.preventDefault();
11409 // update location manually
11410 if ($location.absUrl() != $browser.url()) {
11411 $rootScope.$apply();
11412 // hack to work around FF6 bug 684208 when scenario runner clicks on links
11413 $window.angular['ff-684208-preventDefault'] = true;
11420 // rewrite hashbang url <> html5 url
11421 if (trimEmptyHash($location.absUrl()) != trimEmptyHash(initialUrl)) {
11422 $browser.url($location.absUrl(), true);
11425 var initializing = true;
11427 // update $location when $browser url changes
11428 $browser.onUrlChange(function(newUrl, newState) {
11429 $rootScope.$evalAsync(function() {
11430 var oldUrl = $location.absUrl();
11431 var oldState = $location.$$state;
11432 var defaultPrevented;
11434 $location.$$parse(newUrl);
11435 $location.$$state = newState;
11437 defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
11438 newState, oldState).defaultPrevented;
11440 // if the location was changed by a `$locationChangeStart` handler then stop
11441 // processing this location change
11442 if ($location.absUrl() !== newUrl) return;
11444 if (defaultPrevented) {
11445 $location.$$parse(oldUrl);
11446 $location.$$state = oldState;
11447 setBrowserUrlWithFallback(oldUrl, false, oldState);
11449 initializing = false;
11450 afterLocationChange(oldUrl, oldState);
11453 if (!$rootScope.$$phase) $rootScope.$digest();
11457 $rootScope.$watch(function $locationWatch() {
11458 var oldUrl = trimEmptyHash($browser.url());
11459 var newUrl = trimEmptyHash($location.absUrl());
11460 var oldState = $browser.state();
11461 var currentReplace = $location.$$replace;
11462 var urlOrStateChanged = oldUrl !== newUrl ||
11463 ($location.$$html5 && $sniffer.history && oldState !== $location.$$state);
11465 if (initializing || urlOrStateChanged) {
11466 initializing = false;
11468 $rootScope.$evalAsync(function() {
11469 var newUrl = $location.absUrl();
11470 var defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
11471 $location.$$state, oldState).defaultPrevented;
11473 // if the location was changed by a `$locationChangeStart` handler then stop
11474 // processing this location change
11475 if ($location.absUrl() !== newUrl) return;
11477 if (defaultPrevented) {
11478 $location.$$parse(oldUrl);
11479 $location.$$state = oldState;
11481 if (urlOrStateChanged) {
11482 setBrowserUrlWithFallback(newUrl, currentReplace,
11483 oldState === $location.$$state ? null : $location.$$state);
11485 afterLocationChange(oldUrl, oldState);
11490 $location.$$replace = false;
11492 // we don't need to return anything because $evalAsync will make the digest loop dirty when
11493 // there is a change
11498 function afterLocationChange(oldUrl, oldState) {
11499 $rootScope.$broadcast('$locationChangeSuccess', $location.absUrl(), oldUrl,
11500 $location.$$state, oldState);
11508 * @requires $window
11511 * Simple service for logging. Default implementation safely writes the message
11512 * into the browser's console (if present).
11514 * The main purpose of this service is to simplify debugging and troubleshooting.
11516 * The default is to log `debug` messages. You can use
11517 * {@link ng.$logProvider ng.$logProvider#debugEnabled} to change this.
11520 <example module="logExample">
11521 <file name="script.js">
11522 angular.module('logExample', [])
11523 .controller('LogController', ['$scope', '$log', function($scope, $log) {
11524 $scope.$log = $log;
11525 $scope.message = 'Hello World!';
11528 <file name="index.html">
11529 <div ng-controller="LogController">
11530 <p>Reload this page with open console, enter text and hit the log button...</p>
11532 <input type="text" ng-model="message"/>
11533 <button ng-click="$log.log(message)">log</button>
11534 <button ng-click="$log.warn(message)">warn</button>
11535 <button ng-click="$log.info(message)">info</button>
11536 <button ng-click="$log.error(message)">error</button>
11537 <button ng-click="$log.debug(message)">debug</button>
11545 * @name $logProvider
11547 * Use the `$logProvider` to configure how the application logs messages
11549 function $LogProvider() {
11555 * @name $logProvider#debugEnabled
11557 * @param {boolean=} flag enable or disable debug level messages
11558 * @returns {*} current value if used as getter or itself (chaining) if used as setter
11560 this.debugEnabled = function(flag) {
11561 if (isDefined(flag)) {
11569 this.$get = ['$window', function($window) {
11576 * Write a log message
11578 log: consoleLog('log'),
11585 * Write an information message
11587 info: consoleLog('info'),
11594 * Write a warning message
11596 warn: consoleLog('warn'),
11603 * Write an error message
11605 error: consoleLog('error'),
11612 * Write a debug message
11614 debug: (function() {
11615 var fn = consoleLog('debug');
11617 return function() {
11619 fn.apply(self, arguments);
11625 function formatError(arg) {
11626 if (arg instanceof Error) {
11628 arg = (arg.message && arg.stack.indexOf(arg.message) === -1)
11629 ? 'Error: ' + arg.message + '\n' + arg.stack
11631 } else if (arg.sourceURL) {
11632 arg = arg.message + '\n' + arg.sourceURL + ':' + arg.line;
11638 function consoleLog(type) {
11639 var console = $window.console || {},
11640 logFn = console[type] || console.log || noop,
11643 // Note: reading logFn.apply throws an error in IE11 in IE8 document mode.
11644 // The reason behind this is that console.log has type "object" in IE8...
11646 hasApply = !!logFn.apply;
11650 return function() {
11652 forEach(arguments, function(arg) {
11653 args.push(formatError(arg));
11655 return logFn.apply(console, args);
11659 // we are IE which either doesn't have window.console => this is noop and we do nothing,
11660 // or we are IE where console.log doesn't have apply so we log at least first 2 args
11661 return function(arg1, arg2) {
11662 logFn(arg1, arg2 == null ? '' : arg2);
11668 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
11669 * Any commits to this file should be reviewed with security in mind. *
11670 * Changes to this file can potentially create security vulnerabilities. *
11671 * An approval from 2 Core members with history of modifying *
11672 * this file is required. *
11674 * Does the change somehow allow for arbitrary javascript to be executed? *
11675 * Or allows for someone to change the prototype of built-in objects? *
11676 * Or gives undesired access to variables likes document or window? *
11677 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
11679 var $parseMinErr = minErr('$parse');
11681 // Sandboxing Angular Expressions
11682 // ------------------------------
11683 // Angular expressions are generally considered safe because these expressions only have direct
11684 // access to `$scope` and locals. However, one can obtain the ability to execute arbitrary JS code by
11685 // obtaining a reference to native JS functions such as the Function constructor.
11687 // As an example, consider the following Angular expression:
11689 // {}.toString.constructor('alert("evil JS code")')
11691 // This sandboxing technique is not perfect and doesn't aim to be. The goal is to prevent exploits
11692 // against the expression language, but not to prevent exploits that were enabled by exposing
11693 // sensitive JavaScript or browser APIs on Scope. Exposing such objects on a Scope is never a good
11694 // practice and therefore we are not even trying to protect against interaction with an object
11695 // explicitly exposed in this way.
11697 // In general, it is not possible to access a Window object from an angular expression unless a
11698 // window or some DOM object that has a reference to window is published onto a Scope.
11699 // Similarly we prevent invocations of function known to be dangerous, as well as assignments to
11702 // See https://docs.angularjs.org/guide/security
11705 function ensureSafeMemberName(name, fullExpression) {
11706 if (name === "__defineGetter__" || name === "__defineSetter__"
11707 || name === "__lookupGetter__" || name === "__lookupSetter__"
11708 || name === "__proto__") {
11709 throw $parseMinErr('isecfld',
11710 'Attempting to access a disallowed field in Angular expressions! '
11711 + 'Expression: {0}', fullExpression);
11716 function ensureSafeObject(obj, fullExpression) {
11717 // nifty check if obj is Function that is fast and works across iframes and other contexts
11719 if (obj.constructor === obj) {
11720 throw $parseMinErr('isecfn',
11721 'Referencing Function in Angular expressions is disallowed! Expression: {0}',
11723 } else if (// isWindow(obj)
11724 obj.window === obj) {
11725 throw $parseMinErr('isecwindow',
11726 'Referencing the Window in Angular expressions is disallowed! Expression: {0}',
11728 } else if (// isElement(obj)
11729 obj.children && (obj.nodeName || (obj.prop && obj.attr && obj.find))) {
11730 throw $parseMinErr('isecdom',
11731 'Referencing DOM nodes in Angular expressions is disallowed! Expression: {0}',
11733 } else if (// block Object so that we can't get hold of dangerous Object.* methods
11735 throw $parseMinErr('isecobj',
11736 'Referencing Object in Angular expressions is disallowed! Expression: {0}',
11743 var CALL = Function.prototype.call;
11744 var APPLY = Function.prototype.apply;
11745 var BIND = Function.prototype.bind;
11747 function ensureSafeFunction(obj, fullExpression) {
11749 if (obj.constructor === obj) {
11750 throw $parseMinErr('isecfn',
11751 'Referencing Function in Angular expressions is disallowed! Expression: {0}',
11753 } else if (obj === CALL || obj === APPLY || obj === BIND) {
11754 throw $parseMinErr('isecff',
11755 'Referencing call, apply or bind in Angular expressions is disallowed! Expression: {0}',
11761 //Keyword constants
11762 var CONSTANTS = createMap();
11764 'null': function() { return null; },
11765 'true': function() { return true; },
11766 'false': function() { return false; },
11767 'undefined': function() {}
11768 }, function(constantGetter, name) {
11769 constantGetter.constant = constantGetter.literal = constantGetter.sharedGetter = true;
11770 CONSTANTS[name] = constantGetter;
11773 //Not quite a constant, but can be lex/parsed the same
11774 CONSTANTS['this'] = function(self) { return self; };
11775 CONSTANTS['this'].sharedGetter = true;
11778 //Operators - will be wrapped by binaryFn/unaryFn/assignment/filter
11779 var OPERATORS = extend(createMap(), {
11780 '+':function(self, locals, a, b) {
11781 a=a(self, locals); b=b(self, locals);
11782 if (isDefined(a)) {
11783 if (isDefined(b)) {
11788 return isDefined(b) ? b : undefined;},
11789 '-':function(self, locals, a, b) {
11790 a=a(self, locals); b=b(self, locals);
11791 return (isDefined(a) ? a : 0) - (isDefined(b) ? b : 0);
11793 '*':function(self, locals, a, b) {return a(self, locals) * b(self, locals);},
11794 '/':function(self, locals, a, b) {return a(self, locals) / b(self, locals);},
11795 '%':function(self, locals, a, b) {return a(self, locals) % b(self, locals);},
11796 '===':function(self, locals, a, b) {return a(self, locals) === b(self, locals);},
11797 '!==':function(self, locals, a, b) {return a(self, locals) !== b(self, locals);},
11798 '==':function(self, locals, a, b) {return a(self, locals) == b(self, locals);},
11799 '!=':function(self, locals, a, b) {return a(self, locals) != b(self, locals);},
11800 '<':function(self, locals, a, b) {return a(self, locals) < b(self, locals);},
11801 '>':function(self, locals, a, b) {return a(self, locals) > b(self, locals);},
11802 '<=':function(self, locals, a, b) {return a(self, locals) <= b(self, locals);},
11803 '>=':function(self, locals, a, b) {return a(self, locals) >= b(self, locals);},
11804 '&&':function(self, locals, a, b) {return a(self, locals) && b(self, locals);},
11805 '||':function(self, locals, a, b) {return a(self, locals) || b(self, locals);},
11806 '!':function(self, locals, a) {return !a(self, locals);},
11808 //Tokenized as operators but parsed as assignment/filters
11812 var ESCAPE = {"n":"\n", "f":"\f", "r":"\r", "t":"\t", "v":"\v", "'":"'", '"':'"'};
11815 /////////////////////////////////////////
11821 var Lexer = function(options) {
11822 this.options = options;
11825 Lexer.prototype = {
11826 constructor: Lexer,
11828 lex: function(text) {
11833 while (this.index < this.text.length) {
11834 var ch = this.text.charAt(this.index);
11835 if (ch === '"' || ch === "'") {
11836 this.readString(ch);
11837 } else if (this.isNumber(ch) || ch === '.' && this.isNumber(this.peek())) {
11839 } else if (this.isIdent(ch)) {
11841 } else if (this.is(ch, '(){}[].,;:?')) {
11842 this.tokens.push({index: this.index, text: ch});
11844 } else if (this.isWhitespace(ch)) {
11847 var ch2 = ch + this.peek();
11848 var ch3 = ch2 + this.peek(2);
11849 var op1 = OPERATORS[ch];
11850 var op2 = OPERATORS[ch2];
11851 var op3 = OPERATORS[ch3];
11852 if (op1 || op2 || op3) {
11853 var token = op3 ? ch3 : (op2 ? ch2 : ch);
11854 this.tokens.push({index: this.index, text: token, operator: true});
11855 this.index += token.length;
11857 this.throwError('Unexpected next character ', this.index, this.index + 1);
11861 return this.tokens;
11864 is: function(ch, chars) {
11865 return chars.indexOf(ch) !== -1;
11868 peek: function(i) {
11870 return (this.index + num < this.text.length) ? this.text.charAt(this.index + num) : false;
11873 isNumber: function(ch) {
11874 return ('0' <= ch && ch <= '9') && typeof ch === "string";
11877 isWhitespace: function(ch) {
11878 // IE treats non-breaking space as \u00A0
11879 return (ch === ' ' || ch === '\r' || ch === '\t' ||
11880 ch === '\n' || ch === '\v' || ch === '\u00A0');
11883 isIdent: function(ch) {
11884 return ('a' <= ch && ch <= 'z' ||
11885 'A' <= ch && ch <= 'Z' ||
11886 '_' === ch || ch === '$');
11889 isExpOperator: function(ch) {
11890 return (ch === '-' || ch === '+' || this.isNumber(ch));
11893 throwError: function(error, start, end) {
11894 end = end || this.index;
11895 var colStr = (isDefined(start)
11896 ? 's ' + start + '-' + this.index + ' [' + this.text.substring(start, end) + ']'
11898 throw $parseMinErr('lexerr', 'Lexer Error: {0} at column{1} in expression [{2}].',
11899 error, colStr, this.text);
11902 readNumber: function() {
11904 var start = this.index;
11905 while (this.index < this.text.length) {
11906 var ch = lowercase(this.text.charAt(this.index));
11907 if (ch == '.' || this.isNumber(ch)) {
11910 var peekCh = this.peek();
11911 if (ch == 'e' && this.isExpOperator(peekCh)) {
11913 } else if (this.isExpOperator(ch) &&
11914 peekCh && this.isNumber(peekCh) &&
11915 number.charAt(number.length - 1) == 'e') {
11917 } else if (this.isExpOperator(ch) &&
11918 (!peekCh || !this.isNumber(peekCh)) &&
11919 number.charAt(number.length - 1) == 'e') {
11920 this.throwError('Invalid exponent');
11931 value: Number(number)
11935 readIdent: function() {
11936 var start = this.index;
11937 while (this.index < this.text.length) {
11938 var ch = this.text.charAt(this.index);
11939 if (!(this.isIdent(ch) || this.isNumber(ch))) {
11946 text: this.text.slice(start, this.index),
11951 readString: function(quote) {
11952 var start = this.index;
11955 var rawString = quote;
11956 var escape = false;
11957 while (this.index < this.text.length) {
11958 var ch = this.text.charAt(this.index);
11962 var hex = this.text.substring(this.index + 1, this.index + 5);
11963 if (!hex.match(/[\da-f]{4}/i))
11964 this.throwError('Invalid unicode escape [\\u' + hex + ']');
11966 string += String.fromCharCode(parseInt(hex, 16));
11968 var rep = ESCAPE[ch];
11969 string = string + (rep || ch);
11972 } else if (ch === '\\') {
11974 } else if (ch === quote) {
11988 this.throwError('Unterminated quote', start);
11993 function isConstant(exp) {
11994 return exp.constant;
12000 var Parser = function(lexer, $filter, options) {
12001 this.lexer = lexer;
12002 this.$filter = $filter;
12003 this.options = options;
12006 Parser.ZERO = extend(function() {
12009 sharedGetter: true,
12013 Parser.prototype = {
12014 constructor: Parser,
12016 parse: function(text) {
12018 this.tokens = this.lexer.lex(text);
12020 var value = this.statements();
12022 if (this.tokens.length !== 0) {
12023 this.throwError('is an unexpected token', this.tokens[0]);
12026 value.literal = !!value.literal;
12027 value.constant = !!value.constant;
12032 primary: function() {
12034 if (this.expect('(')) {
12035 primary = this.filterChain();
12037 } else if (this.expect('[')) {
12038 primary = this.arrayDeclaration();
12039 } else if (this.expect('{')) {
12040 primary = this.object();
12041 } else if (this.peek().identifier && this.peek().text in CONSTANTS) {
12042 primary = CONSTANTS[this.consume().text];
12043 } else if (this.peek().identifier) {
12044 primary = this.identifier();
12045 } else if (this.peek().constant) {
12046 primary = this.constant();
12048 this.throwError('not a primary expression', this.peek());
12052 while ((next = this.expect('(', '[', '.'))) {
12053 if (next.text === '(') {
12054 primary = this.functionCall(primary, context);
12056 } else if (next.text === '[') {
12058 primary = this.objectIndex(primary);
12059 } else if (next.text === '.') {
12061 primary = this.fieldAccess(primary);
12063 this.throwError('IMPOSSIBLE');
12069 throwError: function(msg, token) {
12070 throw $parseMinErr('syntax',
12071 'Syntax Error: Token \'{0}\' {1} at column {2} of the expression [{3}] starting at [{4}].',
12072 token.text, msg, (token.index + 1), this.text, this.text.substring(token.index));
12075 peekToken: function() {
12076 if (this.tokens.length === 0)
12077 throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text);
12078 return this.tokens[0];
12081 peek: function(e1, e2, e3, e4) {
12082 return this.peekAhead(0, e1, e2, e3, e4);
12084 peekAhead: function(i, e1, e2, e3, e4) {
12085 if (this.tokens.length > i) {
12086 var token = this.tokens[i];
12087 var t = token.text;
12088 if (t === e1 || t === e2 || t === e3 || t === e4 ||
12089 (!e1 && !e2 && !e3 && !e4)) {
12096 expect: function(e1, e2, e3, e4) {
12097 var token = this.peek(e1, e2, e3, e4);
12099 this.tokens.shift();
12105 consume: function(e1) {
12106 if (this.tokens.length === 0) {
12107 throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text);
12110 var token = this.expect(e1);
12112 this.throwError('is unexpected, expecting [' + e1 + ']', this.peek());
12117 unaryFn: function(op, right) {
12118 var fn = OPERATORS[op];
12119 return extend(function $parseUnaryFn(self, locals) {
12120 return fn(self, locals, right);
12122 constant:right.constant,
12127 binaryFn: function(left, op, right, isBranching) {
12128 var fn = OPERATORS[op];
12129 return extend(function $parseBinaryFn(self, locals) {
12130 return fn(self, locals, left, right);
12132 constant: left.constant && right.constant,
12133 inputs: !isBranching && [left, right]
12137 identifier: function() {
12138 var id = this.consume().text;
12140 //Continue reading each `.identifier` unless it is a method invocation
12141 while (this.peek('.') && this.peekAhead(1).identifier && !this.peekAhead(2, '(')) {
12142 id += this.consume().text + this.consume().text;
12145 return getterFn(id, this.options, this.text);
12148 constant: function() {
12149 var value = this.consume().value;
12151 return extend(function $parseConstant() {
12159 statements: function() {
12160 var statements = [];
12162 if (this.tokens.length > 0 && !this.peek('}', ')', ';', ']'))
12163 statements.push(this.filterChain());
12164 if (!this.expect(';')) {
12165 // optimize for the common case where there is only one statement.
12166 // TODO(size): maybe we should not support multiple statements?
12167 return (statements.length === 1)
12169 : function $parseStatements(self, locals) {
12171 for (var i = 0, ii = statements.length; i < ii; i++) {
12172 value = statements[i](self, locals);
12180 filterChain: function() {
12181 var left = this.expression();
12183 while ((token = this.expect('|'))) {
12184 left = this.filter(left);
12189 filter: function(inputFn) {
12190 var fn = this.$filter(this.consume().text);
12194 if (this.peek(':')) {
12196 args = []; // we can safely reuse the array
12197 while (this.expect(':')) {
12198 argsFn.push(this.expression());
12202 var inputs = [inputFn].concat(argsFn || []);
12204 return extend(function $parseFilter(self, locals) {
12205 var input = inputFn(self, locals);
12209 var i = argsFn.length;
12211 args[i + 1] = argsFn[i](self, locals);
12214 return fn.apply(undefined, args);
12219 constant: !fn.$stateful && inputs.every(isConstant),
12220 inputs: !fn.$stateful && inputs
12224 expression: function() {
12225 return this.assignment();
12228 assignment: function() {
12229 var left = this.ternary();
12232 if ((token = this.expect('='))) {
12233 if (!left.assign) {
12234 this.throwError('implies assignment but [' +
12235 this.text.substring(0, token.index) + '] can not be assigned to', token);
12237 right = this.ternary();
12238 return extend(function $parseAssignment(scope, locals) {
12239 return left.assign(scope, right(scope, locals), locals);
12241 inputs: [left, right]
12247 ternary: function() {
12248 var left = this.logicalOR();
12251 if ((token = this.expect('?'))) {
12252 middle = this.assignment();
12253 if (this.consume(':')) {
12254 var right = this.assignment();
12256 return extend(function $parseTernary(self, locals) {
12257 return left(self, locals) ? middle(self, locals) : right(self, locals);
12259 constant: left.constant && middle.constant && right.constant
12267 logicalOR: function() {
12268 var left = this.logicalAND();
12270 while ((token = this.expect('||'))) {
12271 left = this.binaryFn(left, token.text, this.logicalAND(), true);
12276 logicalAND: function() {
12277 var left = this.equality();
12279 while ((token = this.expect('&&'))) {
12280 left = this.binaryFn(left, token.text, this.equality(), true);
12285 equality: function() {
12286 var left = this.relational();
12288 while ((token = this.expect('==','!=','===','!=='))) {
12289 left = this.binaryFn(left, token.text, this.relational());
12294 relational: function() {
12295 var left = this.additive();
12297 while ((token = this.expect('<', '>', '<=', '>='))) {
12298 left = this.binaryFn(left, token.text, this.additive());
12303 additive: function() {
12304 var left = this.multiplicative();
12306 while ((token = this.expect('+','-'))) {
12307 left = this.binaryFn(left, token.text, this.multiplicative());
12312 multiplicative: function() {
12313 var left = this.unary();
12315 while ((token = this.expect('*','/','%'))) {
12316 left = this.binaryFn(left, token.text, this.unary());
12321 unary: function() {
12323 if (this.expect('+')) {
12324 return this.primary();
12325 } else if ((token = this.expect('-'))) {
12326 return this.binaryFn(Parser.ZERO, token.text, this.unary());
12327 } else if ((token = this.expect('!'))) {
12328 return this.unaryFn(token.text, this.unary());
12330 return this.primary();
12334 fieldAccess: function(object) {
12335 var getter = this.identifier();
12337 return extend(function $parseFieldAccess(scope, locals, self) {
12338 var o = self || object(scope, locals);
12339 return (o == null) ? undefined : getter(o);
12341 assign: function(scope, value, locals) {
12342 var o = object(scope, locals);
12343 if (!o) object.assign(scope, o = {}, locals);
12344 return getter.assign(o, value);
12349 objectIndex: function(obj) {
12350 var expression = this.text;
12352 var indexFn = this.expression();
12355 return extend(function $parseObjectIndex(self, locals) {
12356 var o = obj(self, locals),
12357 i = indexFn(self, locals),
12360 ensureSafeMemberName(i, expression);
12361 if (!o) return undefined;
12362 v = ensureSafeObject(o[i], expression);
12365 assign: function(self, value, locals) {
12366 var key = ensureSafeMemberName(indexFn(self, locals), expression);
12367 // prevent overwriting of Function.constructor which would break ensureSafeObject check
12368 var o = ensureSafeObject(obj(self, locals), expression);
12369 if (!o) obj.assign(self, o = {}, locals);
12370 return o[key] = value;
12375 functionCall: function(fnGetter, contextGetter) {
12377 if (this.peekToken().text !== ')') {
12379 argsFn.push(this.expression());
12380 } while (this.expect(','));
12384 var expressionText = this.text;
12385 // we can safely reuse the array across invocations
12386 var args = argsFn.length ? [] : null;
12388 return function $parseFunctionCall(scope, locals) {
12389 var context = contextGetter ? contextGetter(scope, locals) : isDefined(contextGetter) ? undefined : scope;
12390 var fn = fnGetter(scope, locals, context) || noop;
12393 var i = argsFn.length;
12395 args[i] = ensureSafeObject(argsFn[i](scope, locals), expressionText);
12399 ensureSafeObject(context, expressionText);
12400 ensureSafeFunction(fn, expressionText);
12402 // IE doesn't have apply for some native functions
12404 ? fn.apply(context, args)
12405 : fn(args[0], args[1], args[2], args[3], args[4]);
12408 // Free-up the memory (arguments of the last function call).
12412 return ensureSafeObject(v, expressionText);
12416 // This is used with json array declaration
12417 arrayDeclaration: function() {
12418 var elementFns = [];
12419 if (this.peekToken().text !== ']') {
12421 if (this.peek(']')) {
12422 // Support trailing commas per ES5.1.
12425 elementFns.push(this.expression());
12426 } while (this.expect(','));
12430 return extend(function $parseArrayLiteral(self, locals) {
12432 for (var i = 0, ii = elementFns.length; i < ii; i++) {
12433 array.push(elementFns[i](self, locals));
12438 constant: elementFns.every(isConstant),
12443 object: function() {
12444 var keys = [], valueFns = [];
12445 if (this.peekToken().text !== '}') {
12447 if (this.peek('}')) {
12448 // Support trailing commas per ES5.1.
12451 var token = this.consume();
12452 if (token.constant) {
12453 keys.push(token.value);
12454 } else if (token.identifier) {
12455 keys.push(token.text);
12457 this.throwError("invalid key", token);
12460 valueFns.push(this.expression());
12461 } while (this.expect(','));
12465 return extend(function $parseObjectLiteral(self, locals) {
12467 for (var i = 0, ii = valueFns.length; i < ii; i++) {
12468 object[keys[i]] = valueFns[i](self, locals);
12473 constant: valueFns.every(isConstant),
12480 //////////////////////////////////////////////////
12481 // Parser helper functions
12482 //////////////////////////////////////////////////
12484 function setter(obj, locals, path, setValue, fullExp) {
12485 ensureSafeObject(obj, fullExp);
12486 ensureSafeObject(locals, fullExp);
12488 var element = path.split('.'), key;
12489 for (var i = 0; element.length > 1; i++) {
12490 key = ensureSafeMemberName(element.shift(), fullExp);
12491 var propertyObj = (i === 0 && locals && locals[key]) || obj[key];
12492 if (!propertyObj) {
12494 obj[key] = propertyObj;
12496 obj = ensureSafeObject(propertyObj, fullExp);
12498 key = ensureSafeMemberName(element.shift(), fullExp);
12499 ensureSafeObject(obj[key], fullExp);
12500 obj[key] = setValue;
12504 var getterFnCacheDefault = createMap();
12505 var getterFnCacheExpensive = createMap();
12507 function isPossiblyDangerousMemberName(name) {
12508 return name == 'constructor';
12512 * Implementation of the "Black Hole" variant from:
12513 * - http://jsperf.com/angularjs-parse-getter/4
12514 * - http://jsperf.com/path-evaluation-simplified/7
12516 function cspSafeGetterFn(key0, key1, key2, key3, key4, fullExp, expensiveChecks) {
12517 ensureSafeMemberName(key0, fullExp);
12518 ensureSafeMemberName(key1, fullExp);
12519 ensureSafeMemberName(key2, fullExp);
12520 ensureSafeMemberName(key3, fullExp);
12521 ensureSafeMemberName(key4, fullExp);
12522 var eso = function(o) {
12523 return ensureSafeObject(o, fullExp);
12525 var eso0 = (expensiveChecks || isPossiblyDangerousMemberName(key0)) ? eso : identity;
12526 var eso1 = (expensiveChecks || isPossiblyDangerousMemberName(key1)) ? eso : identity;
12527 var eso2 = (expensiveChecks || isPossiblyDangerousMemberName(key2)) ? eso : identity;
12528 var eso3 = (expensiveChecks || isPossiblyDangerousMemberName(key3)) ? eso : identity;
12529 var eso4 = (expensiveChecks || isPossiblyDangerousMemberName(key4)) ? eso : identity;
12531 return function cspSafeGetter(scope, locals) {
12532 var pathVal = (locals && locals.hasOwnProperty(key0)) ? locals : scope;
12534 if (pathVal == null) return pathVal;
12535 pathVal = eso0(pathVal[key0]);
12537 if (!key1) return pathVal;
12538 if (pathVal == null) return undefined;
12539 pathVal = eso1(pathVal[key1]);
12541 if (!key2) return pathVal;
12542 if (pathVal == null) return undefined;
12543 pathVal = eso2(pathVal[key2]);
12545 if (!key3) return pathVal;
12546 if (pathVal == null) return undefined;
12547 pathVal = eso3(pathVal[key3]);
12549 if (!key4) return pathVal;
12550 if (pathVal == null) return undefined;
12551 pathVal = eso4(pathVal[key4]);
12557 function getterFnWithEnsureSafeObject(fn, fullExpression) {
12558 return function(s, l) {
12559 return fn(s, l, ensureSafeObject, fullExpression);
12563 function getterFn(path, options, fullExp) {
12564 var expensiveChecks = options.expensiveChecks;
12565 var getterFnCache = (expensiveChecks ? getterFnCacheExpensive : getterFnCacheDefault);
12566 var fn = getterFnCache[path];
12570 var pathKeys = path.split('.'),
12571 pathKeysLength = pathKeys.length;
12573 // http://jsperf.com/angularjs-parse-getter/6
12575 if (pathKeysLength < 6) {
12576 fn = cspSafeGetterFn(pathKeys[0], pathKeys[1], pathKeys[2], pathKeys[3], pathKeys[4], fullExp, expensiveChecks);
12578 fn = function cspSafeGetter(scope, locals) {
12581 val = cspSafeGetterFn(pathKeys[i++], pathKeys[i++], pathKeys[i++], pathKeys[i++],
12582 pathKeys[i++], fullExp, expensiveChecks)(scope, locals);
12584 locals = undefined; // clear after first iteration
12586 } while (i < pathKeysLength);
12592 if (expensiveChecks) {
12593 code += 's = eso(s, fe);\nl = eso(l, fe);\n';
12595 var needsEnsureSafeObject = expensiveChecks;
12596 forEach(pathKeys, function(key, index) {
12597 ensureSafeMemberName(key, fullExp);
12598 var lookupJs = (index
12599 // we simply dereference 's' on any .dot notation
12601 // but if we are first then we check locals first, and if so read it first
12602 : '((l&&l.hasOwnProperty("' + key + '"))?l:s)') + '.' + key;
12603 if (expensiveChecks || isPossiblyDangerousMemberName(key)) {
12604 lookupJs = 'eso(' + lookupJs + ', fe)';
12605 needsEnsureSafeObject = true;
12607 code += 'if(s == null) return undefined;\n' +
12608 's=' + lookupJs + ';\n';
12610 code += 'return s;';
12613 var evaledFnGetter = new Function('s', 'l', 'eso', 'fe', code); // s=scope, l=locals, eso=ensureSafeObject
12615 evaledFnGetter.toString = valueFn(code);
12616 if (needsEnsureSafeObject) {
12617 evaledFnGetter = getterFnWithEnsureSafeObject(evaledFnGetter, fullExp);
12619 fn = evaledFnGetter;
12622 fn.sharedGetter = true;
12623 fn.assign = function(self, value, locals) {
12624 return setter(self, locals, path, value, path);
12626 getterFnCache[path] = fn;
12630 var objectValueOf = Object.prototype.valueOf;
12632 function getValueOf(value) {
12633 return isFunction(value.valueOf) ? value.valueOf() : objectValueOf.call(value);
12636 ///////////////////////////////////
12645 * Converts Angular {@link guide/expression expression} into a function.
12648 * var getter = $parse('user.name');
12649 * var setter = getter.assign;
12650 * var context = {user:{name:'angular'}};
12651 * var locals = {user:{name:'local'}};
12653 * expect(getter(context)).toEqual('angular');
12654 * setter(context, 'newValue');
12655 * expect(context.user.name).toEqual('newValue');
12656 * expect(getter(context, locals)).toEqual('local');
12660 * @param {string} expression String expression to compile.
12661 * @returns {function(context, locals)} a function which represents the compiled expression:
12663 * * `context` – `{object}` – an object against which any expressions embedded in the strings
12664 * are evaluated against (typically a scope object).
12665 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
12668 * The returned function also has the following properties:
12669 * * `literal` – `{boolean}` – whether the expression's top-level node is a JavaScript
12671 * * `constant` – `{boolean}` – whether the expression is made entirely of JavaScript
12672 * constant literals.
12673 * * `assign` – `{?function(context, value)}` – if the expression is assignable, this will be
12674 * set to a function to change its value on the given context.
12681 * @name $parseProvider
12684 * `$parseProvider` can be used for configuring the default behavior of the {@link ng.$parse $parse}
12687 function $ParseProvider() {
12688 var cacheDefault = createMap();
12689 var cacheExpensive = createMap();
12693 this.$get = ['$filter', '$sniffer', function($filter, $sniffer) {
12694 var $parseOptions = {
12696 expensiveChecks: false
12698 $parseOptionsExpensive = {
12700 expensiveChecks: true
12703 function wrapSharedExpression(exp) {
12706 if (exp.sharedGetter) {
12707 wrapped = function $parseWrapper(self, locals) {
12708 return exp(self, locals);
12710 wrapped.literal = exp.literal;
12711 wrapped.constant = exp.constant;
12712 wrapped.assign = exp.assign;
12718 return function $parse(exp, interceptorFn, expensiveChecks) {
12719 var parsedExpression, oneTime, cacheKey;
12721 switch (typeof exp) {
12723 cacheKey = exp = exp.trim();
12725 var cache = (expensiveChecks ? cacheExpensive : cacheDefault);
12726 parsedExpression = cache[cacheKey];
12728 if (!parsedExpression) {
12729 if (exp.charAt(0) === ':' && exp.charAt(1) === ':') {
12731 exp = exp.substring(2);
12734 var parseOptions = expensiveChecks ? $parseOptionsExpensive : $parseOptions;
12735 var lexer = new Lexer(parseOptions);
12736 var parser = new Parser(lexer, $filter, parseOptions);
12737 parsedExpression = parser.parse(exp);
12739 if (parsedExpression.constant) {
12740 parsedExpression.$$watchDelegate = constantWatchDelegate;
12741 } else if (oneTime) {
12742 //oneTime is not part of the exp passed to the Parser so we may have to
12743 //wrap the parsedExpression before adding a $$watchDelegate
12744 parsedExpression = wrapSharedExpression(parsedExpression);
12745 parsedExpression.$$watchDelegate = parsedExpression.literal ?
12746 oneTimeLiteralWatchDelegate : oneTimeWatchDelegate;
12747 } else if (parsedExpression.inputs) {
12748 parsedExpression.$$watchDelegate = inputsWatchDelegate;
12751 cache[cacheKey] = parsedExpression;
12753 return addInterceptor(parsedExpression, interceptorFn);
12756 return addInterceptor(exp, interceptorFn);
12759 return addInterceptor(noop, interceptorFn);
12763 function collectExpressionInputs(inputs, list) {
12764 for (var i = 0, ii = inputs.length; i < ii; i++) {
12765 var input = inputs[i];
12766 if (!input.constant) {
12767 if (input.inputs) {
12768 collectExpressionInputs(input.inputs, list);
12769 } else if (list.indexOf(input) === -1) { // TODO(perf) can we do better?
12778 function expressionInputDirtyCheck(newValue, oldValueOfValue) {
12780 if (newValue == null || oldValueOfValue == null) { // null/undefined
12781 return newValue === oldValueOfValue;
12784 if (typeof newValue === 'object') {
12786 // attempt to convert the value to a primitive type
12787 // TODO(docs): add a note to docs that by implementing valueOf even objects and arrays can
12788 // be cheaply dirty-checked
12789 newValue = getValueOf(newValue);
12791 if (typeof newValue === 'object') {
12792 // objects/arrays are not supported - deep-watching them would be too expensive
12796 // fall-through to the primitive equality check
12800 return newValue === oldValueOfValue || (newValue !== newValue && oldValueOfValue !== oldValueOfValue);
12803 function inputsWatchDelegate(scope, listener, objectEquality, parsedExpression) {
12804 var inputExpressions = parsedExpression.$$inputs ||
12805 (parsedExpression.$$inputs = collectExpressionInputs(parsedExpression.inputs, []));
12809 if (inputExpressions.length === 1) {
12810 var oldInputValue = expressionInputDirtyCheck; // init to something unique so that equals check fails
12811 inputExpressions = inputExpressions[0];
12812 return scope.$watch(function expressionInputWatch(scope) {
12813 var newInputValue = inputExpressions(scope);
12814 if (!expressionInputDirtyCheck(newInputValue, oldInputValue)) {
12815 lastResult = parsedExpression(scope);
12816 oldInputValue = newInputValue && getValueOf(newInputValue);
12819 }, listener, objectEquality);
12822 var oldInputValueOfValues = [];
12823 for (var i = 0, ii = inputExpressions.length; i < ii; i++) {
12824 oldInputValueOfValues[i] = expressionInputDirtyCheck; // init to something unique so that equals check fails
12827 return scope.$watch(function expressionInputsWatch(scope) {
12828 var changed = false;
12830 for (var i = 0, ii = inputExpressions.length; i < ii; i++) {
12831 var newInputValue = inputExpressions[i](scope);
12832 if (changed || (changed = !expressionInputDirtyCheck(newInputValue, oldInputValueOfValues[i]))) {
12833 oldInputValueOfValues[i] = newInputValue && getValueOf(newInputValue);
12838 lastResult = parsedExpression(scope);
12842 }, listener, objectEquality);
12845 function oneTimeWatchDelegate(scope, listener, objectEquality, parsedExpression) {
12846 var unwatch, lastValue;
12847 return unwatch = scope.$watch(function oneTimeWatch(scope) {
12848 return parsedExpression(scope);
12849 }, function oneTimeListener(value, old, scope) {
12851 if (isFunction(listener)) {
12852 listener.apply(this, arguments);
12854 if (isDefined(value)) {
12855 scope.$$postDigest(function() {
12856 if (isDefined(lastValue)) {
12861 }, objectEquality);
12864 function oneTimeLiteralWatchDelegate(scope, listener, objectEquality, parsedExpression) {
12865 var unwatch, lastValue;
12866 return unwatch = scope.$watch(function oneTimeWatch(scope) {
12867 return parsedExpression(scope);
12868 }, function oneTimeListener(value, old, scope) {
12870 if (isFunction(listener)) {
12871 listener.call(this, value, old, scope);
12873 if (isAllDefined(value)) {
12874 scope.$$postDigest(function() {
12875 if (isAllDefined(lastValue)) unwatch();
12878 }, objectEquality);
12880 function isAllDefined(value) {
12881 var allDefined = true;
12882 forEach(value, function(val) {
12883 if (!isDefined(val)) allDefined = false;
12889 function constantWatchDelegate(scope, listener, objectEquality, parsedExpression) {
12891 return unwatch = scope.$watch(function constantWatch(scope) {
12892 return parsedExpression(scope);
12893 }, function constantListener(value, old, scope) {
12894 if (isFunction(listener)) {
12895 listener.apply(this, arguments);
12898 }, objectEquality);
12901 function addInterceptor(parsedExpression, interceptorFn) {
12902 if (!interceptorFn) return parsedExpression;
12903 var watchDelegate = parsedExpression.$$watchDelegate;
12906 watchDelegate !== oneTimeLiteralWatchDelegate &&
12907 watchDelegate !== oneTimeWatchDelegate;
12909 var fn = regularWatch ? function regularInterceptedExpression(scope, locals) {
12910 var value = parsedExpression(scope, locals);
12911 return interceptorFn(value, scope, locals);
12912 } : function oneTimeInterceptedExpression(scope, locals) {
12913 var value = parsedExpression(scope, locals);
12914 var result = interceptorFn(value, scope, locals);
12915 // we only return the interceptor's result if the
12916 // initial value is defined (for bind-once)
12917 return isDefined(value) ? result : value;
12920 // Propagate $$watchDelegates other then inputsWatchDelegate
12921 if (parsedExpression.$$watchDelegate &&
12922 parsedExpression.$$watchDelegate !== inputsWatchDelegate) {
12923 fn.$$watchDelegate = parsedExpression.$$watchDelegate;
12924 } else if (!interceptorFn.$stateful) {
12925 // If there is an interceptor, but no watchDelegate then treat the interceptor like
12926 // we treat filters - it is assumed to be a pure function unless flagged with $stateful
12927 fn.$$watchDelegate = inputsWatchDelegate;
12928 fn.inputs = [parsedExpression];
12939 * @requires $rootScope
12942 * A service that helps you run functions asynchronously, and use their return values (or exceptions)
12943 * when they are done processing.
12945 * This is an implementation of promises/deferred objects inspired by
12946 * [Kris Kowal's Q](https://github.com/kriskowal/q).
12948 * $q can be used in two fashions --- one which is more similar to Kris Kowal's Q or jQuery's Deferred
12949 * implementations, and the other which resembles ES6 promises to some degree.
12953 * The streamlined ES6 style promise is essentially just using $q as a constructor which takes a `resolver`
12954 * function as the first argument. This is similar to the native Promise implementation from ES6 Harmony,
12955 * see [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise).
12957 * While the constructor-style use is supported, not all of the supporting methods from ES6 Harmony promises are
12960 * It can be used like so:
12963 * // for the purpose of this example let's assume that variables `$q` and `okToGreet`
12964 * // are available in the current lexical scope (they could have been injected or passed in).
12966 * function asyncGreet(name) {
12967 * // perform some asynchronous operation, resolve or reject the promise when appropriate.
12968 * return $q(function(resolve, reject) {
12969 * setTimeout(function() {
12970 * if (okToGreet(name)) {
12971 * resolve('Hello, ' + name + '!');
12973 * reject('Greeting ' + name + ' is not allowed.');
12979 * var promise = asyncGreet('Robin Hood');
12980 * promise.then(function(greeting) {
12981 * alert('Success: ' + greeting);
12982 * }, function(reason) {
12983 * alert('Failed: ' + reason);
12987 * Note: progress/notify callbacks are not currently supported via the ES6-style interface.
12989 * However, the more traditional CommonJS-style usage is still available, and documented below.
12991 * [The CommonJS Promise proposal](http://wiki.commonjs.org/wiki/Promises) describes a promise as an
12992 * interface for interacting with an object that represents the result of an action that is
12993 * performed asynchronously, and may or may not be finished at any given point in time.
12995 * From the perspective of dealing with error handling, deferred and promise APIs are to
12996 * asynchronous programming what `try`, `catch` and `throw` keywords are to synchronous programming.
12999 * // for the purpose of this example let's assume that variables `$q` and `okToGreet`
13000 * // are available in the current lexical scope (they could have been injected or passed in).
13002 * function asyncGreet(name) {
13003 * var deferred = $q.defer();
13005 * setTimeout(function() {
13006 * deferred.notify('About to greet ' + name + '.');
13008 * if (okToGreet(name)) {
13009 * deferred.resolve('Hello, ' + name + '!');
13011 * deferred.reject('Greeting ' + name + ' is not allowed.');
13015 * return deferred.promise;
13018 * var promise = asyncGreet('Robin Hood');
13019 * promise.then(function(greeting) {
13020 * alert('Success: ' + greeting);
13021 * }, function(reason) {
13022 * alert('Failed: ' + reason);
13023 * }, function(update) {
13024 * alert('Got notification: ' + update);
13028 * At first it might not be obvious why this extra complexity is worth the trouble. The payoff
13029 * comes in the way of guarantees that promise and deferred APIs make, see
13030 * https://github.com/kriskowal/uncommonjs/blob/master/promises/specification.md.
13032 * Additionally the promise api allows for composition that is very hard to do with the
13033 * traditional callback ([CPS](http://en.wikipedia.org/wiki/Continuation-passing_style)) approach.
13034 * For more on this please see the [Q documentation](https://github.com/kriskowal/q) especially the
13035 * section on serial or parallel joining of promises.
13037 * # The Deferred API
13039 * A new instance of deferred is constructed by calling `$q.defer()`.
13041 * The purpose of the deferred object is to expose the associated Promise instance as well as APIs
13042 * that can be used for signaling the successful or unsuccessful completion, as well as the status
13047 * - `resolve(value)` – resolves the derived promise with the `value`. If the value is a rejection
13048 * constructed via `$q.reject`, the promise will be rejected instead.
13049 * - `reject(reason)` – rejects the derived promise with the `reason`. This is equivalent to
13050 * resolving it with a rejection constructed via `$q.reject`.
13051 * - `notify(value)` - provides updates on the status of the promise's execution. This may be called
13052 * multiple times before the promise is either resolved or rejected.
13056 * - promise – `{Promise}` – promise object associated with this deferred.
13059 * # The Promise API
13061 * A new promise instance is created when a deferred instance is created and can be retrieved by
13062 * calling `deferred.promise`.
13064 * The purpose of the promise object is to allow for interested parties to get access to the result
13065 * of the deferred task when it completes.
13069 * - `then(successCallback, errorCallback, notifyCallback)` – regardless of when the promise was or
13070 * will be resolved or rejected, `then` calls one of the success or error callbacks asynchronously
13071 * as soon as the result is available. The callbacks are called with a single argument: the result
13072 * or rejection reason. Additionally, the notify callback may be called zero or more times to
13073 * provide a progress indication, before the promise is resolved or rejected.
13075 * This method *returns a new promise* which is resolved or rejected via the return value of the
13076 * `successCallback`, `errorCallback`. It also notifies via the return value of the
13077 * `notifyCallback` method. The promise cannot be resolved or rejected from the notifyCallback
13080 * - `catch(errorCallback)` – shorthand for `promise.then(null, errorCallback)`
13082 * - `finally(callback, notifyCallback)` – allows you to observe either the fulfillment or rejection of a promise,
13083 * but to do so without modifying the final value. This is useful to release resources or do some
13084 * clean-up that needs to be done whether the promise was rejected or resolved. See the [full
13085 * specification](https://github.com/kriskowal/q/wiki/API-Reference#promisefinallycallback) for
13086 * more information.
13088 * # Chaining promises
13090 * Because calling the `then` method of a promise returns a new derived promise, it is easily
13091 * possible to create a chain of promises:
13094 * promiseB = promiseA.then(function(result) {
13095 * return result + 1;
13098 * // promiseB will be resolved immediately after promiseA is resolved and its value
13099 * // will be the result of promiseA incremented by 1
13102 * It is possible to create chains of any length and since a promise can be resolved with another
13103 * promise (which will defer its resolution further), it is possible to pause/defer resolution of
13104 * the promises at any point in the chain. This makes it possible to implement powerful APIs like
13105 * $http's response interceptors.
13108 * # Differences between Kris Kowal's Q and $q
13110 * There are two main differences:
13112 * - $q is integrated with the {@link ng.$rootScope.Scope} Scope model observation
13113 * mechanism in angular, which means faster propagation of resolution or rejection into your
13114 * models and avoiding unnecessary browser repaints, which would result in flickering UI.
13115 * - Q has many more features than $q, but that comes at a cost of bytes. $q is tiny, but contains
13116 * all the important functionality needed for common async tasks.
13121 * it('should simulate promise', inject(function($q, $rootScope) {
13122 * var deferred = $q.defer();
13123 * var promise = deferred.promise;
13124 * var resolvedValue;
13126 * promise.then(function(value) { resolvedValue = value; });
13127 * expect(resolvedValue).toBeUndefined();
13129 * // Simulate resolving of promise
13130 * deferred.resolve(123);
13131 * // Note that the 'then' function does not get called synchronously.
13132 * // This is because we want the promise API to always be async, whether or not
13133 * // it got called synchronously or asynchronously.
13134 * expect(resolvedValue).toBeUndefined();
13136 * // Propagate promise resolution to 'then' functions using $apply().
13137 * $rootScope.$apply();
13138 * expect(resolvedValue).toEqual(123);
13142 * @param {function(function, function)} resolver Function which is responsible for resolving or
13143 * rejecting the newly created promise. The first parameter is a function which resolves the
13144 * promise, the second parameter is a function which rejects the promise.
13146 * @returns {Promise} The newly created promise.
13148 function $QProvider() {
13150 this.$get = ['$rootScope', '$exceptionHandler', function($rootScope, $exceptionHandler) {
13151 return qFactory(function(callback) {
13152 $rootScope.$evalAsync(callback);
13153 }, $exceptionHandler);
13157 function $$QProvider() {
13158 this.$get = ['$browser', '$exceptionHandler', function($browser, $exceptionHandler) {
13159 return qFactory(function(callback) {
13160 $browser.defer(callback);
13161 }, $exceptionHandler);
13166 * Constructs a promise manager.
13168 * @param {function(function)} nextTick Function for executing functions in the next turn.
13169 * @param {function(...*)} exceptionHandler Function into which unexpected exceptions are passed for
13170 * debugging purposes.
13171 * @returns {object} Promise manager.
13173 function qFactory(nextTick, exceptionHandler) {
13174 var $qMinErr = minErr('$q', TypeError);
13175 function callOnce(self, resolveFn, rejectFn) {
13176 var called = false;
13177 function wrap(fn) {
13178 return function(value) {
13179 if (called) return;
13181 fn.call(self, value);
13185 return [wrap(resolveFn), wrap(rejectFn)];
13190 * @name ng.$q#defer
13194 * Creates a `Deferred` object which represents a task which will finish in the future.
13196 * @returns {Deferred} Returns a new instance of deferred.
13198 var defer = function() {
13199 return new Deferred();
13202 function Promise() {
13203 this.$$state = { status: 0 };
13206 Promise.prototype = {
13207 then: function(onFulfilled, onRejected, progressBack) {
13208 var result = new Deferred();
13210 this.$$state.pending = this.$$state.pending || [];
13211 this.$$state.pending.push([result, onFulfilled, onRejected, progressBack]);
13212 if (this.$$state.status > 0) scheduleProcessQueue(this.$$state);
13214 return result.promise;
13217 "catch": function(callback) {
13218 return this.then(null, callback);
13221 "finally": function(callback, progressBack) {
13222 return this.then(function(value) {
13223 return handleCallback(value, true, callback);
13224 }, function(error) {
13225 return handleCallback(error, false, callback);
13230 //Faster, more basic than angular.bind http://jsperf.com/angular-bind-vs-custom-vs-native
13231 function simpleBind(context, fn) {
13232 return function(value) {
13233 fn.call(context, value);
13237 function processQueue(state) {
13238 var fn, promise, pending;
13240 pending = state.pending;
13241 state.processScheduled = false;
13242 state.pending = undefined;
13243 for (var i = 0, ii = pending.length; i < ii; ++i) {
13244 promise = pending[i][0];
13245 fn = pending[i][state.status];
13247 if (isFunction(fn)) {
13248 promise.resolve(fn(state.value));
13249 } else if (state.status === 1) {
13250 promise.resolve(state.value);
13252 promise.reject(state.value);
13256 exceptionHandler(e);
13261 function scheduleProcessQueue(state) {
13262 if (state.processScheduled || !state.pending) return;
13263 state.processScheduled = true;
13264 nextTick(function() { processQueue(state); });
13267 function Deferred() {
13268 this.promise = new Promise();
13269 //Necessary to support unbound execution :/
13270 this.resolve = simpleBind(this, this.resolve);
13271 this.reject = simpleBind(this, this.reject);
13272 this.notify = simpleBind(this, this.notify);
13275 Deferred.prototype = {
13276 resolve: function(val) {
13277 if (this.promise.$$state.status) return;
13278 if (val === this.promise) {
13279 this.$$reject($qMinErr(
13281 "Expected promise to be resolved with value other than itself '{0}'",
13284 this.$$resolve(val);
13289 $$resolve: function(val) {
13292 fns = callOnce(this, this.$$resolve, this.$$reject);
13294 if ((isObject(val) || isFunction(val))) then = val && val.then;
13295 if (isFunction(then)) {
13296 this.promise.$$state.status = -1;
13297 then.call(val, fns[0], fns[1], this.notify);
13299 this.promise.$$state.value = val;
13300 this.promise.$$state.status = 1;
13301 scheduleProcessQueue(this.promise.$$state);
13305 exceptionHandler(e);
13309 reject: function(reason) {
13310 if (this.promise.$$state.status) return;
13311 this.$$reject(reason);
13314 $$reject: function(reason) {
13315 this.promise.$$state.value = reason;
13316 this.promise.$$state.status = 2;
13317 scheduleProcessQueue(this.promise.$$state);
13320 notify: function(progress) {
13321 var callbacks = this.promise.$$state.pending;
13323 if ((this.promise.$$state.status <= 0) && callbacks && callbacks.length) {
13324 nextTick(function() {
13325 var callback, result;
13326 for (var i = 0, ii = callbacks.length; i < ii; i++) {
13327 result = callbacks[i][0];
13328 callback = callbacks[i][3];
13330 result.notify(isFunction(callback) ? callback(progress) : progress);
13332 exceptionHandler(e);
13346 * Creates a promise that is resolved as rejected with the specified `reason`. This api should be
13347 * used to forward rejection in a chain of promises. If you are dealing with the last promise in
13348 * a promise chain, you don't need to worry about it.
13350 * When comparing deferreds/promises to the familiar behavior of try/catch/throw, think of
13351 * `reject` as the `throw` keyword in JavaScript. This also means that if you "catch" an error via
13352 * a promise error callback and you want to forward the error to the promise derived from the
13353 * current promise, you have to "rethrow" the error by returning a rejection constructed via
13357 * promiseB = promiseA.then(function(result) {
13358 * // success: do something and resolve promiseB
13359 * // with the old or a new result
13361 * }, function(reason) {
13362 * // error: handle the error if possible and
13363 * // resolve promiseB with newPromiseOrValue,
13364 * // otherwise forward the rejection to promiseB
13365 * if (canHandle(reason)) {
13366 * // handle the error and recover
13367 * return newPromiseOrValue;
13369 * return $q.reject(reason);
13373 * @param {*} reason Constant, message, exception or an object representing the rejection reason.
13374 * @returns {Promise} Returns a promise that was already resolved as rejected with the `reason`.
13376 var reject = function(reason) {
13377 var result = new Deferred();
13378 result.reject(reason);
13379 return result.promise;
13382 var makePromise = function makePromise(value, resolved) {
13383 var result = new Deferred();
13385 result.resolve(value);
13387 result.reject(value);
13389 return result.promise;
13392 var handleCallback = function handleCallback(value, isResolved, callback) {
13393 var callbackOutput = null;
13395 if (isFunction(callback)) callbackOutput = callback();
13397 return makePromise(e, false);
13399 if (isPromiseLike(callbackOutput)) {
13400 return callbackOutput.then(function() {
13401 return makePromise(value, isResolved);
13402 }, function(error) {
13403 return makePromise(error, false);
13406 return makePromise(value, isResolved);
13416 * Wraps an object that might be a value or a (3rd party) then-able promise into a $q promise.
13417 * This is useful when you are dealing with an object that might or might not be a promise, or if
13418 * the promise comes from a source that can't be trusted.
13420 * @param {*} value Value or a promise
13421 * @returns {Promise} Returns a promise of the passed value or promise
13425 var when = function(value, callback, errback, progressBack) {
13426 var result = new Deferred();
13427 result.resolve(value);
13428 return result.promise.then(callback, errback, progressBack);
13437 * Combines multiple promises into a single promise that is resolved when all of the input
13438 * promises are resolved.
13440 * @param {Array.<Promise>|Object.<Promise>} promises An array or hash of promises.
13441 * @returns {Promise} Returns a single promise that will be resolved with an array/hash of values,
13442 * each value corresponding to the promise at the same index/key in the `promises` array/hash.
13443 * If any of the promises is resolved with a rejection, this resulting promise will be rejected
13444 * with the same rejection value.
13447 function all(promises) {
13448 var deferred = new Deferred(),
13450 results = isArray(promises) ? [] : {};
13452 forEach(promises, function(promise, key) {
13454 when(promise).then(function(value) {
13455 if (results.hasOwnProperty(key)) return;
13456 results[key] = value;
13457 if (!(--counter)) deferred.resolve(results);
13458 }, function(reason) {
13459 if (results.hasOwnProperty(key)) return;
13460 deferred.reject(reason);
13464 if (counter === 0) {
13465 deferred.resolve(results);
13468 return deferred.promise;
13471 var $Q = function Q(resolver) {
13472 if (!isFunction(resolver)) {
13473 throw $qMinErr('norslvr', "Expected resolverFn, got '{0}'", resolver);
13476 if (!(this instanceof Q)) {
13477 // More useful when $Q is the Promise itself.
13478 return new Q(resolver);
13481 var deferred = new Deferred();
13483 function resolveFn(value) {
13484 deferred.resolve(value);
13487 function rejectFn(reason) {
13488 deferred.reject(reason);
13491 resolver(resolveFn, rejectFn);
13493 return deferred.promise;
13497 $Q.reject = reject;
13504 function $$RAFProvider() { //rAF
13505 this.$get = ['$window', '$timeout', function($window, $timeout) {
13506 var requestAnimationFrame = $window.requestAnimationFrame ||
13507 $window.webkitRequestAnimationFrame;
13509 var cancelAnimationFrame = $window.cancelAnimationFrame ||
13510 $window.webkitCancelAnimationFrame ||
13511 $window.webkitCancelRequestAnimationFrame;
13513 var rafSupported = !!requestAnimationFrame;
13514 var raf = rafSupported
13516 var id = requestAnimationFrame(fn);
13517 return function() {
13518 cancelAnimationFrame(id);
13522 var timer = $timeout(fn, 16.66, false); // 1000 / 60 = 16.666
13523 return function() {
13524 $timeout.cancel(timer);
13528 raf.supported = rafSupported;
13537 * The design decisions behind the scope are heavily favored for speed and memory consumption.
13539 * The typical use of scope is to watch the expressions, which most of the time return the same
13540 * value as last time so we optimize the operation.
13542 * Closures construction is expensive in terms of speed as well as memory:
13543 * - No closures, instead use prototypical inheritance for API
13544 * - Internal state needs to be stored on scope directly, which means that private state is
13545 * exposed as $$____ properties
13547 * Loop operations are optimized by using while(count--) { ... }
13548 * - this means that in order to keep the same order of execution as addition we have to add
13549 * items to the array at the beginning (unshift) instead of at the end (push)
13551 * Child scopes are created and removed often
13552 * - Using an array would be slow since inserts in middle are expensive so we use linked list
13554 * There are few watches then a lot of observers. This is why you don't want the observer to be
13555 * implemented in the same way as watch. Watch requires return of initialization function which
13556 * are expensive to construct.
13562 * @name $rootScopeProvider
13565 * Provider for the $rootScope service.
13570 * @name $rootScopeProvider#digestTtl
13573 * Sets the number of `$digest` iterations the scope should attempt to execute before giving up and
13574 * assuming that the model is unstable.
13576 * The current default is 10 iterations.
13578 * In complex applications it's possible that the dependencies between `$watch`s will result in
13579 * several digest iterations. However if an application needs more than the default 10 digest
13580 * iterations for its model to stabilize then you should investigate what is causing the model to
13581 * continuously change during the digest.
13583 * Increasing the TTL could have performance implications, so you should not change it without
13584 * proper justification.
13586 * @param {number} limit The number of digest iterations.
13595 * Every application has a single root {@link ng.$rootScope.Scope scope}.
13596 * All other scopes are descendant scopes of the root scope. Scopes provide separation
13597 * between the model and the view, via a mechanism for watching the model for changes.
13598 * They also provide an event emission/broadcast and subscription facility. See the
13599 * {@link guide/scope developer guide on scopes}.
13601 function $RootScopeProvider() {
13603 var $rootScopeMinErr = minErr('$rootScope');
13604 var lastDirtyWatch = null;
13605 var applyAsyncId = null;
13607 this.digestTtl = function(value) {
13608 if (arguments.length) {
13614 function createChildScopeClass(parent) {
13615 function ChildScope() {
13616 this.$$watchers = this.$$nextSibling =
13617 this.$$childHead = this.$$childTail = null;
13618 this.$$listeners = {};
13619 this.$$listenerCount = {};
13620 this.$$watchersCount = 0;
13621 this.$id = nextUid();
13622 this.$$ChildScope = null;
13624 ChildScope.prototype = parent;
13628 this.$get = ['$injector', '$exceptionHandler', '$parse', '$browser',
13629 function($injector, $exceptionHandler, $parse, $browser) {
13631 function destroyChildScope($event) {
13632 $event.currentScope.$$destroyed = true;
13637 * @name $rootScope.Scope
13640 * A root scope can be retrieved using the {@link ng.$rootScope $rootScope} key from the
13641 * {@link auto.$injector $injector}. Child scopes are created using the
13642 * {@link ng.$rootScope.Scope#$new $new()} method. (Most scopes are created automatically when
13643 * compiled HTML template is executed.)
13645 * Here is a simple scope snippet to show how you can interact with the scope.
13647 * <file src="./test/ng/rootScopeSpec.js" tag="docs1" />
13651 * A scope can inherit from a parent scope, as in this example:
13653 var parent = $rootScope;
13654 var child = parent.$new();
13656 parent.salutation = "Hello";
13657 expect(child.salutation).toEqual('Hello');
13659 child.salutation = "Welcome";
13660 expect(child.salutation).toEqual('Welcome');
13661 expect(parent.salutation).toEqual('Hello');
13664 * When interacting with `Scope` in tests, additional helper methods are available on the
13665 * instances of `Scope` type. See {@link ngMock.$rootScope.Scope ngMock Scope} for additional
13669 * @param {Object.<string, function()>=} providers Map of service factory which need to be
13670 * provided for the current scope. Defaults to {@link ng}.
13671 * @param {Object.<string, *>=} instanceCache Provides pre-instantiated services which should
13672 * append/override services provided by `providers`. This is handy
13673 * when unit-testing and having the need to override a default
13675 * @returns {Object} Newly created scope.
13679 this.$id = nextUid();
13680 this.$$phase = this.$parent = this.$$watchers =
13681 this.$$nextSibling = this.$$prevSibling =
13682 this.$$childHead = this.$$childTail = null;
13684 this.$$destroyed = false;
13685 this.$$listeners = {};
13686 this.$$listenerCount = {};
13687 this.$$isolateBindings = null;
13692 * @name $rootScope.Scope#$id
13695 * Unique scope ID (monotonically increasing) useful for debugging.
13700 * @name $rootScope.Scope#$parent
13703 * Reference to the parent scope.
13708 * @name $rootScope.Scope#$root
13711 * Reference to the root scope.
13714 Scope.prototype = {
13715 constructor: Scope,
13718 * @name $rootScope.Scope#$new
13722 * Creates a new child {@link ng.$rootScope.Scope scope}.
13724 * The parent scope will propagate the {@link ng.$rootScope.Scope#$digest $digest()} event.
13725 * The scope can be removed from the scope hierarchy using {@link ng.$rootScope.Scope#$destroy $destroy()}.
13727 * {@link ng.$rootScope.Scope#$destroy $destroy()} must be called on a scope when it is
13728 * desired for the scope and its child scopes to be permanently detached from the parent and
13729 * thus stop participating in model change detection and listener notification by invoking.
13731 * @param {boolean} isolate If true, then the scope does not prototypically inherit from the
13732 * parent scope. The scope is isolated, as it can not see parent scope properties.
13733 * When creating widgets, it is useful for the widget to not accidentally read parent
13736 * @param {Scope} [parent=this] The {@link ng.$rootScope.Scope `Scope`} that will be the `$parent`
13737 * of the newly created scope. Defaults to `this` scope if not provided.
13738 * This is used when creating a transclude scope to correctly place it
13739 * in the scope hierarchy while maintaining the correct prototypical
13742 * @returns {Object} The newly created child scope.
13745 $new: function(isolate, parent) {
13748 parent = parent || this;
13751 child = new Scope();
13752 child.$root = this.$root;
13754 // Only create a child scope class if somebody asks for one,
13755 // but cache it to allow the VM to optimize lookups.
13756 if (!this.$$ChildScope) {
13757 this.$$ChildScope = createChildScopeClass(this);
13759 child = new this.$$ChildScope();
13761 child.$parent = parent;
13762 child.$$prevSibling = parent.$$childTail;
13763 if (parent.$$childHead) {
13764 parent.$$childTail.$$nextSibling = child;
13765 parent.$$childTail = child;
13767 parent.$$childHead = parent.$$childTail = child;
13770 // When the new scope is not isolated or we inherit from `this`, and
13771 // the parent scope is destroyed, the property `$$destroyed` is inherited
13772 // prototypically. In all other cases, this property needs to be set
13773 // when the parent scope is destroyed.
13774 // The listener needs to be added after the parent is set
13775 if (isolate || parent != this) child.$on('$destroy', destroyChildScope);
13782 * @name $rootScope.Scope#$watch
13786 * Registers a `listener` callback to be executed whenever the `watchExpression` changes.
13788 * - The `watchExpression` is called on every call to {@link ng.$rootScope.Scope#$digest
13789 * $digest()} and should return the value that will be watched. (Since
13790 * {@link ng.$rootScope.Scope#$digest $digest()} reruns when it detects changes the
13791 * `watchExpression` can execute multiple times per
13792 * {@link ng.$rootScope.Scope#$digest $digest()} and should be idempotent.)
13793 * - The `listener` is called only when the value from the current `watchExpression` and the
13794 * previous call to `watchExpression` are not equal (with the exception of the initial run,
13795 * see below). Inequality is determined according to reference inequality,
13796 * [strict comparison](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators)
13797 * via the `!==` Javascript operator, unless `objectEquality == true`
13799 * - When `objectEquality == true`, inequality of the `watchExpression` is determined
13800 * according to the {@link angular.equals} function. To save the value of the object for
13801 * later comparison, the {@link angular.copy} function is used. This therefore means that
13802 * watching complex objects will have adverse memory and performance implications.
13803 * - The watch `listener` may change the model, which may trigger other `listener`s to fire.
13804 * This is achieved by rerunning the watchers until no changes are detected. The rerun
13805 * iteration limit is 10 to prevent an infinite loop deadlock.
13808 * If you want to be notified whenever {@link ng.$rootScope.Scope#$digest $digest} is called,
13809 * you can register a `watchExpression` function with no `listener`. (Since `watchExpression`
13810 * can execute multiple times per {@link ng.$rootScope.Scope#$digest $digest} cycle when a
13811 * change is detected, be prepared for multiple calls to your listener.)
13813 * After a watcher is registered with the scope, the `listener` fn is called asynchronously
13814 * (via {@link ng.$rootScope.Scope#$evalAsync $evalAsync}) to initialize the
13815 * watcher. In rare cases, this is undesirable because the listener is called when the result
13816 * of `watchExpression` didn't change. To detect this scenario within the `listener` fn, you
13817 * can compare the `newVal` and `oldVal`. If these two values are identical (`===`) then the
13818 * listener was called due to initialization.
13824 // let's assume that scope was dependency injected as the $rootScope
13825 var scope = $rootScope;
13826 scope.name = 'misko';
13829 expect(scope.counter).toEqual(0);
13830 scope.$watch('name', function(newValue, oldValue) {
13831 scope.counter = scope.counter + 1;
13833 expect(scope.counter).toEqual(0);
13836 // the listener is always called during the first $digest loop after it was registered
13837 expect(scope.counter).toEqual(1);
13840 // but now it will not be called unless the value changes
13841 expect(scope.counter).toEqual(1);
13843 scope.name = 'adam';
13845 expect(scope.counter).toEqual(2);
13849 // Using a function as a watchExpression
13851 scope.foodCounter = 0;
13852 expect(scope.foodCounter).toEqual(0);
13854 // This function returns the value being watched. It is called for each turn of the $digest loop
13855 function() { return food; },
13856 // This is the change listener, called when the value returned from the above function changes
13857 function(newValue, oldValue) {
13858 if ( newValue !== oldValue ) {
13859 // Only increment the counter if the value changed
13860 scope.foodCounter = scope.foodCounter + 1;
13864 // No digest has been run so the counter will be zero
13865 expect(scope.foodCounter).toEqual(0);
13867 // Run the digest but since food has not changed count will still be zero
13869 expect(scope.foodCounter).toEqual(0);
13871 // Update food and run digest. Now the counter will increment
13872 food = 'cheeseburger';
13874 expect(scope.foodCounter).toEqual(1);
13880 * @param {(function()|string)} watchExpression Expression that is evaluated on each
13881 * {@link ng.$rootScope.Scope#$digest $digest} cycle. A change in the return value triggers
13882 * a call to the `listener`.
13884 * - `string`: Evaluated as {@link guide/expression expression}
13885 * - `function(scope)`: called with current `scope` as a parameter.
13886 * @param {function(newVal, oldVal, scope)} listener Callback called whenever the value
13887 * of `watchExpression` changes.
13889 * - `newVal` contains the current value of the `watchExpression`
13890 * - `oldVal` contains the previous value of the `watchExpression`
13891 * - `scope` refers to the current scope
13892 * @param {boolean=} objectEquality Compare for object equality using {@link angular.equals} instead of
13893 * comparing for reference equality.
13894 * @returns {function()} Returns a deregistration function for this listener.
13896 $watch: function(watchExp, listener, objectEquality) {
13897 var get = $parse(watchExp);
13899 if (get.$$watchDelegate) {
13900 return get.$$watchDelegate(this, listener, objectEquality, get);
13903 array = scope.$$watchers,
13906 last: initWatchVal,
13909 eq: !!objectEquality
13912 lastDirtyWatch = null;
13914 if (!isFunction(listener)) {
13919 array = scope.$$watchers = [];
13921 // we use unshift since we use a while loop in $digest for speed.
13922 // the while loop reads in reverse order.
13923 array.unshift(watcher);
13925 return function deregisterWatch() {
13926 arrayRemove(array, watcher);
13927 lastDirtyWatch = null;
13933 * @name $rootScope.Scope#$watchGroup
13937 * A variant of {@link ng.$rootScope.Scope#$watch $watch()} where it watches an array of `watchExpressions`.
13938 * If any one expression in the collection changes the `listener` is executed.
13940 * - The items in the `watchExpressions` array are observed via standard $watch operation and are examined on every
13941 * call to $digest() to see if any items changes.
13942 * - The `listener` is called whenever any expression in the `watchExpressions` array changes.
13944 * @param {Array.<string|Function(scope)>} watchExpressions Array of expressions that will be individually
13945 * watched using {@link ng.$rootScope.Scope#$watch $watch()}
13947 * @param {function(newValues, oldValues, scope)} listener Callback called whenever the return value of any
13948 * expression in `watchExpressions` changes
13949 * The `newValues` array contains the current values of the `watchExpressions`, with the indexes matching
13950 * those of `watchExpression`
13951 * and the `oldValues` array contains the previous values of the `watchExpressions`, with the indexes matching
13952 * those of `watchExpression`
13953 * The `scope` refers to the current scope.
13954 * @returns {function()} Returns a de-registration function for all listeners.
13956 $watchGroup: function(watchExpressions, listener) {
13957 var oldValues = new Array(watchExpressions.length);
13958 var newValues = new Array(watchExpressions.length);
13959 var deregisterFns = [];
13961 var changeReactionScheduled = false;
13962 var firstRun = true;
13964 if (!watchExpressions.length) {
13965 // No expressions means we call the listener ASAP
13966 var shouldCall = true;
13967 self.$evalAsync(function() {
13968 if (shouldCall) listener(newValues, newValues, self);
13970 return function deregisterWatchGroup() {
13971 shouldCall = false;
13975 if (watchExpressions.length === 1) {
13976 // Special case size of one
13977 return this.$watch(watchExpressions[0], function watchGroupAction(value, oldValue, scope) {
13978 newValues[0] = value;
13979 oldValues[0] = oldValue;
13980 listener(newValues, (value === oldValue) ? newValues : oldValues, scope);
13984 forEach(watchExpressions, function(expr, i) {
13985 var unwatchFn = self.$watch(expr, function watchGroupSubAction(value, oldValue) {
13986 newValues[i] = value;
13987 oldValues[i] = oldValue;
13988 if (!changeReactionScheduled) {
13989 changeReactionScheduled = true;
13990 self.$evalAsync(watchGroupAction);
13993 deregisterFns.push(unwatchFn);
13996 function watchGroupAction() {
13997 changeReactionScheduled = false;
14001 listener(newValues, newValues, self);
14003 listener(newValues, oldValues, self);
14007 return function deregisterWatchGroup() {
14008 while (deregisterFns.length) {
14009 deregisterFns.shift()();
14017 * @name $rootScope.Scope#$watchCollection
14021 * Shallow watches the properties of an object and fires whenever any of the properties change
14022 * (for arrays, this implies watching the array items; for object maps, this implies watching
14023 * the properties). If a change is detected, the `listener` callback is fired.
14025 * - The `obj` collection is observed via standard $watch operation and is examined on every
14026 * call to $digest() to see if any items have been added, removed, or moved.
14027 * - The `listener` is called whenever anything within the `obj` has changed. Examples include
14028 * adding, removing, and moving items belonging to an object or array.
14033 $scope.names = ['igor', 'matias', 'misko', 'james'];
14034 $scope.dataCount = 4;
14036 $scope.$watchCollection('names', function(newNames, oldNames) {
14037 $scope.dataCount = newNames.length;
14040 expect($scope.dataCount).toEqual(4);
14043 //still at 4 ... no changes
14044 expect($scope.dataCount).toEqual(4);
14046 $scope.names.pop();
14049 //now there's been a change
14050 expect($scope.dataCount).toEqual(3);
14054 * @param {string|function(scope)} obj Evaluated as {@link guide/expression expression}. The
14055 * expression value should evaluate to an object or an array which is observed on each
14056 * {@link ng.$rootScope.Scope#$digest $digest} cycle. Any shallow change within the
14057 * collection will trigger a call to the `listener`.
14059 * @param {function(newCollection, oldCollection, scope)} listener a callback function called
14060 * when a change is detected.
14061 * - The `newCollection` object is the newly modified data obtained from the `obj` expression
14062 * - The `oldCollection` object is a copy of the former collection data.
14063 * Due to performance considerations, the`oldCollection` value is computed only if the
14064 * `listener` function declares two or more arguments.
14065 * - The `scope` argument refers to the current scope.
14067 * @returns {function()} Returns a de-registration function for this listener. When the
14068 * de-registration function is executed, the internal watch operation is terminated.
14070 $watchCollection: function(obj, listener) {
14071 $watchCollectionInterceptor.$stateful = true;
14074 // the current value, updated on each dirty-check run
14076 // a shallow copy of the newValue from the last dirty-check run,
14077 // updated to match newValue during dirty-check run
14079 // a shallow copy of the newValue from when the last change happened
14081 // only track veryOldValue if the listener is asking for it
14082 var trackVeryOldValue = (listener.length > 1);
14083 var changeDetected = 0;
14084 var changeDetector = $parse(obj, $watchCollectionInterceptor);
14085 var internalArray = [];
14086 var internalObject = {};
14087 var initRun = true;
14090 function $watchCollectionInterceptor(_value) {
14092 var newLength, key, bothNaN, newItem, oldItem;
14094 // If the new value is undefined, then return undefined as the watch may be a one-time watch
14095 if (isUndefined(newValue)) return;
14097 if (!isObject(newValue)) { // if primitive
14098 if (oldValue !== newValue) {
14099 oldValue = newValue;
14102 } else if (isArrayLike(newValue)) {
14103 if (oldValue !== internalArray) {
14104 // we are transitioning from something which was not an array into array.
14105 oldValue = internalArray;
14106 oldLength = oldValue.length = 0;
14110 newLength = newValue.length;
14112 if (oldLength !== newLength) {
14113 // if lengths do not match we need to trigger change notification
14115 oldValue.length = oldLength = newLength;
14117 // copy the items to oldValue and look for changes.
14118 for (var i = 0; i < newLength; i++) {
14119 oldItem = oldValue[i];
14120 newItem = newValue[i];
14122 bothNaN = (oldItem !== oldItem) && (newItem !== newItem);
14123 if (!bothNaN && (oldItem !== newItem)) {
14125 oldValue[i] = newItem;
14129 if (oldValue !== internalObject) {
14130 // we are transitioning from something which was not an object into object.
14131 oldValue = internalObject = {};
14135 // copy the items to oldValue and look for changes.
14137 for (key in newValue) {
14138 if (newValue.hasOwnProperty(key)) {
14140 newItem = newValue[key];
14141 oldItem = oldValue[key];
14143 if (key in oldValue) {
14144 bothNaN = (oldItem !== oldItem) && (newItem !== newItem);
14145 if (!bothNaN && (oldItem !== newItem)) {
14147 oldValue[key] = newItem;
14151 oldValue[key] = newItem;
14156 if (oldLength > newLength) {
14157 // we used to have more keys, need to find them and destroy them.
14159 for (key in oldValue) {
14160 if (!newValue.hasOwnProperty(key)) {
14162 delete oldValue[key];
14167 return changeDetected;
14170 function $watchCollectionAction() {
14173 listener(newValue, newValue, self);
14175 listener(newValue, veryOldValue, self);
14178 // make a copy for the next time a collection is changed
14179 if (trackVeryOldValue) {
14180 if (!isObject(newValue)) {
14182 veryOldValue = newValue;
14183 } else if (isArrayLike(newValue)) {
14184 veryOldValue = new Array(newValue.length);
14185 for (var i = 0; i < newValue.length; i++) {
14186 veryOldValue[i] = newValue[i];
14188 } else { // if object
14190 for (var key in newValue) {
14191 if (hasOwnProperty.call(newValue, key)) {
14192 veryOldValue[key] = newValue[key];
14199 return this.$watch(changeDetector, $watchCollectionAction);
14204 * @name $rootScope.Scope#$digest
14208 * Processes all of the {@link ng.$rootScope.Scope#$watch watchers} of the current scope and
14209 * its children. Because a {@link ng.$rootScope.Scope#$watch watcher}'s listener can change
14210 * the model, the `$digest()` keeps calling the {@link ng.$rootScope.Scope#$watch watchers}
14211 * until no more listeners are firing. This means that it is possible to get into an infinite
14212 * loop. This function will throw `'Maximum iteration limit exceeded.'` if the number of
14213 * iterations exceeds 10.
14215 * Usually, you don't call `$digest()` directly in
14216 * {@link ng.directive:ngController controllers} or in
14217 * {@link ng.$compileProvider#directive directives}.
14218 * Instead, you should call {@link ng.$rootScope.Scope#$apply $apply()} (typically from within
14219 * a {@link ng.$compileProvider#directive directive}), which will force a `$digest()`.
14221 * If you want to be notified whenever `$digest()` is called,
14222 * you can register a `watchExpression` function with
14223 * {@link ng.$rootScope.Scope#$watch $watch()} with no `listener`.
14225 * In unit tests, you may need to call `$digest()` to simulate the scope life cycle.
14230 scope.name = 'misko';
14233 expect(scope.counter).toEqual(0);
14234 scope.$watch('name', function(newValue, oldValue) {
14235 scope.counter = scope.counter + 1;
14237 expect(scope.counter).toEqual(0);
14240 // the listener is always called during the first $digest loop after it was registered
14241 expect(scope.counter).toEqual(1);
14244 // but now it will not be called unless the value changes
14245 expect(scope.counter).toEqual(1);
14247 scope.name = 'adam';
14249 expect(scope.counter).toEqual(2);
14253 $digest: function() {
14254 var watch, value, last,
14258 next, current, target = this,
14260 logIdx, logMsg, asyncTask;
14262 beginPhase('$digest');
14263 // Check for changes to browser url that happened in sync before the call to $digest
14264 $browser.$$checkUrlChange();
14266 if (this === $rootScope && applyAsyncId !== null) {
14267 // If this is the root scope, and $applyAsync has scheduled a deferred $apply(), then
14268 // cancel the scheduled $apply and flush the queue of expressions to be evaluated.
14269 $browser.defer.cancel(applyAsyncId);
14273 lastDirtyWatch = null;
14275 do { // "while dirty" loop
14279 while (asyncQueue.length) {
14281 asyncTask = asyncQueue.shift();
14282 asyncTask.scope.$eval(asyncTask.expression, asyncTask.locals);
14284 $exceptionHandler(e);
14286 lastDirtyWatch = null;
14289 traverseScopesLoop:
14290 do { // "traverse the scopes" loop
14291 if ((watchers = current.$$watchers)) {
14292 // process our watches
14293 length = watchers.length;
14296 watch = watchers[length];
14297 // Most common watches are on primitives, in which case we can short
14298 // circuit it with === operator, only when === fails do we use .equals
14300 if ((value = watch.get(current)) !== (last = watch.last) &&
14302 ? equals(value, last)
14303 : (typeof value === 'number' && typeof last === 'number'
14304 && isNaN(value) && isNaN(last)))) {
14306 lastDirtyWatch = watch;
14307 watch.last = watch.eq ? copy(value, null) : value;
14308 watch.fn(value, ((last === initWatchVal) ? value : last), current);
14311 if (!watchLog[logIdx]) watchLog[logIdx] = [];
14312 watchLog[logIdx].push({
14313 msg: isFunction(watch.exp) ? 'fn: ' + (watch.exp.name || watch.exp.toString()) : watch.exp,
14318 } else if (watch === lastDirtyWatch) {
14319 // If the most recently dirty watcher is now clean, short circuit since the remaining watchers
14320 // have already been tested.
14322 break traverseScopesLoop;
14326 $exceptionHandler(e);
14331 // Insanity Warning: scope depth-first traversal
14332 // yes, this code is a bit crazy, but it works and we have tests to prove it!
14333 // this piece should be kept in sync with the traversal in $broadcast
14334 if (!(next = (current.$$childHead ||
14335 (current !== target && current.$$nextSibling)))) {
14336 while (current !== target && !(next = current.$$nextSibling)) {
14337 current = current.$parent;
14340 } while ((current = next));
14342 // `break traverseScopesLoop;` takes us to here
14344 if ((dirty || asyncQueue.length) && !(ttl--)) {
14346 throw $rootScopeMinErr('infdig',
14347 '{0} $digest() iterations reached. Aborting!\n' +
14348 'Watchers fired in the last 5 iterations: {1}',
14352 } while (dirty || asyncQueue.length);
14356 while (postDigestQueue.length) {
14358 postDigestQueue.shift()();
14360 $exceptionHandler(e);
14368 * @name $rootScope.Scope#$destroy
14369 * @eventType broadcast on scope being destroyed
14372 * Broadcasted when a scope and its children are being destroyed.
14374 * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to
14375 * clean up DOM bindings before an element is removed from the DOM.
14380 * @name $rootScope.Scope#$destroy
14384 * Removes the current scope (and all of its children) from the parent scope. Removal implies
14385 * that calls to {@link ng.$rootScope.Scope#$digest $digest()} will no longer
14386 * propagate to the current scope and its children. Removal also implies that the current
14387 * scope is eligible for garbage collection.
14389 * The `$destroy()` is usually used by directives such as
14390 * {@link ng.directive:ngRepeat ngRepeat} for managing the
14391 * unrolling of the loop.
14393 * Just before a scope is destroyed, a `$destroy` event is broadcasted on this scope.
14394 * Application code can register a `$destroy` event handler that will give it a chance to
14395 * perform any necessary cleanup.
14397 * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to
14398 * clean up DOM bindings before an element is removed from the DOM.
14400 $destroy: function() {
14401 // we can't destroy the root scope or a scope that has been already destroyed
14402 if (this.$$destroyed) return;
14403 var parent = this.$parent;
14405 this.$broadcast('$destroy');
14406 this.$$destroyed = true;
14407 if (this === $rootScope) return;
14409 for (var eventName in this.$$listenerCount) {
14410 decrementListenerCount(this, this.$$listenerCount[eventName], eventName);
14413 // sever all the references to parent scopes (after this cleanup, the current scope should
14414 // not be retained by any of our references and should be eligible for garbage collection)
14415 if (parent.$$childHead == this) parent.$$childHead = this.$$nextSibling;
14416 if (parent.$$childTail == this) parent.$$childTail = this.$$prevSibling;
14417 if (this.$$prevSibling) this.$$prevSibling.$$nextSibling = this.$$nextSibling;
14418 if (this.$$nextSibling) this.$$nextSibling.$$prevSibling = this.$$prevSibling;
14420 // Disable listeners, watchers and apply/digest methods
14421 this.$destroy = this.$digest = this.$apply = this.$evalAsync = this.$applyAsync = noop;
14422 this.$on = this.$watch = this.$watchGroup = function() { return noop; };
14423 this.$$listeners = {};
14425 // All of the code below is bogus code that works around V8's memory leak via optimized code
14426 // and inline caches.
14429 // - https://code.google.com/p/v8/issues/detail?id=2073#c26
14430 // - https://github.com/angular/angular.js/issues/6794#issuecomment-38648909
14431 // - https://github.com/angular/angular.js/issues/1313#issuecomment-10378451
14433 this.$parent = this.$$nextSibling = this.$$prevSibling = this.$$childHead =
14434 this.$$childTail = this.$root = this.$$watchers = null;
14439 * @name $rootScope.Scope#$eval
14443 * Executes the `expression` on the current scope and returns the result. Any exceptions in
14444 * the expression are propagated (uncaught). This is useful when evaluating Angular
14449 var scope = ng.$rootScope.Scope();
14453 expect(scope.$eval('a+b')).toEqual(3);
14454 expect(scope.$eval(function(scope){ return scope.a + scope.b; })).toEqual(3);
14457 * @param {(string|function())=} expression An angular expression to be executed.
14459 * - `string`: execute using the rules as defined in {@link guide/expression expression}.
14460 * - `function(scope)`: execute the function with the current `scope` parameter.
14462 * @param {(object)=} locals Local variables object, useful for overriding values in scope.
14463 * @returns {*} The result of evaluating the expression.
14465 $eval: function(expr, locals) {
14466 return $parse(expr)(this, locals);
14471 * @name $rootScope.Scope#$evalAsync
14475 * Executes the expression on the current scope at a later point in time.
14477 * The `$evalAsync` makes no guarantees as to when the `expression` will be executed, only
14480 * - it will execute after the function that scheduled the evaluation (preferably before DOM
14482 * - at least one {@link ng.$rootScope.Scope#$digest $digest cycle} will be performed after
14483 * `expression` execution.
14485 * Any exceptions from the execution of the expression are forwarded to the
14486 * {@link ng.$exceptionHandler $exceptionHandler} service.
14488 * __Note:__ if this function is called outside of a `$digest` cycle, a new `$digest` cycle
14489 * will be scheduled. However, it is encouraged to always call code that changes the model
14490 * from within an `$apply` call. That includes code evaluated via `$evalAsync`.
14492 * @param {(string|function())=} expression An angular expression to be executed.
14494 * - `string`: execute using the rules as defined in {@link guide/expression expression}.
14495 * - `function(scope)`: execute the function with the current `scope` parameter.
14497 * @param {(object)=} locals Local variables object, useful for overriding values in scope.
14499 $evalAsync: function(expr, locals) {
14500 // if we are outside of an $digest loop and this is the first time we are scheduling async
14501 // task also schedule async auto-flush
14502 if (!$rootScope.$$phase && !asyncQueue.length) {
14503 $browser.defer(function() {
14504 if (asyncQueue.length) {
14505 $rootScope.$digest();
14510 asyncQueue.push({scope: this, expression: expr, locals: locals});
14513 $$postDigest: function(fn) {
14514 postDigestQueue.push(fn);
14519 * @name $rootScope.Scope#$apply
14523 * `$apply()` is used to execute an expression in angular from outside of the angular
14524 * framework. (For example from browser DOM events, setTimeout, XHR or third party libraries).
14525 * Because we are calling into the angular framework we need to perform proper scope life
14526 * cycle of {@link ng.$exceptionHandler exception handling},
14527 * {@link ng.$rootScope.Scope#$digest executing watches}.
14531 * # Pseudo-Code of `$apply()`
14533 function $apply(expr) {
14535 return $eval(expr);
14537 $exceptionHandler(e);
14545 * Scope's `$apply()` method transitions through the following stages:
14547 * 1. The {@link guide/expression expression} is executed using the
14548 * {@link ng.$rootScope.Scope#$eval $eval()} method.
14549 * 2. Any exceptions from the execution of the expression are forwarded to the
14550 * {@link ng.$exceptionHandler $exceptionHandler} service.
14551 * 3. The {@link ng.$rootScope.Scope#$watch watch} listeners are fired immediately after the
14552 * expression was executed using the {@link ng.$rootScope.Scope#$digest $digest()} method.
14555 * @param {(string|function())=} exp An angular expression to be executed.
14557 * - `string`: execute using the rules as defined in {@link guide/expression expression}.
14558 * - `function(scope)`: execute the function with current `scope` parameter.
14560 * @returns {*} The result of evaluating the expression.
14562 $apply: function(expr) {
14564 beginPhase('$apply');
14565 return this.$eval(expr);
14567 $exceptionHandler(e);
14571 $rootScope.$digest();
14573 $exceptionHandler(e);
14581 * @name $rootScope.Scope#$applyAsync
14585 * Schedule the invocation of $apply to occur at a later time. The actual time difference
14586 * varies across browsers, but is typically around ~10 milliseconds.
14588 * This can be used to queue up multiple expressions which need to be evaluated in the same
14591 * @param {(string|function())=} exp An angular expression to be executed.
14593 * - `string`: execute using the rules as defined in {@link guide/expression expression}.
14594 * - `function(scope)`: execute the function with current `scope` parameter.
14596 $applyAsync: function(expr) {
14598 expr && applyAsyncQueue.push($applyAsyncExpression);
14599 scheduleApplyAsync();
14601 function $applyAsyncExpression() {
14608 * @name $rootScope.Scope#$on
14612 * Listens on events of a given type. See {@link ng.$rootScope.Scope#$emit $emit} for
14613 * discussion of event life cycle.
14615 * The event listener function format is: `function(event, args...)`. The `event` object
14616 * passed into the listener has the following attributes:
14618 * - `targetScope` - `{Scope}`: the scope on which the event was `$emit`-ed or
14620 * - `currentScope` - `{Scope}`: the scope that is currently handling the event. Once the
14621 * event propagates through the scope hierarchy, this property is set to null.
14622 * - `name` - `{string}`: name of the event.
14623 * - `stopPropagation` - `{function=}`: calling `stopPropagation` function will cancel
14624 * further event propagation (available only for events that were `$emit`-ed).
14625 * - `preventDefault` - `{function}`: calling `preventDefault` sets `defaultPrevented` flag
14627 * - `defaultPrevented` - `{boolean}`: true if `preventDefault` was called.
14629 * @param {string} name Event name to listen on.
14630 * @param {function(event, ...args)} listener Function to call when the event is emitted.
14631 * @returns {function()} Returns a deregistration function for this listener.
14633 $on: function(name, listener) {
14634 var namedListeners = this.$$listeners[name];
14635 if (!namedListeners) {
14636 this.$$listeners[name] = namedListeners = [];
14638 namedListeners.push(listener);
14640 var current = this;
14642 if (!current.$$listenerCount[name]) {
14643 current.$$listenerCount[name] = 0;
14645 current.$$listenerCount[name]++;
14646 } while ((current = current.$parent));
14649 return function() {
14650 var indexOfListener = namedListeners.indexOf(listener);
14651 if (indexOfListener !== -1) {
14652 namedListeners[indexOfListener] = null;
14653 decrementListenerCount(self, 1, name);
14661 * @name $rootScope.Scope#$emit
14665 * Dispatches an event `name` upwards through the scope hierarchy notifying the
14666 * registered {@link ng.$rootScope.Scope#$on} listeners.
14668 * The event life cycle starts at the scope on which `$emit` was called. All
14669 * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get
14670 * notified. Afterwards, the event traverses upwards toward the root scope and calls all
14671 * registered listeners along the way. The event will stop propagating if one of the listeners
14674 * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
14675 * onto the {@link ng.$exceptionHandler $exceptionHandler} service.
14677 * @param {string} name Event name to emit.
14678 * @param {...*} args Optional one or more arguments which will be passed onto the event listeners.
14679 * @return {Object} Event object (see {@link ng.$rootScope.Scope#$on}).
14681 $emit: function(name, args) {
14685 stopPropagation = false,
14688 targetScope: scope,
14689 stopPropagation: function() {stopPropagation = true;},
14690 preventDefault: function() {
14691 event.defaultPrevented = true;
14693 defaultPrevented: false
14695 listenerArgs = concat([event], arguments, 1),
14699 namedListeners = scope.$$listeners[name] || empty;
14700 event.currentScope = scope;
14701 for (i = 0, length = namedListeners.length; i < length; i++) {
14703 // if listeners were deregistered, defragment the array
14704 if (!namedListeners[i]) {
14705 namedListeners.splice(i, 1);
14711 //allow all listeners attached to the current scope to run
14712 namedListeners[i].apply(null, listenerArgs);
14714 $exceptionHandler(e);
14717 //if any listener on the current scope stops propagation, prevent bubbling
14718 if (stopPropagation) {
14719 event.currentScope = null;
14723 scope = scope.$parent;
14726 event.currentScope = null;
14734 * @name $rootScope.Scope#$broadcast
14738 * Dispatches an event `name` downwards to all child scopes (and their children) notifying the
14739 * registered {@link ng.$rootScope.Scope#$on} listeners.
14741 * The event life cycle starts at the scope on which `$broadcast` was called. All
14742 * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get
14743 * notified. Afterwards, the event propagates to all direct and indirect scopes of the current
14744 * scope and calls all registered listeners along the way. The event cannot be canceled.
14746 * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
14747 * onto the {@link ng.$exceptionHandler $exceptionHandler} service.
14749 * @param {string} name Event name to broadcast.
14750 * @param {...*} args Optional one or more arguments which will be passed onto the event listeners.
14751 * @return {Object} Event object, see {@link ng.$rootScope.Scope#$on}
14753 $broadcast: function(name, args) {
14759 targetScope: target,
14760 preventDefault: function() {
14761 event.defaultPrevented = true;
14763 defaultPrevented: false
14766 if (!target.$$listenerCount[name]) return event;
14768 var listenerArgs = concat([event], arguments, 1),
14769 listeners, i, length;
14771 //down while you can, then up and next sibling or up and next sibling until back at root
14772 while ((current = next)) {
14773 event.currentScope = current;
14774 listeners = current.$$listeners[name] || [];
14775 for (i = 0, length = listeners.length; i < length; i++) {
14776 // if listeners were deregistered, defragment the array
14777 if (!listeners[i]) {
14778 listeners.splice(i, 1);
14785 listeners[i].apply(null, listenerArgs);
14787 $exceptionHandler(e);
14791 // Insanity Warning: scope depth-first traversal
14792 // yes, this code is a bit crazy, but it works and we have tests to prove it!
14793 // this piece should be kept in sync with the traversal in $digest
14794 // (though it differs due to having the extra check for $$listenerCount)
14795 if (!(next = ((current.$$listenerCount[name] && current.$$childHead) ||
14796 (current !== target && current.$$nextSibling)))) {
14797 while (current !== target && !(next = current.$$nextSibling)) {
14798 current = current.$parent;
14803 event.currentScope = null;
14808 var $rootScope = new Scope();
14810 //The internal queues. Expose them on the $rootScope for debugging/testing purposes.
14811 var asyncQueue = $rootScope.$$asyncQueue = [];
14812 var postDigestQueue = $rootScope.$$postDigestQueue = [];
14813 var applyAsyncQueue = $rootScope.$$applyAsyncQueue = [];
14818 function beginPhase(phase) {
14819 if ($rootScope.$$phase) {
14820 throw $rootScopeMinErr('inprog', '{0} already in progress', $rootScope.$$phase);
14823 $rootScope.$$phase = phase;
14826 function clearPhase() {
14827 $rootScope.$$phase = null;
14831 function decrementListenerCount(current, count, name) {
14833 current.$$listenerCount[name] -= count;
14835 if (current.$$listenerCount[name] === 0) {
14836 delete current.$$listenerCount[name];
14838 } while ((current = current.$parent));
14842 * function used as an initial value for watchers.
14843 * because it's unique we can easily tell it apart from other values
14845 function initWatchVal() {}
14847 function flushApplyAsync() {
14848 while (applyAsyncQueue.length) {
14850 applyAsyncQueue.shift()();
14852 $exceptionHandler(e);
14855 applyAsyncId = null;
14858 function scheduleApplyAsync() {
14859 if (applyAsyncId === null) {
14860 applyAsyncId = $browser.defer(function() {
14861 $rootScope.$apply(flushApplyAsync);
14870 * Private service to sanitize uris for links and images. Used by $compile and $sanitize.
14872 function $$SanitizeUriProvider() {
14873 var aHrefSanitizationWhitelist = /^\s*(https?|ftp|mailto|tel|file):/,
14874 imgSrcSanitizationWhitelist = /^\s*((https?|ftp|file|blob):|data:image\/)/;
14878 * Retrieves or overrides the default regular expression that is used for whitelisting of safe
14879 * urls during a[href] sanitization.
14881 * The sanitization is a security measure aimed at prevent XSS attacks via html links.
14883 * Any url about to be assigned to a[href] via data-binding is first normalized and turned into
14884 * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist`
14885 * regular expression. If a match is found, the original url is written into the dom. Otherwise,
14886 * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
14888 * @param {RegExp=} regexp New regexp to whitelist urls with.
14889 * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
14890 * chaining otherwise.
14892 this.aHrefSanitizationWhitelist = function(regexp) {
14893 if (isDefined(regexp)) {
14894 aHrefSanitizationWhitelist = regexp;
14897 return aHrefSanitizationWhitelist;
14903 * Retrieves or overrides the default regular expression that is used for whitelisting of safe
14904 * urls during img[src] sanitization.
14906 * The sanitization is a security measure aimed at prevent XSS attacks via html links.
14908 * Any url about to be assigned to img[src] via data-binding is first normalized and turned into
14909 * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist`
14910 * regular expression. If a match is found, the original url is written into the dom. Otherwise,
14911 * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
14913 * @param {RegExp=} regexp New regexp to whitelist urls with.
14914 * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
14915 * chaining otherwise.
14917 this.imgSrcSanitizationWhitelist = function(regexp) {
14918 if (isDefined(regexp)) {
14919 imgSrcSanitizationWhitelist = regexp;
14922 return imgSrcSanitizationWhitelist;
14925 this.$get = function() {
14926 return function sanitizeUri(uri, isImage) {
14927 var regex = isImage ? imgSrcSanitizationWhitelist : aHrefSanitizationWhitelist;
14929 normalizedVal = urlResolve(uri).href;
14930 if (normalizedVal !== '' && !normalizedVal.match(regex)) {
14931 return 'unsafe:' + normalizedVal;
14938 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
14939 * Any commits to this file should be reviewed with security in mind. *
14940 * Changes to this file can potentially create security vulnerabilities. *
14941 * An approval from 2 Core members with history of modifying *
14942 * this file is required. *
14944 * Does the change somehow allow for arbitrary javascript to be executed? *
14945 * Or allows for someone to change the prototype of built-in objects? *
14946 * Or gives undesired access to variables likes document or window? *
14947 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
14949 var $sceMinErr = minErr('$sce');
14951 var SCE_CONTEXTS = {
14955 // RESOURCE_URL is a subtype of URL used in contexts where a privileged resource is sourced from a
14956 // url. (e.g. ng-include, script src, templateUrl)
14957 RESOURCE_URL: 'resourceUrl',
14961 // Helper functions follow.
14963 function adjustMatcher(matcher) {
14964 if (matcher === 'self') {
14966 } else if (isString(matcher)) {
14967 // Strings match exactly except for 2 wildcards - '*' and '**'.
14968 // '*' matches any character except those from the set ':/.?&'.
14969 // '**' matches any character (like .* in a RegExp).
14970 // More than 2 *'s raises an error as it's ill defined.
14971 if (matcher.indexOf('***') > -1) {
14972 throw $sceMinErr('iwcard',
14973 'Illegal sequence *** in string matcher. String: {0}', matcher);
14975 matcher = escapeForRegexp(matcher).
14976 replace('\\*\\*', '.*').
14977 replace('\\*', '[^:/.?&;]*');
14978 return new RegExp('^' + matcher + '$');
14979 } else if (isRegExp(matcher)) {
14980 // The only other type of matcher allowed is a Regexp.
14981 // Match entire URL / disallow partial matches.
14982 // Flags are reset (i.e. no global, ignoreCase or multiline)
14983 return new RegExp('^' + matcher.source + '$');
14985 throw $sceMinErr('imatcher',
14986 'Matchers may only be "self", string patterns or RegExp objects');
14991 function adjustMatchers(matchers) {
14992 var adjustedMatchers = [];
14993 if (isDefined(matchers)) {
14994 forEach(matchers, function(matcher) {
14995 adjustedMatchers.push(adjustMatcher(matcher));
14998 return adjustedMatchers;
15004 * @name $sceDelegate
15009 * `$sceDelegate` is a service that is used by the `$sce` service to provide {@link ng.$sce Strict
15010 * Contextual Escaping (SCE)} services to AngularJS.
15012 * Typically, you would configure or override the {@link ng.$sceDelegate $sceDelegate} instead of
15013 * the `$sce` service to customize the way Strict Contextual Escaping works in AngularJS. This is
15014 * because, while the `$sce` provides numerous shorthand methods, etc., you really only need to
15015 * override 3 core functions (`trustAs`, `getTrusted` and `valueOf`) to replace the way things
15016 * work because `$sce` delegates to `$sceDelegate` for these operations.
15018 * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} to configure this service.
15020 * The default instance of `$sceDelegate` should work out of the box with little pain. While you
15021 * can override it completely to change the behavior of `$sce`, the common case would
15022 * involve configuring the {@link ng.$sceDelegateProvider $sceDelegateProvider} instead by setting
15023 * your own whitelists and blacklists for trusting URLs used for loading AngularJS resources such as
15024 * templates. Refer {@link ng.$sceDelegateProvider#resourceUrlWhitelist
15025 * $sceDelegateProvider.resourceUrlWhitelist} and {@link
15026 * ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist}
15031 * @name $sceDelegateProvider
15034 * The `$sceDelegateProvider` provider allows developers to configure the {@link ng.$sceDelegate
15035 * $sceDelegate} service. This allows one to get/set the whitelists and blacklists used to ensure
15036 * that the URLs used for sourcing Angular templates are safe. Refer {@link
15037 * ng.$sceDelegateProvider#resourceUrlWhitelist $sceDelegateProvider.resourceUrlWhitelist} and
15038 * {@link ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist}
15040 * For the general details about this service in Angular, read the main page for {@link ng.$sce
15041 * Strict Contextual Escaping (SCE)}.
15043 * **Example**: Consider the following case. <a name="example"></a>
15045 * - your app is hosted at url `http://myapp.example.com/`
15046 * - but some of your templates are hosted on other domains you control such as
15047 * `http://srv01.assets.example.com/`, `http://srv02.assets.example.com/`, etc.
15048 * - and you have an open redirect at `http://myapp.example.com/clickThru?...`.
15050 * Here is what a secure configuration for this scenario might look like:
15053 * angular.module('myApp', []).config(function($sceDelegateProvider) {
15054 * $sceDelegateProvider.resourceUrlWhitelist([
15055 * // Allow same origin resource loads.
15057 * // Allow loading from our assets domain. Notice the difference between * and **.
15058 * 'http://srv*.assets.example.com/**'
15061 * // The blacklist overrides the whitelist so the open redirect here is blocked.
15062 * $sceDelegateProvider.resourceUrlBlacklist([
15063 * 'http://myapp.example.com/clickThru**'
15069 function $SceDelegateProvider() {
15070 this.SCE_CONTEXTS = SCE_CONTEXTS;
15072 // Resource URLs can also be trusted by policy.
15073 var resourceUrlWhitelist = ['self'],
15074 resourceUrlBlacklist = [];
15078 * @name $sceDelegateProvider#resourceUrlWhitelist
15081 * @param {Array=} whitelist When provided, replaces the resourceUrlWhitelist with the value
15082 * provided. This must be an array or null. A snapshot of this array is used so further
15083 * changes to the array are ignored.
15085 * Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
15086 * allowed in this array.
15088 * Note: **an empty whitelist array will block all URLs**!
15090 * @return {Array} the currently set whitelist array.
15092 * The **default value** when no whitelist has been explicitly set is `['self']` allowing only
15093 * same origin resource requests.
15096 * Sets/Gets the whitelist of trusted resource URLs.
15098 this.resourceUrlWhitelist = function(value) {
15099 if (arguments.length) {
15100 resourceUrlWhitelist = adjustMatchers(value);
15102 return resourceUrlWhitelist;
15107 * @name $sceDelegateProvider#resourceUrlBlacklist
15110 * @param {Array=} blacklist When provided, replaces the resourceUrlBlacklist with the value
15111 * provided. This must be an array or null. A snapshot of this array is used so further
15112 * changes to the array are ignored.
15114 * Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
15115 * allowed in this array.
15117 * The typical usage for the blacklist is to **block
15118 * [open redirects](http://cwe.mitre.org/data/definitions/601.html)** served by your domain as
15119 * these would otherwise be trusted but actually return content from the redirected domain.
15121 * Finally, **the blacklist overrides the whitelist** and has the final say.
15123 * @return {Array} the currently set blacklist array.
15125 * The **default value** when no whitelist has been explicitly set is the empty array (i.e. there
15126 * is no blacklist.)
15129 * Sets/Gets the blacklist of trusted resource URLs.
15132 this.resourceUrlBlacklist = function(value) {
15133 if (arguments.length) {
15134 resourceUrlBlacklist = adjustMatchers(value);
15136 return resourceUrlBlacklist;
15139 this.$get = ['$injector', function($injector) {
15141 var htmlSanitizer = function htmlSanitizer(html) {
15142 throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.');
15145 if ($injector.has('$sanitize')) {
15146 htmlSanitizer = $injector.get('$sanitize');
15150 function matchUrl(matcher, parsedUrl) {
15151 if (matcher === 'self') {
15152 return urlIsSameOrigin(parsedUrl);
15154 // definitely a regex. See adjustMatchers()
15155 return !!matcher.exec(parsedUrl.href);
15159 function isResourceUrlAllowedByPolicy(url) {
15160 var parsedUrl = urlResolve(url.toString());
15161 var i, n, allowed = false;
15162 // Ensure that at least one item from the whitelist allows this url.
15163 for (i = 0, n = resourceUrlWhitelist.length; i < n; i++) {
15164 if (matchUrl(resourceUrlWhitelist[i], parsedUrl)) {
15170 // Ensure that no item from the blacklist blocked this url.
15171 for (i = 0, n = resourceUrlBlacklist.length; i < n; i++) {
15172 if (matchUrl(resourceUrlBlacklist[i], parsedUrl)) {
15181 function generateHolderType(Base) {
15182 var holderType = function TrustedValueHolderType(trustedValue) {
15183 this.$$unwrapTrustedValue = function() {
15184 return trustedValue;
15188 holderType.prototype = new Base();
15190 holderType.prototype.valueOf = function sceValueOf() {
15191 return this.$$unwrapTrustedValue();
15193 holderType.prototype.toString = function sceToString() {
15194 return this.$$unwrapTrustedValue().toString();
15199 var trustedValueHolderBase = generateHolderType(),
15202 byType[SCE_CONTEXTS.HTML] = generateHolderType(trustedValueHolderBase);
15203 byType[SCE_CONTEXTS.CSS] = generateHolderType(trustedValueHolderBase);
15204 byType[SCE_CONTEXTS.URL] = generateHolderType(trustedValueHolderBase);
15205 byType[SCE_CONTEXTS.JS] = generateHolderType(trustedValueHolderBase);
15206 byType[SCE_CONTEXTS.RESOURCE_URL] = generateHolderType(byType[SCE_CONTEXTS.URL]);
15210 * @name $sceDelegate#trustAs
15213 * Returns an object that is trusted by angular for use in specified strict
15214 * contextual escaping contexts (such as ng-bind-html, ng-include, any src
15215 * attribute interpolation, any dom event binding attribute interpolation
15216 * such as for onclick, etc.) that uses the provided value.
15217 * See {@link ng.$sce $sce} for enabling strict contextual escaping.
15219 * @param {string} type The kind of context in which this value is safe for use. e.g. url,
15220 * resourceUrl, html, js and css.
15221 * @param {*} value The value that that should be considered trusted/safe.
15222 * @returns {*} A value that can be used to stand in for the provided `value` in places
15223 * where Angular expects a $sce.trustAs() return value.
15225 function trustAs(type, trustedValue) {
15226 var Constructor = (byType.hasOwnProperty(type) ? byType[type] : null);
15227 if (!Constructor) {
15228 throw $sceMinErr('icontext',
15229 'Attempted to trust a value in invalid context. Context: {0}; Value: {1}',
15230 type, trustedValue);
15232 if (trustedValue === null || trustedValue === undefined || trustedValue === '') {
15233 return trustedValue;
15235 // All the current contexts in SCE_CONTEXTS happen to be strings. In order to avoid trusting
15236 // mutable objects, we ensure here that the value passed in is actually a string.
15237 if (typeof trustedValue !== 'string') {
15238 throw $sceMinErr('itype',
15239 'Attempted to trust a non-string value in a content requiring a string: Context: {0}',
15242 return new Constructor(trustedValue);
15247 * @name $sceDelegate#valueOf
15250 * If the passed parameter had been returned by a prior call to {@link ng.$sceDelegate#trustAs
15251 * `$sceDelegate.trustAs`}, returns the value that had been passed to {@link
15252 * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}.
15254 * If the passed parameter is not a value that had been returned by {@link
15255 * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}, returns it as-is.
15257 * @param {*} value The result of a prior {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}
15258 * call or anything else.
15259 * @returns {*} The `value` that was originally provided to {@link ng.$sceDelegate#trustAs
15260 * `$sceDelegate.trustAs`} if `value` is the result of such a call. Otherwise, returns
15261 * `value` unchanged.
15263 function valueOf(maybeTrusted) {
15264 if (maybeTrusted instanceof trustedValueHolderBase) {
15265 return maybeTrusted.$$unwrapTrustedValue();
15267 return maybeTrusted;
15273 * @name $sceDelegate#getTrusted
15276 * Takes the result of a {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`} call and
15277 * returns the originally supplied value if the queried context type is a supertype of the
15278 * created type. If this condition isn't satisfied, throws an exception.
15280 * @param {string} type The kind of context in which this value is to be used.
15281 * @param {*} maybeTrusted The result of a prior {@link ng.$sceDelegate#trustAs
15282 * `$sceDelegate.trustAs`} call.
15283 * @returns {*} The value the was originally provided to {@link ng.$sceDelegate#trustAs
15284 * `$sceDelegate.trustAs`} if valid in this context. Otherwise, throws an exception.
15286 function getTrusted(type, maybeTrusted) {
15287 if (maybeTrusted === null || maybeTrusted === undefined || maybeTrusted === '') {
15288 return maybeTrusted;
15290 var constructor = (byType.hasOwnProperty(type) ? byType[type] : null);
15291 if (constructor && maybeTrusted instanceof constructor) {
15292 return maybeTrusted.$$unwrapTrustedValue();
15294 // If we get here, then we may only take one of two actions.
15295 // 1. sanitize the value for the requested type, or
15296 // 2. throw an exception.
15297 if (type === SCE_CONTEXTS.RESOURCE_URL) {
15298 if (isResourceUrlAllowedByPolicy(maybeTrusted)) {
15299 return maybeTrusted;
15301 throw $sceMinErr('insecurl',
15302 'Blocked loading resource from url not allowed by $sceDelegate policy. URL: {0}',
15303 maybeTrusted.toString());
15305 } else if (type === SCE_CONTEXTS.HTML) {
15306 return htmlSanitizer(maybeTrusted);
15308 throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.');
15311 return { trustAs: trustAs,
15312 getTrusted: getTrusted,
15313 valueOf: valueOf };
15320 * @name $sceProvider
15323 * The $sceProvider provider allows developers to configure the {@link ng.$sce $sce} service.
15324 * - enable/disable Strict Contextual Escaping (SCE) in a module
15325 * - override the default implementation with a custom delegate
15327 * Read more about {@link ng.$sce Strict Contextual Escaping (SCE)}.
15330 /* jshint maxlen: false*/
15339 * `$sce` is a service that provides Strict Contextual Escaping services to AngularJS.
15341 * # Strict Contextual Escaping
15343 * Strict Contextual Escaping (SCE) is a mode in which AngularJS requires bindings in certain
15344 * contexts to result in a value that is marked as safe to use for that context. One example of
15345 * such a context is binding arbitrary html controlled by the user via `ng-bind-html`. We refer
15346 * to these contexts as privileged or SCE contexts.
15348 * As of version 1.2, Angular ships with SCE enabled by default.
15350 * Note: When enabled (the default), IE<11 in quirks mode is not supported. In this mode, IE<11 allow
15351 * one to execute arbitrary javascript by the use of the expression() syntax. Refer
15352 * <http://blogs.msdn.com/b/ie/archive/2008/10/16/ending-expressions.aspx> to learn more about them.
15353 * You can ensure your document is in standards mode and not quirks mode by adding `<!doctype html>`
15354 * to the top of your HTML document.
15356 * SCE assists in writing code in way that (a) is secure by default and (b) makes auditing for
15357 * security vulnerabilities such as XSS, clickjacking, etc. a lot easier.
15359 * Here's an example of a binding in a privileged context:
15362 * <input ng-model="userHtml">
15363 * <div ng-bind-html="userHtml"></div>
15366 * Notice that `ng-bind-html` is bound to `userHtml` controlled by the user. With SCE
15367 * disabled, this application allows the user to render arbitrary HTML into the DIV.
15368 * In a more realistic example, one may be rendering user comments, blog articles, etc. via
15369 * bindings. (HTML is just one example of a context where rendering user controlled input creates
15370 * security vulnerabilities.)
15372 * For the case of HTML, you might use a library, either on the client side, or on the server side,
15373 * to sanitize unsafe HTML before binding to the value and rendering it in the document.
15375 * How would you ensure that every place that used these types of bindings was bound to a value that
15376 * was sanitized by your library (or returned as safe for rendering by your server?) How can you
15377 * ensure that you didn't accidentally delete the line that sanitized the value, or renamed some
15378 * properties/fields and forgot to update the binding to the sanitized value?
15380 * To be secure by default, you want to ensure that any such bindings are disallowed unless you can
15381 * determine that something explicitly says it's safe to use a value for binding in that
15382 * context. You can then audit your code (a simple grep would do) to ensure that this is only done
15383 * for those values that you can easily tell are safe - because they were received from your server,
15384 * sanitized by your library, etc. You can organize your codebase to help with this - perhaps
15385 * allowing only the files in a specific directory to do this. Ensuring that the internal API
15386 * exposed by that code doesn't markup arbitrary values as safe then becomes a more manageable task.
15388 * In the case of AngularJS' SCE service, one uses {@link ng.$sce#trustAs $sce.trustAs}
15389 * (and shorthand methods such as {@link ng.$sce#trustAsHtml $sce.trustAsHtml}, etc.) to
15390 * obtain values that will be accepted by SCE / privileged contexts.
15393 * ## How does it work?
15395 * In privileged contexts, directives and code will bind to the result of {@link ng.$sce#getTrusted
15396 * $sce.getTrusted(context, value)} rather than to the value directly. Directives use {@link
15397 * ng.$sce#parseAs $sce.parseAs} rather than `$parse` to watch attribute bindings, which performs the
15398 * {@link ng.$sce#getTrusted $sce.getTrusted} behind the scenes on non-constant literals.
15400 * As an example, {@link ng.directive:ngBindHtml ngBindHtml} uses {@link
15401 * ng.$sce#parseAsHtml $sce.parseAsHtml(binding expression)}. Here's the actual code (slightly
15405 * var ngBindHtmlDirective = ['$sce', function($sce) {
15406 * return function(scope, element, attr) {
15407 * scope.$watch($sce.parseAsHtml(attr.ngBindHtml), function(value) {
15408 * element.html(value || '');
15414 * ## Impact on loading templates
15416 * This applies both to the {@link ng.directive:ngInclude `ng-include`} directive as well as
15417 * `templateUrl`'s specified by {@link guide/directive directives}.
15419 * By default, Angular only loads templates from the same domain and protocol as the application
15420 * document. This is done by calling {@link ng.$sce#getTrustedResourceUrl
15421 * $sce.getTrustedResourceUrl} on the template URL. To load templates from other domains and/or
15422 * protocols, you may either either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist
15423 * them} or {@link ng.$sce#trustAsResourceUrl wrap it} into a trusted value.
15427 * [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest)
15428 * and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/)
15429 * policy apply in addition to this and may further restrict whether the template is successfully
15430 * loaded. This means that without the right CORS policy, loading templates from a different domain
15431 * won't work on all browsers. Also, loading templates from `file://` URL does not work on some
15434 * ## This feels like too much overhead
15436 * It's important to remember that SCE only applies to interpolation expressions.
15438 * If your expressions are constant literals, they're automatically trusted and you don't need to
15439 * call `$sce.trustAs` on them (remember to include the `ngSanitize` module) (e.g.
15440 * `<div ng-bind-html="'<b>implicitly trusted</b>'"></div>`) just works.
15442 * Additionally, `a[href]` and `img[src]` automatically sanitize their URLs and do not pass them
15443 * through {@link ng.$sce#getTrusted $sce.getTrusted}. SCE doesn't play a role here.
15445 * The included {@link ng.$sceDelegate $sceDelegate} comes with sane defaults to allow you to load
15446 * templates in `ng-include` from your application's domain without having to even know about SCE.
15447 * It blocks loading templates from other domains or loading templates over http from an https
15448 * served document. You can change these by setting your own custom {@link
15449 * ng.$sceDelegateProvider#resourceUrlWhitelist whitelists} and {@link
15450 * ng.$sceDelegateProvider#resourceUrlBlacklist blacklists} for matching such URLs.
15452 * This significantly reduces the overhead. It is far easier to pay the small overhead and have an
15453 * application that's secure and can be audited to verify that with much more ease than bolting
15454 * security onto an application later.
15456 * <a name="contexts"></a>
15457 * ## What trusted context types are supported?
15459 * | Context | Notes |
15460 * |---------------------|----------------|
15461 * | `$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. |
15462 * | `$sce.CSS` | For CSS that's safe to source into the application. Currently unused. Feel free to use it in your own directives. |
15463 * | `$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. |
15464 * | `$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. |
15465 * | `$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. |
15467 * ## Format of items in {@link ng.$sceDelegateProvider#resourceUrlWhitelist resourceUrlWhitelist}/{@link ng.$sceDelegateProvider#resourceUrlBlacklist Blacklist} <a name="resourceUrlPatternItem"></a>
15469 * Each element in these arrays must be one of the following:
15472 * - The special **string**, `'self'`, can be used to match against all URLs of the **same
15473 * domain** as the application document using the **same protocol**.
15474 * - **String** (except the special value `'self'`)
15475 * - The string is matched against the full *normalized / absolute URL* of the resource
15476 * being tested (substring matches are not good enough.)
15477 * - There are exactly **two wildcard sequences** - `*` and `**`. All other characters
15478 * match themselves.
15479 * - `*`: matches zero or more occurrences of any character other than one of the following 6
15480 * characters: '`:`', '`/`', '`.`', '`?`', '`&`' and ';'. It's a useful wildcard for use
15482 * - `**`: matches zero or more occurrences of *any* character. As such, it's not
15483 * not appropriate to use in for a scheme, domain, etc. as it would match too much. (e.g.
15484 * http://**.example.com/ would match http://evil.com/?ignore=.example.com/ and that might
15485 * not have been the intention.) Its usage at the very end of the path is ok. (e.g.
15486 * http://foo.example.com/templates/**).
15487 * - **RegExp** (*see caveat below*)
15488 * - *Caveat*: While regular expressions are powerful and offer great flexibility, their syntax
15489 * (and all the inevitable escaping) makes them *harder to maintain*. It's easy to
15490 * accidentally introduce a bug when one updates a complex expression (imho, all regexes should
15491 * have good test coverage.). For instance, the use of `.` in the regex is correct only in a
15492 * small number of cases. A `.` character in the regex used when matching the scheme or a
15493 * subdomain could be matched against a `:` or literal `.` that was likely not intended. It
15494 * is highly recommended to use the string patterns and only fall back to regular expressions
15495 * if they as a last resort.
15496 * - The regular expression must be an instance of RegExp (i.e. not a string.) It is
15497 * matched against the **entire** *normalized / absolute URL* of the resource being tested
15498 * (even when the RegExp did not have the `^` and `$` codes.) In addition, any flags
15499 * present on the RegExp (such as multiline, global, ignoreCase) are ignored.
15500 * - If you are generating your JavaScript from some other templating engine (not
15501 * recommended, e.g. in issue [#4006](https://github.com/angular/angular.js/issues/4006)),
15502 * remember to escape your regular expression (and be aware that you might need more than
15503 * one level of escaping depending on your templating engine and the way you interpolated
15504 * the value.) Do make use of your platform's escaping mechanism as it might be good
15505 * enough before coding your own. e.g. Ruby has
15506 * [Regexp.escape(str)](http://www.ruby-doc.org/core-2.0.0/Regexp.html#method-c-escape)
15507 * and Python has [re.escape](http://docs.python.org/library/re.html#re.escape).
15508 * Javascript lacks a similar built in function for escaping. Take a look at Google
15509 * Closure library's [goog.string.regExpEscape(s)](
15510 * http://docs.closure-library.googlecode.com/git/closure_goog_string_string.js.source.html#line962).
15512 * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} for an example.
15514 * ## Show me an example using SCE.
15516 * <example module="mySceApp" deps="angular-sanitize.js">
15517 * <file name="index.html">
15518 * <div ng-controller="AppController as myCtrl">
15519 * <i ng-bind-html="myCtrl.explicitlyTrustedHtml" id="explicitlyTrustedHtml"></i><br><br>
15520 * <b>User comments</b><br>
15521 * By default, HTML that isn't explicitly trusted (e.g. Alice's comment) is sanitized when
15522 * $sanitize is available. If $sanitize isn't available, this results in an error instead of an
15524 * <div class="well">
15525 * <div ng-repeat="userComment in myCtrl.userComments">
15526 * <b>{{userComment.name}}</b>:
15527 * <span ng-bind-html="userComment.htmlComment" class="htmlComment"></span>
15534 * <file name="script.js">
15535 * angular.module('mySceApp', ['ngSanitize'])
15536 * .controller('AppController', ['$http', '$templateCache', '$sce',
15537 * function($http, $templateCache, $sce) {
15539 * $http.get("test_data.json", {cache: $templateCache}).success(function(userComments) {
15540 * self.userComments = userComments;
15542 * self.explicitlyTrustedHtml = $sce.trustAsHtml(
15543 * '<span onmouseover="this.textContent="Explicitly trusted HTML bypasses ' +
15544 * 'sanitization."">Hover over this text.</span>');
15548 * <file name="test_data.json">
15550 * { "name": "Alice",
15552 * "<span onmouseover='this.textContent=\"PWN3D!\"'>Is <i>anyone</i> reading this?</span>"
15555 * "htmlComment": "<i>Yes!</i> Am I the only other one?"
15560 * <file name="protractor.js" type="protractor">
15561 * describe('SCE doc demo', function() {
15562 * it('should sanitize untrusted values', function() {
15563 * expect(element.all(by.css('.htmlComment')).first().getInnerHtml())
15564 * .toBe('<span>Is <i>anyone</i> reading this?</span>');
15567 * it('should NOT sanitize explicitly trusted values', function() {
15568 * expect(element(by.id('explicitlyTrustedHtml')).getInnerHtml()).toBe(
15569 * '<span onmouseover="this.textContent="Explicitly trusted HTML bypasses ' +
15570 * 'sanitization."">Hover over this text.</span>');
15578 * ## Can I disable SCE completely?
15580 * Yes, you can. However, this is strongly discouraged. SCE gives you a lot of security benefits
15581 * for little coding overhead. It will be much harder to take an SCE disabled application and
15582 * either secure it on your own or enable SCE at a later stage. It might make sense to disable SCE
15583 * for cases where you have a lot of existing code that was written before SCE was introduced and
15584 * you're migrating them a module at a time.
15586 * That said, here's how you can completely disable SCE:
15589 * angular.module('myAppWithSceDisabledmyApp', []).config(function($sceProvider) {
15590 * // Completely disable SCE. For demonstration purposes only!
15591 * // Do not use in new projects.
15592 * $sceProvider.enabled(false);
15597 /* jshint maxlen: 100 */
15599 function $SceProvider() {
15600 var enabled = true;
15604 * @name $sceProvider#enabled
15607 * @param {boolean=} value If provided, then enables/disables SCE.
15608 * @return {boolean} true if SCE is enabled, false otherwise.
15611 * Enables/disables SCE and returns the current value.
15613 this.enabled = function(value) {
15614 if (arguments.length) {
15621 /* Design notes on the default implementation for SCE.
15623 * The API contract for the SCE delegate
15624 * -------------------------------------
15625 * The SCE delegate object must provide the following 3 methods:
15627 * - trustAs(contextEnum, value)
15628 * This method is used to tell the SCE service that the provided value is OK to use in the
15629 * contexts specified by contextEnum. It must return an object that will be accepted by
15630 * getTrusted() for a compatible contextEnum and return this value.
15633 * For values that were not produced by trustAs(), return them as is. For values that were
15634 * produced by trustAs(), return the corresponding input value to trustAs. Basically, if
15635 * trustAs is wrapping the given values into some type, this operation unwraps it when given
15638 * - getTrusted(contextEnum, value)
15639 * This function should return the a value that is safe to use in the context specified by
15640 * contextEnum or throw and exception otherwise.
15642 * NOTE: This contract deliberately does NOT state that values returned by trustAs() must be
15643 * opaque or wrapped in some holder object. That happens to be an implementation detail. For
15644 * instance, an implementation could maintain a registry of all trusted objects by context. In
15645 * such a case, trustAs() would return the same object that was passed in. getTrusted() would
15646 * return the same object passed in if it was found in the registry under a compatible context or
15647 * throw an exception otherwise. An implementation might only wrap values some of the time based
15648 * on some criteria. getTrusted() might return a value and not throw an exception for special
15649 * constants or objects even if not wrapped. All such implementations fulfill this contract.
15652 * A note on the inheritance model for SCE contexts
15653 * ------------------------------------------------
15654 * I've used inheritance and made RESOURCE_URL wrapped types a subtype of URL wrapped types. This
15655 * is purely an implementation details.
15657 * The contract is simply this:
15659 * getTrusted($sce.RESOURCE_URL, value) succeeding implies that getTrusted($sce.URL, value)
15660 * will also succeed.
15662 * Inheritance happens to capture this in a natural way. In some future, we
15663 * may not use inheritance anymore. That is OK because no code outside of
15664 * sce.js and sceSpecs.js would need to be aware of this detail.
15667 this.$get = ['$parse', '$sceDelegate', function(
15668 $parse, $sceDelegate) {
15669 // Prereq: Ensure that we're not running in IE<11 quirks mode. In that mode, IE < 11 allow
15670 // the "expression(javascript expression)" syntax which is insecure.
15671 if (enabled && msie < 8) {
15672 throw $sceMinErr('iequirks',
15673 'Strict Contextual Escaping does not support Internet Explorer version < 11 in quirks ' +
15674 'mode. You can fix this by adding the text <!doctype html> to the top of your HTML ' +
15675 'document. See http://docs.angularjs.org/api/ng.$sce for more information.');
15678 var sce = shallowCopy(SCE_CONTEXTS);
15682 * @name $sce#isEnabled
15685 * @return {Boolean} true if SCE is enabled, false otherwise. If you want to set the value, you
15686 * have to do it at module config time on {@link ng.$sceProvider $sceProvider}.
15689 * Returns a boolean indicating if SCE is enabled.
15691 sce.isEnabled = function() {
15694 sce.trustAs = $sceDelegate.trustAs;
15695 sce.getTrusted = $sceDelegate.getTrusted;
15696 sce.valueOf = $sceDelegate.valueOf;
15699 sce.trustAs = sce.getTrusted = function(type, value) { return value; };
15700 sce.valueOf = identity;
15705 * @name $sce#parseAs
15708 * Converts Angular {@link guide/expression expression} into a function. This is like {@link
15709 * ng.$parse $parse} and is identical when the expression is a literal constant. Otherwise, it
15710 * wraps the expression in a call to {@link ng.$sce#getTrusted $sce.getTrusted(*type*,
15713 * @param {string} type The kind of SCE context in which this result will be used.
15714 * @param {string} expression String expression to compile.
15715 * @returns {function(context, locals)} a function which represents the compiled expression:
15717 * * `context` – `{object}` – an object against which any expressions embedded in the strings
15718 * are evaluated against (typically a scope object).
15719 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
15722 sce.parseAs = function sceParseAs(type, expr) {
15723 var parsed = $parse(expr);
15724 if (parsed.literal && parsed.constant) {
15727 return $parse(expr, function(value) {
15728 return sce.getTrusted(type, value);
15735 * @name $sce#trustAs
15738 * Delegates to {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}. As such,
15739 * returns an object that is trusted by angular for use in specified strict contextual
15740 * escaping contexts (such as ng-bind-html, ng-include, any src attribute
15741 * interpolation, any dom event binding attribute interpolation such as for onclick, etc.)
15742 * that uses the provided value. See * {@link ng.$sce $sce} for enabling strict contextual
15745 * @param {string} type The kind of context in which this value is safe for use. e.g. url,
15746 * resource_url, html, js and css.
15747 * @param {*} value The value that that should be considered trusted/safe.
15748 * @returns {*} A value that can be used to stand in for the provided `value` in places
15749 * where Angular expects a $sce.trustAs() return value.
15754 * @name $sce#trustAsHtml
15757 * Shorthand method. `$sce.trustAsHtml(value)` →
15758 * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.HTML, value)`}
15760 * @param {*} value The value to trustAs.
15761 * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedHtml
15762 * $sce.getTrustedHtml(value)} to obtain the original value. (privileged directives
15763 * only accept expressions that are either literal constants or are the
15764 * return value of {@link ng.$sce#trustAs $sce.trustAs}.)
15769 * @name $sce#trustAsUrl
15772 * Shorthand method. `$sce.trustAsUrl(value)` →
15773 * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.URL, value)`}
15775 * @param {*} value The value to trustAs.
15776 * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedUrl
15777 * $sce.getTrustedUrl(value)} to obtain the original value. (privileged directives
15778 * only accept expressions that are either literal constants or are the
15779 * return value of {@link ng.$sce#trustAs $sce.trustAs}.)
15784 * @name $sce#trustAsResourceUrl
15787 * Shorthand method. `$sce.trustAsResourceUrl(value)` →
15788 * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.RESOURCE_URL, value)`}
15790 * @param {*} value The value to trustAs.
15791 * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedResourceUrl
15792 * $sce.getTrustedResourceUrl(value)} to obtain the original value. (privileged directives
15793 * only accept expressions that are either literal constants or are the return
15794 * value of {@link ng.$sce#trustAs $sce.trustAs}.)
15799 * @name $sce#trustAsJs
15802 * Shorthand method. `$sce.trustAsJs(value)` →
15803 * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.JS, value)`}
15805 * @param {*} value The value to trustAs.
15806 * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedJs
15807 * $sce.getTrustedJs(value)} to obtain the original value. (privileged directives
15808 * only accept expressions that are either literal constants or are the
15809 * return value of {@link ng.$sce#trustAs $sce.trustAs}.)
15814 * @name $sce#getTrusted
15817 * Delegates to {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted`}. As such,
15818 * takes the result of a {@link ng.$sce#trustAs `$sce.trustAs`}() call and returns the
15819 * originally supplied value if the queried context type is a supertype of the created type.
15820 * If this condition isn't satisfied, throws an exception.
15822 * @param {string} type The kind of context in which this value is to be used.
15823 * @param {*} maybeTrusted The result of a prior {@link ng.$sce#trustAs `$sce.trustAs`}
15825 * @returns {*} The value the was originally provided to
15826 * {@link ng.$sce#trustAs `$sce.trustAs`} if valid in this context.
15827 * Otherwise, throws an exception.
15832 * @name $sce#getTrustedHtml
15835 * Shorthand method. `$sce.getTrustedHtml(value)` →
15836 * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.HTML, value)`}
15838 * @param {*} value The value to pass to `$sce.getTrusted`.
15839 * @returns {*} The return value of `$sce.getTrusted($sce.HTML, value)`
15844 * @name $sce#getTrustedCss
15847 * Shorthand method. `$sce.getTrustedCss(value)` →
15848 * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.CSS, value)`}
15850 * @param {*} value The value to pass to `$sce.getTrusted`.
15851 * @returns {*} The return value of `$sce.getTrusted($sce.CSS, value)`
15856 * @name $sce#getTrustedUrl
15859 * Shorthand method. `$sce.getTrustedUrl(value)` →
15860 * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.URL, value)`}
15862 * @param {*} value The value to pass to `$sce.getTrusted`.
15863 * @returns {*} The return value of `$sce.getTrusted($sce.URL, value)`
15868 * @name $sce#getTrustedResourceUrl
15871 * Shorthand method. `$sce.getTrustedResourceUrl(value)` →
15872 * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.RESOURCE_URL, value)`}
15874 * @param {*} value The value to pass to `$sceDelegate.getTrusted`.
15875 * @returns {*} The return value of `$sce.getTrusted($sce.RESOURCE_URL, value)`
15880 * @name $sce#getTrustedJs
15883 * Shorthand method. `$sce.getTrustedJs(value)` →
15884 * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.JS, value)`}
15886 * @param {*} value The value to pass to `$sce.getTrusted`.
15887 * @returns {*} The return value of `$sce.getTrusted($sce.JS, value)`
15892 * @name $sce#parseAsHtml
15895 * Shorthand method. `$sce.parseAsHtml(expression string)` →
15896 * {@link ng.$sce#parseAs `$sce.parseAs($sce.HTML, value)`}
15898 * @param {string} expression String expression to compile.
15899 * @returns {function(context, locals)} a function which represents the compiled expression:
15901 * * `context` – `{object}` – an object against which any expressions embedded in the strings
15902 * are evaluated against (typically a scope object).
15903 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
15909 * @name $sce#parseAsCss
15912 * Shorthand method. `$sce.parseAsCss(value)` →
15913 * {@link ng.$sce#parseAs `$sce.parseAs($sce.CSS, value)`}
15915 * @param {string} expression String expression to compile.
15916 * @returns {function(context, locals)} a function which represents the compiled expression:
15918 * * `context` – `{object}` – an object against which any expressions embedded in the strings
15919 * are evaluated against (typically a scope object).
15920 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
15926 * @name $sce#parseAsUrl
15929 * Shorthand method. `$sce.parseAsUrl(value)` →
15930 * {@link ng.$sce#parseAs `$sce.parseAs($sce.URL, value)`}
15932 * @param {string} expression String expression to compile.
15933 * @returns {function(context, locals)} a function which represents the compiled expression:
15935 * * `context` – `{object}` – an object against which any expressions embedded in the strings
15936 * are evaluated against (typically a scope object).
15937 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
15943 * @name $sce#parseAsResourceUrl
15946 * Shorthand method. `$sce.parseAsResourceUrl(value)` →
15947 * {@link ng.$sce#parseAs `$sce.parseAs($sce.RESOURCE_URL, value)`}
15949 * @param {string} expression String expression to compile.
15950 * @returns {function(context, locals)} a function which represents the compiled expression:
15952 * * `context` – `{object}` – an object against which any expressions embedded in the strings
15953 * are evaluated against (typically a scope object).
15954 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
15960 * @name $sce#parseAsJs
15963 * Shorthand method. `$sce.parseAsJs(value)` →
15964 * {@link ng.$sce#parseAs `$sce.parseAs($sce.JS, value)`}
15966 * @param {string} expression String expression to compile.
15967 * @returns {function(context, locals)} a function which represents the compiled expression:
15969 * * `context` – `{object}` – an object against which any expressions embedded in the strings
15970 * are evaluated against (typically a scope object).
15971 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
15975 // Shorthand delegations.
15976 var parse = sce.parseAs,
15977 getTrusted = sce.getTrusted,
15978 trustAs = sce.trustAs;
15980 forEach(SCE_CONTEXTS, function(enumValue, name) {
15981 var lName = lowercase(name);
15982 sce[camelCase("parse_as_" + lName)] = function(expr) {
15983 return parse(enumValue, expr);
15985 sce[camelCase("get_trusted_" + lName)] = function(value) {
15986 return getTrusted(enumValue, value);
15988 sce[camelCase("trust_as_" + lName)] = function(value) {
15989 return trustAs(enumValue, value);
15998 * !!! This is an undocumented "private" service !!!
16001 * @requires $window
16002 * @requires $document
16004 * @property {boolean} history Does the browser support html5 history api ?
16005 * @property {boolean} transitions Does the browser support CSS transition events ?
16006 * @property {boolean} animations Does the browser support CSS animation events ?
16009 * This is very simple implementation of testing browser's features.
16011 function $SnifferProvider() {
16012 this.$get = ['$window', '$document', function($window, $document) {
16013 var eventSupport = {},
16015 int((/android (\d+)/.exec(lowercase(($window.navigator || {}).userAgent)) || [])[1]),
16016 boxee = /Boxee/i.test(($window.navigator || {}).userAgent),
16017 document = $document[0] || {},
16019 vendorRegex = /^(Moz|webkit|ms)(?=[A-Z])/,
16020 bodyStyle = document.body && document.body.style,
16021 transitions = false,
16022 animations = false,
16026 for (var prop in bodyStyle) {
16027 if (match = vendorRegex.exec(prop)) {
16028 vendorPrefix = match[0];
16029 vendorPrefix = vendorPrefix.substr(0, 1).toUpperCase() + vendorPrefix.substr(1);
16034 if (!vendorPrefix) {
16035 vendorPrefix = ('WebkitOpacity' in bodyStyle) && 'webkit';
16038 transitions = !!(('transition' in bodyStyle) || (vendorPrefix + 'Transition' in bodyStyle));
16039 animations = !!(('animation' in bodyStyle) || (vendorPrefix + 'Animation' in bodyStyle));
16041 if (android && (!transitions || !animations)) {
16042 transitions = isString(document.body.style.webkitTransition);
16043 animations = isString(document.body.style.webkitAnimation);
16049 // Android has history.pushState, but it does not update location correctly
16050 // so let's not use the history API at all.
16051 // http://code.google.com/p/android/issues/detail?id=17471
16052 // https://github.com/angular/angular.js/issues/904
16054 // older webkit browser (533.9) on Boxee box has exactly the same problem as Android has
16055 // so let's not use the history API also
16056 // We are purposefully using `!(android < 4)` to cover the case when `android` is undefined
16058 history: !!($window.history && $window.history.pushState && !(android < 4) && !boxee),
16060 hasEvent: function(event) {
16061 // IE9 implements 'input' event it's so fubared that we rather pretend that it doesn't have
16062 // it. In particular the event is not fired when backspace or delete key are pressed or
16063 // when cut operation is performed.
16064 // IE10+ implements 'input' event but it erroneously fires under various situations,
16065 // e.g. when placeholder changes, or a form is focused.
16066 if (event === 'input' && msie <= 11) return false;
16068 if (isUndefined(eventSupport[event])) {
16069 var divElm = document.createElement('div');
16070 eventSupport[event] = 'on' + event in divElm;
16073 return eventSupport[event];
16076 vendorPrefix: vendorPrefix,
16077 transitions: transitions,
16078 animations: animations,
16084 var $compileMinErr = minErr('$compile');
16088 * @name $templateRequest
16091 * The `$templateRequest` service downloads the provided template using `$http` and, upon success,
16092 * stores the contents inside of `$templateCache`. If the HTTP request fails or the response data
16093 * of the HTTP request is empty, a `$compile` error will be thrown (the exception can be thwarted
16094 * by setting the 2nd parameter of the function to true).
16096 * @param {string} tpl The HTTP request template URL
16097 * @param {boolean=} ignoreRequestError Whether or not to ignore the exception when the request fails or the template is empty
16099 * @return {Promise} the HTTP Promise for the given.
16101 * @property {number} totalPendingRequests total amount of pending template requests being downloaded.
16103 function $TemplateRequestProvider() {
16104 this.$get = ['$templateCache', '$http', '$q', function($templateCache, $http, $q) {
16105 function handleRequestFn(tpl, ignoreRequestError) {
16106 handleRequestFn.totalPendingRequests++;
16108 var transformResponse = $http.defaults && $http.defaults.transformResponse;
16110 if (isArray(transformResponse)) {
16111 transformResponse = transformResponse.filter(function(transformer) {
16112 return transformer !== defaultHttpResponseTransform;
16114 } else if (transformResponse === defaultHttpResponseTransform) {
16115 transformResponse = null;
16118 var httpOptions = {
16119 cache: $templateCache,
16120 transformResponse: transformResponse
16123 return $http.get(tpl, httpOptions)
16124 ['finally'](function() {
16125 handleRequestFn.totalPendingRequests--;
16127 .then(function(response) {
16128 return response.data;
16131 function handleError(resp) {
16132 if (!ignoreRequestError) {
16133 throw $compileMinErr('tpload', 'Failed to load template: {0}', tpl);
16135 return $q.reject(resp);
16139 handleRequestFn.totalPendingRequests = 0;
16141 return handleRequestFn;
16145 function $$TestabilityProvider() {
16146 this.$get = ['$rootScope', '$browser', '$location',
16147 function($rootScope, $browser, $location) {
16150 * @name $testability
16153 * The private $$testability service provides a collection of methods for use when debugging
16154 * or by automated test and debugging tools.
16156 var testability = {};
16159 * @name $$testability#findBindings
16162 * Returns an array of elements that are bound (via ng-bind or {{}})
16163 * to expressions matching the input.
16165 * @param {Element} element The element root to search from.
16166 * @param {string} expression The binding expression to match.
16167 * @param {boolean} opt_exactMatch If true, only returns exact matches
16168 * for the expression. Filters and whitespace are ignored.
16170 testability.findBindings = function(element, expression, opt_exactMatch) {
16171 var bindings = element.getElementsByClassName('ng-binding');
16173 forEach(bindings, function(binding) {
16174 var dataBinding = angular.element(binding).data('$binding');
16176 forEach(dataBinding, function(bindingName) {
16177 if (opt_exactMatch) {
16178 var matcher = new RegExp('(^|\\s)' + escapeForRegexp(expression) + '(\\s|\\||$)');
16179 if (matcher.test(bindingName)) {
16180 matches.push(binding);
16183 if (bindingName.indexOf(expression) != -1) {
16184 matches.push(binding);
16194 * @name $$testability#findModels
16197 * Returns an array of elements that are two-way found via ng-model to
16198 * expressions matching the input.
16200 * @param {Element} element The element root to search from.
16201 * @param {string} expression The model expression to match.
16202 * @param {boolean} opt_exactMatch If true, only returns exact matches
16203 * for the expression.
16205 testability.findModels = function(element, expression, opt_exactMatch) {
16206 var prefixes = ['ng-', 'data-ng-', 'ng\\:'];
16207 for (var p = 0; p < prefixes.length; ++p) {
16208 var attributeEquals = opt_exactMatch ? '=' : '*=';
16209 var selector = '[' + prefixes[p] + 'model' + attributeEquals + '"' + expression + '"]';
16210 var elements = element.querySelectorAll(selector);
16211 if (elements.length) {
16218 * @name $$testability#getLocation
16221 * Shortcut for getting the location in a browser agnostic way. Returns
16222 * the path, search, and hash. (e.g. /path?a=b#hash)
16224 testability.getLocation = function() {
16225 return $location.url();
16229 * @name $$testability#setLocation
16232 * Shortcut for navigating to a location without doing a full page reload.
16234 * @param {string} url The location url (path, search and hash,
16235 * e.g. /path?a=b#hash) to go to.
16237 testability.setLocation = function(url) {
16238 if (url !== $location.url()) {
16239 $location.url(url);
16240 $rootScope.$digest();
16245 * @name $$testability#whenStable
16248 * Calls the callback when $timeout and $http requests are completed.
16250 * @param {function} callback
16252 testability.whenStable = function(callback) {
16253 $browser.notifyWhenNoOutstandingRequests(callback);
16256 return testability;
16260 function $TimeoutProvider() {
16261 this.$get = ['$rootScope', '$browser', '$q', '$$q', '$exceptionHandler',
16262 function($rootScope, $browser, $q, $$q, $exceptionHandler) {
16263 var deferreds = {};
16271 * Angular's wrapper for `window.setTimeout`. The `fn` function is wrapped into a try/catch
16272 * block and delegates any exceptions to
16273 * {@link ng.$exceptionHandler $exceptionHandler} service.
16275 * The return value of registering a timeout function is a promise, which will be resolved when
16276 * the timeout is reached and the timeout function is executed.
16278 * To cancel a timeout request, call `$timeout.cancel(promise)`.
16280 * In tests you can use {@link ngMock.$timeout `$timeout.flush()`} to
16281 * synchronously flush the queue of deferred functions.
16283 * @param {function()} fn A function, whose execution should be delayed.
16284 * @param {number=} [delay=0] Delay in milliseconds.
16285 * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
16286 * will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
16287 * @returns {Promise} Promise that will be resolved when the timeout is reached. The value this
16288 * promise will be resolved with is the return value of the `fn` function.
16291 function timeout(fn, delay, invokeApply) {
16292 var skipApply = (isDefined(invokeApply) && !invokeApply),
16293 deferred = (skipApply ? $$q : $q).defer(),
16294 promise = deferred.promise,
16297 timeoutId = $browser.defer(function() {
16299 deferred.resolve(fn());
16301 deferred.reject(e);
16302 $exceptionHandler(e);
16305 delete deferreds[promise.$$timeoutId];
16308 if (!skipApply) $rootScope.$apply();
16311 promise.$$timeoutId = timeoutId;
16312 deferreds[timeoutId] = deferred;
16320 * @name $timeout#cancel
16323 * Cancels a task associated with the `promise`. As a result of this, the promise will be
16324 * resolved with a rejection.
16326 * @param {Promise=} promise Promise returned by the `$timeout` function.
16327 * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully
16330 timeout.cancel = function(promise) {
16331 if (promise && promise.$$timeoutId in deferreds) {
16332 deferreds[promise.$$timeoutId].reject('canceled');
16333 delete deferreds[promise.$$timeoutId];
16334 return $browser.defer.cancel(promise.$$timeoutId);
16343 // NOTE: The usage of window and document instead of $window and $document here is
16344 // deliberate. This service depends on the specific behavior of anchor nodes created by the
16345 // browser (resolving and parsing URLs) that is unlikely to be provided by mock objects and
16346 // cause us to break tests. In addition, when the browser resolves a URL for XHR, it
16347 // doesn't know about mocked locations and resolves URLs to the real document - which is
16348 // exactly the behavior needed here. There is little value is mocking these out for this
16350 var urlParsingNode = document.createElement("a");
16351 var originUrl = urlResolve(window.location.href);
16356 * Implementation Notes for non-IE browsers
16357 * ----------------------------------------
16358 * Assigning a URL to the href property of an anchor DOM node, even one attached to the DOM,
16359 * results both in the normalizing and parsing of the URL. Normalizing means that a relative
16360 * URL will be resolved into an absolute URL in the context of the application document.
16361 * Parsing means that the anchor node's host, hostname, protocol, port, pathname and related
16362 * properties are all populated to reflect the normalized URL. This approach has wide
16363 * compatibility - Safari 1+, Mozilla 1+, Opera 7+,e etc. See
16364 * http://www.aptana.com/reference/html/api/HTMLAnchorElement.html
16366 * Implementation Notes for IE
16367 * ---------------------------
16368 * IE >= 8 and <= 10 normalizes the URL when assigned to the anchor node similar to the other
16369 * browsers. However, the parsed components will not be set if the URL assigned did not specify
16370 * them. (e.g. if you assign a.href = "foo", then a.protocol, a.host, etc. will be empty.) We
16371 * work around that by performing the parsing in a 2nd step by taking a previously normalized
16372 * URL (e.g. by assigning to a.href) and assigning it a.href again. This correctly populates the
16373 * properties such as protocol, hostname, port, etc.
16375 * IE7 does not normalize the URL when assigned to an anchor node. (Apparently, it does, if one
16376 * uses the inner HTML approach to assign the URL as part of an HTML snippet -
16377 * http://stackoverflow.com/a/472729) However, setting img[src] does normalize the URL.
16378 * Unfortunately, setting img[src] to something like "javascript:foo" on IE throws an exception.
16379 * Since the primary usage for normalizing URLs is to sanitize such URLs, we can't use that
16380 * method and IE < 8 is unsupported.
16383 * http://developer.mozilla.org/en-US/docs/Web/API/HTMLAnchorElement
16384 * http://www.aptana.com/reference/html/api/HTMLAnchorElement.html
16385 * http://url.spec.whatwg.org/#urlutils
16386 * https://github.com/angular/angular.js/pull/2902
16387 * http://james.padolsey.com/javascript/parsing-urls-with-the-dom/
16390 * @param {string} url The URL to be parsed.
16391 * @description Normalizes and parses a URL.
16392 * @returns {object} Returns the normalized URL as a dictionary.
16394 * | member name | Description |
16395 * |---------------|----------------|
16396 * | href | A normalized version of the provided URL if it was not an absolute URL |
16397 * | protocol | The protocol including the trailing colon |
16398 * | host | The host and port (if the port is non-default) of the normalizedUrl |
16399 * | search | The search params, minus the question mark |
16400 * | hash | The hash string, minus the hash symbol
16401 * | hostname | The hostname
16402 * | port | The port, without ":"
16403 * | pathname | The pathname, beginning with "/"
16406 function urlResolve(url) {
16410 // Normalize before parse. Refer Implementation Notes on why this is
16411 // done in two steps on IE.
16412 urlParsingNode.setAttribute("href", href);
16413 href = urlParsingNode.href;
16416 urlParsingNode.setAttribute('href', href);
16418 // urlParsingNode provides the UrlUtils interface - http://url.spec.whatwg.org/#urlutils
16420 href: urlParsingNode.href,
16421 protocol: urlParsingNode.protocol ? urlParsingNode.protocol.replace(/:$/, '') : '',
16422 host: urlParsingNode.host,
16423 search: urlParsingNode.search ? urlParsingNode.search.replace(/^\?/, '') : '',
16424 hash: urlParsingNode.hash ? urlParsingNode.hash.replace(/^#/, '') : '',
16425 hostname: urlParsingNode.hostname,
16426 port: urlParsingNode.port,
16427 pathname: (urlParsingNode.pathname.charAt(0) === '/')
16428 ? urlParsingNode.pathname
16429 : '/' + urlParsingNode.pathname
16434 * Parse a request URL and determine whether this is a same-origin request as the application document.
16436 * @param {string|object} requestUrl The url of the request as a string that will be resolved
16437 * or a parsed URL object.
16438 * @returns {boolean} Whether the request is for the same origin as the application document.
16440 function urlIsSameOrigin(requestUrl) {
16441 var parsed = (isString(requestUrl)) ? urlResolve(requestUrl) : requestUrl;
16442 return (parsed.protocol === originUrl.protocol &&
16443 parsed.host === originUrl.host);
16451 * A reference to the browser's `window` object. While `window`
16452 * is globally available in JavaScript, it causes testability problems, because
16453 * it is a global variable. In angular we always refer to it through the
16454 * `$window` service, so it may be overridden, removed or mocked for testing.
16456 * Expressions, like the one defined for the `ngClick` directive in the example
16457 * below, are evaluated with respect to the current scope. Therefore, there is
16458 * no risk of inadvertently coding in a dependency on a global value in such an
16462 <example module="windowExample">
16463 <file name="index.html">
16465 angular.module('windowExample', [])
16466 .controller('ExampleController', ['$scope', '$window', function($scope, $window) {
16467 $scope.greeting = 'Hello, World!';
16468 $scope.doGreeting = function(greeting) {
16469 $window.alert(greeting);
16473 <div ng-controller="ExampleController">
16474 <input type="text" ng-model="greeting" />
16475 <button ng-click="doGreeting(greeting)">ALERT</button>
16478 <file name="protractor.js" type="protractor">
16479 it('should display the greeting in the input box', function() {
16480 element(by.model('greeting')).sendKeys('Hello, E2E Tests');
16481 // If we click the button it will block the test runner
16482 // element(':button').click();
16487 function $WindowProvider() {
16488 this.$get = valueFn(window);
16491 /* global currencyFilter: true,
16493 filterFilter: true,
16495 limitToFilter: true,
16496 lowercaseFilter: true,
16497 numberFilter: true,
16498 orderByFilter: true,
16499 uppercaseFilter: true,
16504 * @name $filterProvider
16507 * Filters are just functions which transform input to an output. However filters need to be
16508 * Dependency Injected. To achieve this a filter definition consists of a factory function which is
16509 * annotated with dependencies and is responsible for creating a filter function.
16512 * // Filter registration
16513 * function MyModule($provide, $filterProvider) {
16514 * // create a service to demonstrate injection (not always needed)
16515 * $provide.value('greet', function(name){
16516 * return 'Hello ' + name + '!';
16519 * // register a filter factory which uses the
16520 * // greet service to demonstrate DI.
16521 * $filterProvider.register('greet', function(greet){
16522 * // return the filter function which uses the greet service
16523 * // to generate salutation
16524 * return function(text) {
16525 * // filters need to be forgiving so check input validity
16526 * return text && greet(text) || text;
16532 * The filter function is registered with the `$injector` under the filter name suffix with
16536 * it('should be the same instance', inject(
16537 * function($filterProvider) {
16538 * $filterProvider.register('reverse', function(){
16542 * function($filter, reverseFilter) {
16543 * expect($filter('reverse')).toBe(reverseFilter);
16548 * For more information about how angular filters work, and how to create your own filters, see
16549 * {@link guide/filter Filters} in the Angular Developer Guide.
16557 * Filters are used for formatting data displayed to the user.
16559 * The general syntax in templates is as follows:
16561 * {{ expression [| filter_name[:parameter_value] ... ] }}
16563 * @param {String} name Name of the filter function to retrieve
16564 * @return {Function} the filter function
16566 <example name="$filter" module="filterExample">
16567 <file name="index.html">
16568 <div ng-controller="MainCtrl">
16569 <h3>{{ originalText }}</h3>
16570 <h3>{{ filteredText }}</h3>
16574 <file name="script.js">
16575 angular.module('filterExample', [])
16576 .controller('MainCtrl', function($scope, $filter) {
16577 $scope.originalText = 'hello';
16578 $scope.filteredText = $filter('uppercase')($scope.originalText);
16583 $FilterProvider.$inject = ['$provide'];
16584 function $FilterProvider($provide) {
16585 var suffix = 'Filter';
16589 * @name $filterProvider#register
16590 * @param {string|Object} name Name of the filter function, or an object map of filters where
16591 * the keys are the filter names and the values are the filter factories.
16592 * @returns {Object} Registered filter instance, or if a map of filters was provided then a map
16593 * of the registered filter instances.
16595 function register(name, factory) {
16596 if (isObject(name)) {
16598 forEach(name, function(filter, key) {
16599 filters[key] = register(key, filter);
16603 return $provide.factory(name + suffix, factory);
16606 this.register = register;
16608 this.$get = ['$injector', function($injector) {
16609 return function(name) {
16610 return $injector.get(name + suffix);
16614 ////////////////////////////////////////
16617 currencyFilter: false,
16619 filterFilter: false,
16621 limitToFilter: false,
16622 lowercaseFilter: false,
16623 numberFilter: false,
16624 orderByFilter: false,
16625 uppercaseFilter: false,
16628 register('currency', currencyFilter);
16629 register('date', dateFilter);
16630 register('filter', filterFilter);
16631 register('json', jsonFilter);
16632 register('limitTo', limitToFilter);
16633 register('lowercase', lowercaseFilter);
16634 register('number', numberFilter);
16635 register('orderBy', orderByFilter);
16636 register('uppercase', uppercaseFilter);
16645 * Selects a subset of items from `array` and returns it as a new array.
16647 * @param {Array} array The source array.
16648 * @param {string|Object|function()} expression The predicate to be used for selecting items from
16653 * - `string`: The string is used for matching against the contents of the `array`. All strings or
16654 * objects with string properties in `array` that match this string will be returned. This also
16655 * applies to nested object properties.
16656 * The predicate can be negated by prefixing the string with `!`.
16658 * - `Object`: A pattern object can be used to filter specific properties on objects contained
16659 * by `array`. For example `{name:"M", phone:"1"}` predicate will return an array of items
16660 * which have property `name` containing "M" and property `phone` containing "1". A special
16661 * property name `$` can be used (as in `{$:"text"}`) to accept a match against any
16662 * property of the object or its nested object properties. That's equivalent to the simple
16663 * substring match with a `string` as described above. The predicate can be negated by prefixing
16664 * the string with `!`.
16665 * For example `{name: "!M"}` predicate will return an array of items which have property `name`
16666 * not containing "M".
16668 * Note that a named property will match properties on the same level only, while the special
16669 * `$` property will match properties on the same level or deeper. E.g. an array item like
16670 * `{name: {first: 'John', last: 'Doe'}}` will **not** be matched by `{name: 'John'}`, but
16671 * **will** be matched by `{$: 'John'}`.
16673 * - `function(value, index)`: A predicate function can be used to write arbitrary filters. The
16674 * function is called for each element of `array`. The final result is an array of those
16675 * elements that the predicate returned true for.
16677 * @param {function(actual, expected)|true|undefined} comparator Comparator which is used in
16678 * determining if the expected value (from the filter expression) and actual value (from
16679 * the object in the array) should be considered a match.
16683 * - `function(actual, expected)`:
16684 * The function will be given the object value and the predicate value to compare and
16685 * should return true if both values should be considered equal.
16687 * - `true`: A shorthand for `function(actual, expected) { return angular.equals(actual, expected)}`.
16688 * This is essentially strict comparison of expected and actual.
16690 * - `false|undefined`: A short hand for a function which will look for a substring match in case
16695 <file name="index.html">
16696 <div ng-init="friends = [{name:'John', phone:'555-1276'},
16697 {name:'Mary', phone:'800-BIG-MARY'},
16698 {name:'Mike', phone:'555-4321'},
16699 {name:'Adam', phone:'555-5678'},
16700 {name:'Julie', phone:'555-8765'},
16701 {name:'Juliette', phone:'555-5678'}]"></div>
16703 Search: <input ng-model="searchText">
16704 <table id="searchTextResults">
16705 <tr><th>Name</th><th>Phone</th></tr>
16706 <tr ng-repeat="friend in friends | filter:searchText">
16707 <td>{{friend.name}}</td>
16708 <td>{{friend.phone}}</td>
16712 Any: <input ng-model="search.$"> <br>
16713 Name only <input ng-model="search.name"><br>
16714 Phone only <input ng-model="search.phone"><br>
16715 Equality <input type="checkbox" ng-model="strict"><br>
16716 <table id="searchObjResults">
16717 <tr><th>Name</th><th>Phone</th></tr>
16718 <tr ng-repeat="friendObj in friends | filter:search:strict">
16719 <td>{{friendObj.name}}</td>
16720 <td>{{friendObj.phone}}</td>
16724 <file name="protractor.js" type="protractor">
16725 var expectFriendNames = function(expectedNames, key) {
16726 element.all(by.repeater(key + ' in friends').column(key + '.name')).then(function(arr) {
16727 arr.forEach(function(wd, i) {
16728 expect(wd.getText()).toMatch(expectedNames[i]);
16733 it('should search across all fields when filtering with a string', function() {
16734 var searchText = element(by.model('searchText'));
16735 searchText.clear();
16736 searchText.sendKeys('m');
16737 expectFriendNames(['Mary', 'Mike', 'Adam'], 'friend');
16739 searchText.clear();
16740 searchText.sendKeys('76');
16741 expectFriendNames(['John', 'Julie'], 'friend');
16744 it('should search in specific fields when filtering with a predicate object', function() {
16745 var searchAny = element(by.model('search.$'));
16747 searchAny.sendKeys('i');
16748 expectFriendNames(['Mary', 'Mike', 'Julie', 'Juliette'], 'friendObj');
16750 it('should use a equal comparison when comparator is true', function() {
16751 var searchName = element(by.model('search.name'));
16752 var strict = element(by.model('strict'));
16753 searchName.clear();
16754 searchName.sendKeys('Julie');
16756 expectFriendNames(['Julie'], 'friendObj');
16761 function filterFilter() {
16762 return function(array, expression, comparator) {
16763 if (!isArray(array)) return array;
16766 var matchAgainstAnyProp;
16768 switch (typeof expression) {
16770 predicateFn = expression;
16775 matchAgainstAnyProp = true;
16779 predicateFn = createPredicateFn(expression, comparator, matchAgainstAnyProp);
16785 return array.filter(predicateFn);
16789 // Helper functions for `filterFilter`
16790 function createPredicateFn(expression, comparator, matchAgainstAnyProp) {
16791 var shouldMatchPrimitives = isObject(expression) && ('$' in expression);
16794 if (comparator === true) {
16795 comparator = equals;
16796 } else if (!isFunction(comparator)) {
16797 comparator = function(actual, expected) {
16798 if (isObject(actual) || isObject(expected)) {
16799 // Prevent an object to be considered equal to a string like `'[object'`
16803 actual = lowercase('' + actual);
16804 expected = lowercase('' + expected);
16805 return actual.indexOf(expected) !== -1;
16809 predicateFn = function(item) {
16810 if (shouldMatchPrimitives && !isObject(item)) {
16811 return deepCompare(item, expression.$, comparator, false);
16813 return deepCompare(item, expression, comparator, matchAgainstAnyProp);
16816 return predicateFn;
16819 function deepCompare(actual, expected, comparator, matchAgainstAnyProp, dontMatchWholeObject) {
16820 var actualType = (actual !== null) ? typeof actual : 'null';
16821 var expectedType = (expected !== null) ? typeof expected : 'null';
16823 if ((expectedType === 'string') && (expected.charAt(0) === '!')) {
16824 return !deepCompare(actual, expected.substring(1), comparator, matchAgainstAnyProp);
16825 } else if (isArray(actual)) {
16826 // In case `actual` is an array, consider it a match
16827 // if ANY of it's items matches `expected`
16828 return actual.some(function(item) {
16829 return deepCompare(item, expected, comparator, matchAgainstAnyProp);
16833 switch (actualType) {
16836 if (matchAgainstAnyProp) {
16837 for (key in actual) {
16838 if ((key.charAt(0) !== '$') && deepCompare(actual[key], expected, comparator, true)) {
16842 return dontMatchWholeObject ? false : deepCompare(actual, expected, comparator, false);
16843 } else if (expectedType === 'object') {
16844 for (key in expected) {
16845 var expectedVal = expected[key];
16846 if (isFunction(expectedVal) || isUndefined(expectedVal)) {
16850 var matchAnyProperty = key === '$';
16851 var actualVal = matchAnyProperty ? actual : actual[key];
16852 if (!deepCompare(actualVal, expectedVal, comparator, matchAnyProperty, matchAnyProperty)) {
16858 return comparator(actual, expected);
16864 return comparator(actual, expected);
16874 * Formats a number as a currency (ie $1,234.56). When no currency symbol is provided, default
16875 * symbol for current locale is used.
16877 * @param {number} amount Input to filter.
16878 * @param {string=} symbol Currency symbol or identifier to be displayed.
16879 * @param {number=} fractionSize Number of decimal places to round the amount to, defaults to default max fraction size for current locale
16880 * @returns {string} Formatted number.
16884 <example module="currencyExample">
16885 <file name="index.html">
16887 angular.module('currencyExample', [])
16888 .controller('ExampleController', ['$scope', function($scope) {
16889 $scope.amount = 1234.56;
16892 <div ng-controller="ExampleController">
16893 <input type="number" ng-model="amount"> <br>
16894 default currency symbol ($): <span id="currency-default">{{amount | currency}}</span><br>
16895 custom currency identifier (USD$): <span id="currency-custom">{{amount | currency:"USD$"}}</span>
16896 no fractions (0): <span id="currency-no-fractions">{{amount | currency:"USD$":0}}</span>
16899 <file name="protractor.js" type="protractor">
16900 it('should init with 1234.56', function() {
16901 expect(element(by.id('currency-default')).getText()).toBe('$1,234.56');
16902 expect(element(by.id('currency-custom')).getText()).toBe('USD$1,234.56');
16903 expect(element(by.id('currency-no-fractions')).getText()).toBe('USD$1,235');
16905 it('should update', function() {
16906 if (browser.params.browser == 'safari') {
16907 // Safari does not understand the minus key. See
16908 // https://github.com/angular/protractor/issues/481
16911 element(by.model('amount')).clear();
16912 element(by.model('amount')).sendKeys('-1234');
16913 expect(element(by.id('currency-default')).getText()).toBe('($1,234.00)');
16914 expect(element(by.id('currency-custom')).getText()).toBe('(USD$1,234.00)');
16915 expect(element(by.id('currency-no-fractions')).getText()).toBe('(USD$1,234)');
16920 currencyFilter.$inject = ['$locale'];
16921 function currencyFilter($locale) {
16922 var formats = $locale.NUMBER_FORMATS;
16923 return function(amount, currencySymbol, fractionSize) {
16924 if (isUndefined(currencySymbol)) {
16925 currencySymbol = formats.CURRENCY_SYM;
16928 if (isUndefined(fractionSize)) {
16929 fractionSize = formats.PATTERNS[1].maxFrac;
16932 // if null or undefined pass it through
16933 return (amount == null)
16935 : formatNumber(amount, formats.PATTERNS[1], formats.GROUP_SEP, formats.DECIMAL_SEP, fractionSize).
16936 replace(/\u00A4/g, currencySymbol);
16946 * Formats a number as text.
16948 * If the input is not a number an empty string is returned.
16950 * @param {number|string} number Number to format.
16951 * @param {(number|string)=} fractionSize Number of decimal places to round the number to.
16952 * If this is not provided then the fraction size is computed from the current locale's number
16953 * formatting pattern. In the case of the default locale, it will be 3.
16954 * @returns {string} Number rounded to decimalPlaces and places a “,” after each third digit.
16957 <example module="numberFilterExample">
16958 <file name="index.html">
16960 angular.module('numberFilterExample', [])
16961 .controller('ExampleController', ['$scope', function($scope) {
16962 $scope.val = 1234.56789;
16965 <div ng-controller="ExampleController">
16966 Enter number: <input ng-model='val'><br>
16967 Default formatting: <span id='number-default'>{{val | number}}</span><br>
16968 No fractions: <span>{{val | number:0}}</span><br>
16969 Negative number: <span>{{-val | number:4}}</span>
16972 <file name="protractor.js" type="protractor">
16973 it('should format numbers', function() {
16974 expect(element(by.id('number-default')).getText()).toBe('1,234.568');
16975 expect(element(by.binding('val | number:0')).getText()).toBe('1,235');
16976 expect(element(by.binding('-val | number:4')).getText()).toBe('-1,234.5679');
16979 it('should update', function() {
16980 element(by.model('val')).clear();
16981 element(by.model('val')).sendKeys('3374.333');
16982 expect(element(by.id('number-default')).getText()).toBe('3,374.333');
16983 expect(element(by.binding('val | number:0')).getText()).toBe('3,374');
16984 expect(element(by.binding('-val | number:4')).getText()).toBe('-3,374.3330');
16991 numberFilter.$inject = ['$locale'];
16992 function numberFilter($locale) {
16993 var formats = $locale.NUMBER_FORMATS;
16994 return function(number, fractionSize) {
16996 // if null or undefined pass it through
16997 return (number == null)
16999 : formatNumber(number, formats.PATTERNS[0], formats.GROUP_SEP, formats.DECIMAL_SEP,
17004 var DECIMAL_SEP = '.';
17005 function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) {
17006 if (!isFinite(number) || isObject(number)) return '';
17008 var isNegative = number < 0;
17009 number = Math.abs(number);
17010 var numStr = number + '',
17014 var hasExponent = false;
17015 if (numStr.indexOf('e') !== -1) {
17016 var match = numStr.match(/([\d\.]+)e(-?)(\d+)/);
17017 if (match && match[2] == '-' && match[3] > fractionSize + 1) {
17020 formatedText = numStr;
17021 hasExponent = true;
17025 if (!hasExponent) {
17026 var fractionLen = (numStr.split(DECIMAL_SEP)[1] || '').length;
17028 // determine fractionSize if it is not specified
17029 if (isUndefined(fractionSize)) {
17030 fractionSize = Math.min(Math.max(pattern.minFrac, fractionLen), pattern.maxFrac);
17033 // safely round numbers in JS without hitting imprecisions of floating-point arithmetics
17035 // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round
17036 number = +(Math.round(+(number.toString() + 'e' + fractionSize)).toString() + 'e' + -fractionSize);
17038 var fraction = ('' + number).split(DECIMAL_SEP);
17039 var whole = fraction[0];
17040 fraction = fraction[1] || '';
17043 lgroup = pattern.lgSize,
17044 group = pattern.gSize;
17046 if (whole.length >= (lgroup + group)) {
17047 pos = whole.length - lgroup;
17048 for (i = 0; i < pos; i++) {
17049 if ((pos - i) % group === 0 && i !== 0) {
17050 formatedText += groupSep;
17052 formatedText += whole.charAt(i);
17056 for (i = pos; i < whole.length; i++) {
17057 if ((whole.length - i) % lgroup === 0 && i !== 0) {
17058 formatedText += groupSep;
17060 formatedText += whole.charAt(i);
17063 // format fraction part.
17064 while (fraction.length < fractionSize) {
17068 if (fractionSize && fractionSize !== "0") formatedText += decimalSep + fraction.substr(0, fractionSize);
17070 if (fractionSize > 0 && number < 1) {
17071 formatedText = number.toFixed(fractionSize);
17072 number = parseFloat(formatedText);
17076 if (number === 0) {
17077 isNegative = false;
17080 parts.push(isNegative ? pattern.negPre : pattern.posPre,
17082 isNegative ? pattern.negSuf : pattern.posSuf);
17083 return parts.join('');
17086 function padNumber(num, digits, trim) {
17093 while (num.length < digits) num = '0' + num;
17095 num = num.substr(num.length - digits);
17100 function dateGetter(name, size, offset, trim) {
17101 offset = offset || 0;
17102 return function(date) {
17103 var value = date['get' + name]();
17104 if (offset > 0 || value > -offset)
17106 if (value === 0 && offset == -12) value = 12;
17107 return padNumber(value, size, trim);
17111 function dateStrGetter(name, shortForm) {
17112 return function(date, formats) {
17113 var value = date['get' + name]();
17114 var get = uppercase(shortForm ? ('SHORT' + name) : name);
17116 return formats[get][value];
17120 function timeZoneGetter(date) {
17121 var zone = -1 * date.getTimezoneOffset();
17122 var paddedZone = (zone >= 0) ? "+" : "";
17124 paddedZone += padNumber(Math[zone > 0 ? 'floor' : 'ceil'](zone / 60), 2) +
17125 padNumber(Math.abs(zone % 60), 2);
17130 function getFirstThursdayOfYear(year) {
17131 // 0 = index of January
17132 var dayOfWeekOnFirst = (new Date(year, 0, 1)).getDay();
17133 // 4 = index of Thursday (+1 to account for 1st = 5)
17134 // 11 = index of *next* Thursday (+1 account for 1st = 12)
17135 return new Date(year, 0, ((dayOfWeekOnFirst <= 4) ? 5 : 12) - dayOfWeekOnFirst);
17138 function getThursdayThisWeek(datetime) {
17139 return new Date(datetime.getFullYear(), datetime.getMonth(),
17140 // 4 = index of Thursday
17141 datetime.getDate() + (4 - datetime.getDay()));
17144 function weekGetter(size) {
17145 return function(date) {
17146 var firstThurs = getFirstThursdayOfYear(date.getFullYear()),
17147 thisThurs = getThursdayThisWeek(date);
17149 var diff = +thisThurs - +firstThurs,
17150 result = 1 + Math.round(diff / 6.048e8); // 6.048e8 ms per week
17152 return padNumber(result, size);
17156 function ampmGetter(date, formats) {
17157 return date.getHours() < 12 ? formats.AMPMS[0] : formats.AMPMS[1];
17160 function eraGetter(date, formats) {
17161 return date.getFullYear() <= 0 ? formats.ERAS[0] : formats.ERAS[1];
17164 function longEraGetter(date, formats) {
17165 return date.getFullYear() <= 0 ? formats.ERANAMES[0] : formats.ERANAMES[1];
17168 var DATE_FORMATS = {
17169 yyyy: dateGetter('FullYear', 4),
17170 yy: dateGetter('FullYear', 2, 0, true),
17171 y: dateGetter('FullYear', 1),
17172 MMMM: dateStrGetter('Month'),
17173 MMM: dateStrGetter('Month', true),
17174 MM: dateGetter('Month', 2, 1),
17175 M: dateGetter('Month', 1, 1),
17176 dd: dateGetter('Date', 2),
17177 d: dateGetter('Date', 1),
17178 HH: dateGetter('Hours', 2),
17179 H: dateGetter('Hours', 1),
17180 hh: dateGetter('Hours', 2, -12),
17181 h: dateGetter('Hours', 1, -12),
17182 mm: dateGetter('Minutes', 2),
17183 m: dateGetter('Minutes', 1),
17184 ss: dateGetter('Seconds', 2),
17185 s: dateGetter('Seconds', 1),
17186 // while ISO 8601 requires fractions to be prefixed with `.` or `,`
17187 // we can be just safely rely on using `sss` since we currently don't support single or two digit fractions
17188 sss: dateGetter('Milliseconds', 3),
17189 EEEE: dateStrGetter('Day'),
17190 EEE: dateStrGetter('Day', true),
17198 GGGG: longEraGetter
17201 var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZEwG']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z|G+|w+))(.*)/,
17202 NUMBER_STRING = /^\-?\d+$/;
17210 * Formats `date` to a string based on the requested `format`.
17212 * `format` string can be composed of the following elements:
17214 * * `'yyyy'`: 4 digit representation of year (e.g. AD 1 => 0001, AD 2010 => 2010)
17215 * * `'yy'`: 2 digit representation of year, padded (00-99). (e.g. AD 2001 => 01, AD 2010 => 10)
17216 * * `'y'`: 1 digit representation of year, e.g. (AD 1 => 1, AD 199 => 199)
17217 * * `'MMMM'`: Month in year (January-December)
17218 * * `'MMM'`: Month in year (Jan-Dec)
17219 * * `'MM'`: Month in year, padded (01-12)
17220 * * `'M'`: Month in year (1-12)
17221 * * `'dd'`: Day in month, padded (01-31)
17222 * * `'d'`: Day in month (1-31)
17223 * * `'EEEE'`: Day in Week,(Sunday-Saturday)
17224 * * `'EEE'`: Day in Week, (Sun-Sat)
17225 * * `'HH'`: Hour in day, padded (00-23)
17226 * * `'H'`: Hour in day (0-23)
17227 * * `'hh'`: Hour in AM/PM, padded (01-12)
17228 * * `'h'`: Hour in AM/PM, (1-12)
17229 * * `'mm'`: Minute in hour, padded (00-59)
17230 * * `'m'`: Minute in hour (0-59)
17231 * * `'ss'`: Second in minute, padded (00-59)
17232 * * `'s'`: Second in minute (0-59)
17233 * * `'sss'`: Millisecond in second, padded (000-999)
17234 * * `'a'`: AM/PM marker
17235 * * `'Z'`: 4 digit (+sign) representation of the timezone offset (-1200-+1200)
17236 * * `'ww'`: Week of year, padded (00-53). Week 01 is the week with the first Thursday of the year
17237 * * `'w'`: Week of year (0-53). Week 1 is the week with the first Thursday of the year
17238 * * `'G'`, `'GG'`, `'GGG'`: The abbreviated form of the era string (e.g. 'AD')
17239 * * `'GGGG'`: The long form of the era string (e.g. 'Anno Domini')
17241 * `format` string can also be one of the following predefined
17242 * {@link guide/i18n localizable formats}:
17244 * * `'medium'`: equivalent to `'MMM d, y h:mm:ss a'` for en_US locale
17245 * (e.g. Sep 3, 2010 12:05:08 PM)
17246 * * `'short'`: equivalent to `'M/d/yy h:mm a'` for en_US locale (e.g. 9/3/10 12:05 PM)
17247 * * `'fullDate'`: equivalent to `'EEEE, MMMM d, y'` for en_US locale
17248 * (e.g. Friday, September 3, 2010)
17249 * * `'longDate'`: equivalent to `'MMMM d, y'` for en_US locale (e.g. September 3, 2010)
17250 * * `'mediumDate'`: equivalent to `'MMM d, y'` for en_US locale (e.g. Sep 3, 2010)
17251 * * `'shortDate'`: equivalent to `'M/d/yy'` for en_US locale (e.g. 9/3/10)
17252 * * `'mediumTime'`: equivalent to `'h:mm:ss a'` for en_US locale (e.g. 12:05:08 PM)
17253 * * `'shortTime'`: equivalent to `'h:mm a'` for en_US locale (e.g. 12:05 PM)
17255 * `format` string can contain literal values. These need to be escaped by surrounding with single quotes (e.g.
17256 * `"h 'in the morning'"`). In order to output a single quote, escape it - i.e., two single quotes in a sequence
17257 * (e.g. `"h 'o''clock'"`).
17259 * @param {(Date|number|string)} date Date to format either as Date object, milliseconds (string or
17260 * number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.sssZ and its
17261 * shorter versions like yyyy-MM-ddTHH:mmZ, yyyy-MM-dd or yyyyMMddTHHmmssZ). If no timezone is
17262 * specified in the string input, the time is considered to be in the local timezone.
17263 * @param {string=} format Formatting rules (see Description). If not specified,
17264 * `mediumDate` is used.
17265 * @param {string=} timezone Timezone to be used for formatting. Right now, only `'UTC'` is supported.
17266 * If not specified, the timezone of the browser will be used.
17267 * @returns {string} Formatted string or the input if input is not recognized as date/millis.
17271 <file name="index.html">
17272 <span ng-non-bindable>{{1288323623006 | date:'medium'}}</span>:
17273 <span>{{1288323623006 | date:'medium'}}</span><br>
17274 <span ng-non-bindable>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span>:
17275 <span>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span><br>
17276 <span ng-non-bindable>{{1288323623006 | date:'MM/dd/yyyy @ h:mma'}}</span>:
17277 <span>{{'1288323623006' | date:'MM/dd/yyyy @ h:mma'}}</span><br>
17278 <span ng-non-bindable>{{1288323623006 | date:"MM/dd/yyyy 'at' h:mma"}}</span>:
17279 <span>{{'1288323623006' | date:"MM/dd/yyyy 'at' h:mma"}}</span><br>
17281 <file name="protractor.js" type="protractor">
17282 it('should format date', function() {
17283 expect(element(by.binding("1288323623006 | date:'medium'")).getText()).
17284 toMatch(/Oct 2\d, 2010 \d{1,2}:\d{2}:\d{2} (AM|PM)/);
17285 expect(element(by.binding("1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'")).getText()).
17286 toMatch(/2010\-10\-2\d \d{2}:\d{2}:\d{2} (\-|\+)?\d{4}/);
17287 expect(element(by.binding("'1288323623006' | date:'MM/dd/yyyy @ h:mma'")).getText()).
17288 toMatch(/10\/2\d\/2010 @ \d{1,2}:\d{2}(AM|PM)/);
17289 expect(element(by.binding("'1288323623006' | date:\"MM/dd/yyyy 'at' h:mma\"")).getText()).
17290 toMatch(/10\/2\d\/2010 at \d{1,2}:\d{2}(AM|PM)/);
17295 dateFilter.$inject = ['$locale'];
17296 function dateFilter($locale) {
17299 var R_ISO8601_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/;
17300 // 1 2 3 4 5 6 7 8 9 10 11
17301 function jsonStringToDate(string) {
17303 if (match = string.match(R_ISO8601_STR)) {
17304 var date = new Date(0),
17307 dateSetter = match[8] ? date.setUTCFullYear : date.setFullYear,
17308 timeSetter = match[8] ? date.setUTCHours : date.setHours;
17311 tzHour = int(match[9] + match[10]);
17312 tzMin = int(match[9] + match[11]);
17314 dateSetter.call(date, int(match[1]), int(match[2]) - 1, int(match[3]));
17315 var h = int(match[4] || 0) - tzHour;
17316 var m = int(match[5] || 0) - tzMin;
17317 var s = int(match[6] || 0);
17318 var ms = Math.round(parseFloat('0.' + (match[7] || 0)) * 1000);
17319 timeSetter.call(date, h, m, s, ms);
17326 return function(date, format, timezone) {
17331 format = format || 'mediumDate';
17332 format = $locale.DATETIME_FORMATS[format] || format;
17333 if (isString(date)) {
17334 date = NUMBER_STRING.test(date) ? int(date) : jsonStringToDate(date);
17337 if (isNumber(date)) {
17338 date = new Date(date);
17341 if (!isDate(date)) {
17346 match = DATE_FORMATS_SPLIT.exec(format);
17348 parts = concat(parts, match, 1);
17349 format = parts.pop();
17351 parts.push(format);
17356 if (timezone && timezone === 'UTC') {
17357 date = new Date(date.getTime());
17358 date.setMinutes(date.getMinutes() + date.getTimezoneOffset());
17360 forEach(parts, function(value) {
17361 fn = DATE_FORMATS[value];
17362 text += fn ? fn(date, $locale.DATETIME_FORMATS)
17363 : value.replace(/(^'|'$)/g, '').replace(/''/g, "'");
17377 * Allows you to convert a JavaScript object into JSON string.
17379 * This filter is mostly useful for debugging. When using the double curly {{value}} notation
17380 * the binding is automatically converted to JSON.
17382 * @param {*} object Any JavaScript object (including arrays and primitive types) to filter.
17383 * @param {number=} spacing The number of spaces to use per indentation, defaults to 2.
17384 * @returns {string} JSON string.
17389 <file name="index.html">
17390 <pre id="default-spacing">{{ {'name':'value'} | json }}</pre>
17391 <pre id="custom-spacing">{{ {'name':'value'} | json:4 }}</pre>
17393 <file name="protractor.js" type="protractor">
17394 it('should jsonify filtered objects', function() {
17395 expect(element(by.id('default-spacing')).getText()).toMatch(/\{\n "name": ?"value"\n}/);
17396 expect(element(by.id('custom-spacing')).getText()).toMatch(/\{\n "name": ?"value"\n}/);
17402 function jsonFilter() {
17403 return function(object, spacing) {
17404 if (isUndefined(spacing)) {
17407 return toJson(object, spacing);
17417 * Converts string to lowercase.
17418 * @see angular.lowercase
17420 var lowercaseFilter = valueFn(lowercase);
17428 * Converts string to uppercase.
17429 * @see angular.uppercase
17431 var uppercaseFilter = valueFn(uppercase);
17439 * Creates a new array or string containing only a specified number of elements. The elements
17440 * are taken from either the beginning or the end of the source array, string or number, as specified by
17441 * the value and sign (positive or negative) of `limit`. If a number is used as input, it is
17442 * converted to a string.
17444 * @param {Array|string|number} input Source array, string or number to be limited.
17445 * @param {string|number} limit The length of the returned array or string. If the `limit` number
17446 * is positive, `limit` number of items from the beginning of the source array/string are copied.
17447 * If the number is negative, `limit` number of items from the end of the source array/string
17448 * are copied. The `limit` will be trimmed if it exceeds `array.length`
17449 * @returns {Array|string} A new sub-array or substring of length `limit` or less if input array
17450 * had less than `limit` elements.
17453 <example module="limitToExample">
17454 <file name="index.html">
17456 angular.module('limitToExample', [])
17457 .controller('ExampleController', ['$scope', function($scope) {
17458 $scope.numbers = [1,2,3,4,5,6,7,8,9];
17459 $scope.letters = "abcdefghi";
17460 $scope.longNumber = 2345432342;
17461 $scope.numLimit = 3;
17462 $scope.letterLimit = 3;
17463 $scope.longNumberLimit = 3;
17466 <div ng-controller="ExampleController">
17467 Limit {{numbers}} to: <input type="number" step="1" ng-model="numLimit">
17468 <p>Output numbers: {{ numbers | limitTo:numLimit }}</p>
17469 Limit {{letters}} to: <input type="number" step="1" ng-model="letterLimit">
17470 <p>Output letters: {{ letters | limitTo:letterLimit }}</p>
17471 Limit {{longNumber}} to: <input type="number" step="1" ng-model="longNumberLimit">
17472 <p>Output long number: {{ longNumber | limitTo:longNumberLimit }}</p>
17475 <file name="protractor.js" type="protractor">
17476 var numLimitInput = element(by.model('numLimit'));
17477 var letterLimitInput = element(by.model('letterLimit'));
17478 var longNumberLimitInput = element(by.model('longNumberLimit'));
17479 var limitedNumbers = element(by.binding('numbers | limitTo:numLimit'));
17480 var limitedLetters = element(by.binding('letters | limitTo:letterLimit'));
17481 var limitedLongNumber = element(by.binding('longNumber | limitTo:longNumberLimit'));
17483 it('should limit the number array to first three items', function() {
17484 expect(numLimitInput.getAttribute('value')).toBe('3');
17485 expect(letterLimitInput.getAttribute('value')).toBe('3');
17486 expect(longNumberLimitInput.getAttribute('value')).toBe('3');
17487 expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3]');
17488 expect(limitedLetters.getText()).toEqual('Output letters: abc');
17489 expect(limitedLongNumber.getText()).toEqual('Output long number: 234');
17492 // There is a bug in safari and protractor that doesn't like the minus key
17493 // it('should update the output when -3 is entered', function() {
17494 // numLimitInput.clear();
17495 // numLimitInput.sendKeys('-3');
17496 // letterLimitInput.clear();
17497 // letterLimitInput.sendKeys('-3');
17498 // longNumberLimitInput.clear();
17499 // longNumberLimitInput.sendKeys('-3');
17500 // expect(limitedNumbers.getText()).toEqual('Output numbers: [7,8,9]');
17501 // expect(limitedLetters.getText()).toEqual('Output letters: ghi');
17502 // expect(limitedLongNumber.getText()).toEqual('Output long number: 342');
17505 it('should not exceed the maximum size of input array', function() {
17506 numLimitInput.clear();
17507 numLimitInput.sendKeys('100');
17508 letterLimitInput.clear();
17509 letterLimitInput.sendKeys('100');
17510 longNumberLimitInput.clear();
17511 longNumberLimitInput.sendKeys('100');
17512 expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3,4,5,6,7,8,9]');
17513 expect(limitedLetters.getText()).toEqual('Output letters: abcdefghi');
17514 expect(limitedLongNumber.getText()).toEqual('Output long number: 2345432342');
17519 function limitToFilter() {
17520 return function(input, limit) {
17521 if (isNumber(input)) input = input.toString();
17522 if (!isArray(input) && !isString(input)) return input;
17524 if (Math.abs(Number(limit)) === Infinity) {
17525 limit = Number(limit);
17527 limit = int(limit);
17530 //NaN check on limit
17532 return limit > 0 ? input.slice(0, limit) : input.slice(limit);
17534 return isString(input) ? "" : [];
17545 * Orders a specified `array` by the `expression` predicate. It is ordered alphabetically
17546 * for strings and numerically for numbers. Note: if you notice numbers are not being sorted
17547 * correctly, make sure they are actually being saved as numbers and not strings.
17549 * @param {Array} array The array to sort.
17550 * @param {function(*)|string|Array.<(function(*)|string)>=} expression A predicate to be
17551 * used by the comparator to determine the order of elements.
17555 * - `function`: Getter function. The result of this function will be sorted using the
17556 * `<`, `=`, `>` operator.
17557 * - `string`: An Angular expression. The result of this expression is used to compare elements
17558 * (for example `name` to sort by a property called `name` or `name.substr(0, 3)` to sort by
17559 * 3 first characters of a property called `name`). The result of a constant expression
17560 * is interpreted as a property name to be used in comparisons (for example `"special name"`
17561 * to sort object by the value of their `special name` property). An expression can be
17562 * optionally prefixed with `+` or `-` to control ascending or descending sort order
17563 * (for example, `+name` or `-name`). If no property is provided, (e.g. `'+'`) then the array
17564 * element itself is used to compare where sorting.
17565 * - `Array`: An array of function or string predicates. The first predicate in the array
17566 * is used for sorting, but when two items are equivalent, the next predicate is used.
17568 * If the predicate is missing or empty then it defaults to `'+'`.
17570 * @param {boolean=} reverse Reverse the order of the array.
17571 * @returns {Array} Sorted copy of the source array.
17575 * The example below demonstrates a simple ngRepeat, where the data is sorted
17576 * by age in descending order (predicate is set to `'-age'`).
17577 * `reverse` is not set, which means it defaults to `false`.
17578 <example module="orderByExample">
17579 <file name="index.html">
17581 angular.module('orderByExample', [])
17582 .controller('ExampleController', ['$scope', function($scope) {
17584 [{name:'John', phone:'555-1212', age:10},
17585 {name:'Mary', phone:'555-9876', age:19},
17586 {name:'Mike', phone:'555-4321', age:21},
17587 {name:'Adam', phone:'555-5678', age:35},
17588 {name:'Julie', phone:'555-8765', age:29}];
17591 <div ng-controller="ExampleController">
17592 <table class="friend">
17595 <th>Phone Number</th>
17598 <tr ng-repeat="friend in friends | orderBy:'-age'">
17599 <td>{{friend.name}}</td>
17600 <td>{{friend.phone}}</td>
17601 <td>{{friend.age}}</td>
17608 * The predicate and reverse parameters can be controlled dynamically through scope properties,
17609 * as shown in the next example.
17611 <example module="orderByExample">
17612 <file name="index.html">
17614 angular.module('orderByExample', [])
17615 .controller('ExampleController', ['$scope', function($scope) {
17617 [{name:'John', phone:'555-1212', age:10},
17618 {name:'Mary', phone:'555-9876', age:19},
17619 {name:'Mike', phone:'555-4321', age:21},
17620 {name:'Adam', phone:'555-5678', age:35},
17621 {name:'Julie', phone:'555-8765', age:29}];
17622 $scope.predicate = '-age';
17625 <div ng-controller="ExampleController">
17626 <pre>Sorting predicate = {{predicate}}; reverse = {{reverse}}</pre>
17628 [ <a href="" ng-click="predicate=''">unsorted</a> ]
17629 <table class="friend">
17631 <th><a href="" ng-click="predicate = 'name'; reverse=false">Name</a>
17632 (<a href="" ng-click="predicate = '-name'; reverse=false">^</a>)</th>
17633 <th><a href="" ng-click="predicate = 'phone'; reverse=!reverse">Phone Number</a></th>
17634 <th><a href="" ng-click="predicate = 'age'; reverse=!reverse">Age</a></th>
17636 <tr ng-repeat="friend in friends | orderBy:predicate:reverse">
17637 <td>{{friend.name}}</td>
17638 <td>{{friend.phone}}</td>
17639 <td>{{friend.age}}</td>
17646 * It's also possible to call the orderBy filter manually, by injecting `$filter`, retrieving the
17647 * filter routine with `$filter('orderBy')`, and calling the returned filter routine with the
17648 * desired parameters.
17653 <example module="orderByExample">
17654 <file name="index.html">
17655 <div ng-controller="ExampleController">
17656 <table class="friend">
17658 <th><a href="" ng-click="reverse=false;order('name', false)">Name</a>
17659 (<a href="" ng-click="order('-name',false)">^</a>)</th>
17660 <th><a href="" ng-click="reverse=!reverse;order('phone', reverse)">Phone Number</a></th>
17661 <th><a href="" ng-click="reverse=!reverse;order('age',reverse)">Age</a></th>
17663 <tr ng-repeat="friend in friends">
17664 <td>{{friend.name}}</td>
17665 <td>{{friend.phone}}</td>
17666 <td>{{friend.age}}</td>
17672 <file name="script.js">
17673 angular.module('orderByExample', [])
17674 .controller('ExampleController', ['$scope', '$filter', function($scope, $filter) {
17675 var orderBy = $filter('orderBy');
17677 { name: 'John', phone: '555-1212', age: 10 },
17678 { name: 'Mary', phone: '555-9876', age: 19 },
17679 { name: 'Mike', phone: '555-4321', age: 21 },
17680 { name: 'Adam', phone: '555-5678', age: 35 },
17681 { name: 'Julie', phone: '555-8765', age: 29 }
17683 $scope.order = function(predicate, reverse) {
17684 $scope.friends = orderBy($scope.friends, predicate, reverse);
17686 $scope.order('-age',false);
17691 orderByFilter.$inject = ['$parse'];
17692 function orderByFilter($parse) {
17693 return function(array, sortPredicate, reverseOrder) {
17694 if (!(isArrayLike(array))) return array;
17695 sortPredicate = isArray(sortPredicate) ? sortPredicate : [sortPredicate];
17696 if (sortPredicate.length === 0) { sortPredicate = ['+']; }
17697 sortPredicate = sortPredicate.map(function(predicate) {
17698 var descending = false, get = predicate || identity;
17699 if (isString(predicate)) {
17700 if ((predicate.charAt(0) == '+' || predicate.charAt(0) == '-')) {
17701 descending = predicate.charAt(0) == '-';
17702 predicate = predicate.substring(1);
17704 if (predicate === '') {
17705 // Effectively no predicate was passed so we compare identity
17706 return reverseComparator(compare, descending);
17708 get = $parse(predicate);
17709 if (get.constant) {
17711 return reverseComparator(function(a, b) {
17712 return compare(a[key], b[key]);
17716 return reverseComparator(function(a, b) {
17717 return compare(get(a),get(b));
17720 return slice.call(array).sort(reverseComparator(comparator, reverseOrder));
17722 function comparator(o1, o2) {
17723 for (var i = 0; i < sortPredicate.length; i++) {
17724 var comp = sortPredicate[i](o1, o2);
17725 if (comp !== 0) return comp;
17729 function reverseComparator(comp, descending) {
17731 ? function(a, b) {return comp(b,a);}
17735 function isPrimitive(value) {
17736 switch (typeof value) {
17737 case 'number': /* falls through */
17738 case 'boolean': /* falls through */
17746 function objectToString(value) {
17747 if (value === null) return 'null';
17748 if (typeof value.valueOf === 'function') {
17749 value = value.valueOf();
17750 if (isPrimitive(value)) return value;
17752 if (typeof value.toString === 'function') {
17753 value = value.toString();
17754 if (isPrimitive(value)) return value;
17759 function compare(v1, v2) {
17760 var t1 = typeof v1;
17761 var t2 = typeof v2;
17762 if (t1 === t2 && t1 === "object") {
17763 v1 = objectToString(v1);
17764 v2 = objectToString(v2);
17767 if (t1 === "string") {
17768 v1 = v1.toLowerCase();
17769 v2 = v2.toLowerCase();
17771 if (v1 === v2) return 0;
17772 return v1 < v2 ? -1 : 1;
17774 return t1 < t2 ? -1 : 1;
17780 function ngDirective(directive) {
17781 if (isFunction(directive)) {
17786 directive.restrict = directive.restrict || 'AC';
17787 return valueFn(directive);
17796 * Modifies the default behavior of the html A tag so that the default action is prevented when
17797 * the href attribute is empty.
17799 * This change permits the easy creation of action links with the `ngClick` directive
17800 * without changing the location or causing page reloads, e.g.:
17801 * `<a href="" ng-click="list.addItem()">Add Item</a>`
17803 var htmlAnchorDirective = valueFn({
17805 compile: function(element, attr) {
17806 if (!attr.href && !attr.xlinkHref && !attr.name) {
17807 return function(scope, element) {
17808 // If the linked element is not an anchor tag anymore, do nothing
17809 if (element[0].nodeName.toLowerCase() !== 'a') return;
17811 // SVGAElement does not use the href attribute, but rather the 'xlinkHref' attribute.
17812 var href = toString.call(element.prop('href')) === '[object SVGAnimatedString]' ?
17813 'xlink:href' : 'href';
17814 element.on('click', function(event) {
17815 // if we have no href url, then don't navigate anywhere.
17816 if (!element.attr(href)) {
17817 event.preventDefault();
17832 * Using Angular markup like `{{hash}}` in an href attribute will
17833 * make the link go to the wrong URL if the user clicks it before
17834 * Angular has a chance to replace the `{{hash}}` markup with its
17835 * value. Until Angular replaces the markup the link will be broken
17836 * and will most likely return a 404 error. The `ngHref` directive
17837 * solves this problem.
17839 * The wrong way to write it:
17841 * <a href="http://www.gravatar.com/avatar/{{hash}}">link1</a>
17844 * The correct way to write it:
17846 * <a ng-href="http://www.gravatar.com/avatar/{{hash}}">link1</a>
17850 * @param {template} ngHref any string which can contain `{{}}` markup.
17853 * This example shows various combinations of `href`, `ng-href` and `ng-click` attributes
17854 * in links and their different behaviors:
17856 <file name="index.html">
17857 <input ng-model="value" /><br />
17858 <a id="link-1" href ng-click="value = 1">link 1</a> (link, don't reload)<br />
17859 <a id="link-2" href="" ng-click="value = 2">link 2</a> (link, don't reload)<br />
17860 <a id="link-3" ng-href="/{{'123'}}">link 3</a> (link, reload!)<br />
17861 <a id="link-4" href="" name="xx" ng-click="value = 4">anchor</a> (link, don't reload)<br />
17862 <a id="link-5" name="xxx" ng-click="value = 5">anchor</a> (no link)<br />
17863 <a id="link-6" ng-href="{{value}}">link</a> (link, change location)
17865 <file name="protractor.js" type="protractor">
17866 it('should execute ng-click but not reload when href without value', function() {
17867 element(by.id('link-1')).click();
17868 expect(element(by.model('value')).getAttribute('value')).toEqual('1');
17869 expect(element(by.id('link-1')).getAttribute('href')).toBe('');
17872 it('should execute ng-click but not reload when href empty string', function() {
17873 element(by.id('link-2')).click();
17874 expect(element(by.model('value')).getAttribute('value')).toEqual('2');
17875 expect(element(by.id('link-2')).getAttribute('href')).toBe('');
17878 it('should execute ng-click and change url when ng-href specified', function() {
17879 expect(element(by.id('link-3')).getAttribute('href')).toMatch(/\/123$/);
17881 element(by.id('link-3')).click();
17883 // At this point, we navigate away from an Angular page, so we need
17884 // to use browser.driver to get the base webdriver.
17886 browser.wait(function() {
17887 return browser.driver.getCurrentUrl().then(function(url) {
17888 return url.match(/\/123$/);
17890 }, 5000, 'page should navigate to /123');
17893 xit('should execute ng-click but not reload when href empty string and name specified', function() {
17894 element(by.id('link-4')).click();
17895 expect(element(by.model('value')).getAttribute('value')).toEqual('4');
17896 expect(element(by.id('link-4')).getAttribute('href')).toBe('');
17899 it('should execute ng-click but not reload when no href but name specified', function() {
17900 element(by.id('link-5')).click();
17901 expect(element(by.model('value')).getAttribute('value')).toEqual('5');
17902 expect(element(by.id('link-5')).getAttribute('href')).toBe(null);
17905 it('should only change url when only ng-href', function() {
17906 element(by.model('value')).clear();
17907 element(by.model('value')).sendKeys('6');
17908 expect(element(by.id('link-6')).getAttribute('href')).toMatch(/\/6$/);
17910 element(by.id('link-6')).click();
17912 // At this point, we navigate away from an Angular page, so we need
17913 // to use browser.driver to get the base webdriver.
17914 browser.wait(function() {
17915 return browser.driver.getCurrentUrl().then(function(url) {
17916 return url.match(/\/6$/);
17918 }, 5000, 'page should navigate to /6');
17931 * Using Angular markup like `{{hash}}` in a `src` attribute doesn't
17932 * work right: The browser will fetch from the URL with the literal
17933 * text `{{hash}}` until Angular replaces the expression inside
17934 * `{{hash}}`. The `ngSrc` directive solves this problem.
17936 * The buggy way to write it:
17938 * <img src="http://www.gravatar.com/avatar/{{hash}}"/>
17941 * The correct way to write it:
17943 * <img ng-src="http://www.gravatar.com/avatar/{{hash}}"/>
17947 * @param {template} ngSrc any string which can contain `{{}}` markup.
17957 * Using Angular markup like `{{hash}}` in a `srcset` attribute doesn't
17958 * work right: The browser will fetch from the URL with the literal
17959 * text `{{hash}}` until Angular replaces the expression inside
17960 * `{{hash}}`. The `ngSrcset` directive solves this problem.
17962 * The buggy way to write it:
17964 * <img srcset="http://www.gravatar.com/avatar/{{hash}} 2x"/>
17967 * The correct way to write it:
17969 * <img ng-srcset="http://www.gravatar.com/avatar/{{hash}} 2x"/>
17973 * @param {template} ngSrcset any string which can contain `{{}}` markup.
17984 * This directive sets the `disabled` attribute on the element if the
17985 * {@link guide/expression expression} inside `ngDisabled` evaluates to truthy.
17987 * A special directive is necessary because we cannot use interpolation inside the `disabled`
17988 * attribute. The following example would make the button enabled on Chrome/Firefox
17989 * but not on older IEs:
17992 * <!-- See below for an example of ng-disabled being used correctly -->
17993 * <div ng-init="isDisabled = false">
17994 * <button disabled="{{isDisabled}}">Disabled</button>
17998 * This is because the HTML specification does not require browsers to preserve the values of
17999 * boolean attributes such as `disabled` (Their presence means true and their absence means false.)
18000 * If we put an Angular interpolation expression into such an attribute then the
18001 * binding information would be lost when the browser removes the attribute.
18005 <file name="index.html">
18006 Click me to toggle: <input type="checkbox" ng-model="checked"><br/>
18007 <button ng-model="button" ng-disabled="checked">Button</button>
18009 <file name="protractor.js" type="protractor">
18010 it('should toggle button', function() {
18011 expect(element(by.css('button')).getAttribute('disabled')).toBeFalsy();
18012 element(by.model('checked')).click();
18013 expect(element(by.css('button')).getAttribute('disabled')).toBeTruthy();
18019 * @param {expression} ngDisabled If the {@link guide/expression expression} is truthy,
18020 * then the `disabled` attribute will be set on the element
18031 * The HTML specification does not require browsers to preserve the values of boolean attributes
18032 * such as checked. (Their presence means true and their absence means false.)
18033 * If we put an Angular interpolation expression into such an attribute then the
18034 * binding information would be lost when the browser removes the attribute.
18035 * The `ngChecked` directive solves this problem for the `checked` attribute.
18036 * This complementary directive is not removed by the browser and so provides
18037 * a permanent reliable place to store the binding information.
18040 <file name="index.html">
18041 Check me to check both: <input type="checkbox" ng-model="master"><br/>
18042 <input id="checkSlave" type="checkbox" ng-checked="master">
18044 <file name="protractor.js" type="protractor">
18045 it('should check both checkBoxes', function() {
18046 expect(element(by.id('checkSlave')).getAttribute('checked')).toBeFalsy();
18047 element(by.model('master')).click();
18048 expect(element(by.id('checkSlave')).getAttribute('checked')).toBeTruthy();
18054 * @param {expression} ngChecked If the {@link guide/expression expression} is truthy,
18055 * then special attribute "checked" will be set on the element
18066 * The HTML specification does not require browsers to preserve the values of boolean attributes
18067 * such as readonly. (Their presence means true and their absence means false.)
18068 * If we put an Angular interpolation expression into such an attribute then the
18069 * binding information would be lost when the browser removes the attribute.
18070 * The `ngReadonly` directive solves this problem for the `readonly` attribute.
18071 * This complementary directive is not removed by the browser and so provides
18072 * a permanent reliable place to store the binding information.
18075 <file name="index.html">
18076 Check me to make text readonly: <input type="checkbox" ng-model="checked"><br/>
18077 <input type="text" ng-readonly="checked" value="I'm Angular"/>
18079 <file name="protractor.js" type="protractor">
18080 it('should toggle readonly attr', function() {
18081 expect(element(by.css('[type="text"]')).getAttribute('readonly')).toBeFalsy();
18082 element(by.model('checked')).click();
18083 expect(element(by.css('[type="text"]')).getAttribute('readonly')).toBeTruthy();
18089 * @param {expression} ngReadonly If the {@link guide/expression expression} is truthy,
18090 * then special attribute "readonly" will be set on the element
18101 * The HTML specification does not require browsers to preserve the values of boolean attributes
18102 * such as selected. (Their presence means true and their absence means false.)
18103 * If we put an Angular interpolation expression into such an attribute then the
18104 * binding information would be lost when the browser removes the attribute.
18105 * The `ngSelected` directive solves this problem for the `selected` attribute.
18106 * This complementary directive is not removed by the browser and so provides
18107 * a permanent reliable place to store the binding information.
18111 <file name="index.html">
18112 Check me to select: <input type="checkbox" ng-model="selected"><br/>
18114 <option>Hello!</option>
18115 <option id="greet" ng-selected="selected">Greetings!</option>
18118 <file name="protractor.js" type="protractor">
18119 it('should select Greetings!', function() {
18120 expect(element(by.id('greet')).getAttribute('selected')).toBeFalsy();
18121 element(by.model('selected')).click();
18122 expect(element(by.id('greet')).getAttribute('selected')).toBeTruthy();
18128 * @param {expression} ngSelected If the {@link guide/expression expression} is truthy,
18129 * then special attribute "selected" will be set on the element
18139 * The HTML specification does not require browsers to preserve the values of boolean attributes
18140 * such as open. (Their presence means true and their absence means false.)
18141 * If we put an Angular interpolation expression into such an attribute then the
18142 * binding information would be lost when the browser removes the attribute.
18143 * The `ngOpen` directive solves this problem for the `open` attribute.
18144 * This complementary directive is not removed by the browser and so provides
18145 * a permanent reliable place to store the binding information.
18148 <file name="index.html">
18149 Check me check multiple: <input type="checkbox" ng-model="open"><br/>
18150 <details id="details" ng-open="open">
18151 <summary>Show/Hide me</summary>
18154 <file name="protractor.js" type="protractor">
18155 it('should toggle open', function() {
18156 expect(element(by.id('details')).getAttribute('open')).toBeFalsy();
18157 element(by.model('open')).click();
18158 expect(element(by.id('details')).getAttribute('open')).toBeTruthy();
18164 * @param {expression} ngOpen If the {@link guide/expression expression} is truthy,
18165 * then special attribute "open" will be set on the element
18168 var ngAttributeAliasDirectives = {};
18171 // boolean attrs are evaluated
18172 forEach(BOOLEAN_ATTR, function(propName, attrName) {
18173 // binding to multiple is not supported
18174 if (propName == "multiple") return;
18176 var normalized = directiveNormalize('ng-' + attrName);
18177 ngAttributeAliasDirectives[normalized] = function() {
18181 link: function(scope, element, attr) {
18182 scope.$watch(attr[normalized], function ngBooleanAttrWatchAction(value) {
18183 attr.$set(attrName, !!value);
18190 // aliased input attrs are evaluated
18191 forEach(ALIASED_ATTR, function(htmlAttr, ngAttr) {
18192 ngAttributeAliasDirectives[ngAttr] = function() {
18195 link: function(scope, element, attr) {
18196 //special case ngPattern when a literal regular expression value
18197 //is used as the expression (this way we don't have to watch anything).
18198 if (ngAttr === "ngPattern" && attr.ngPattern.charAt(0) == "/") {
18199 var match = attr.ngPattern.match(REGEX_STRING_REGEXP);
18201 attr.$set("ngPattern", new RegExp(match[1], match[2]));
18206 scope.$watch(attr[ngAttr], function ngAttrAliasWatchAction(value) {
18207 attr.$set(ngAttr, value);
18214 // ng-src, ng-srcset, ng-href are interpolated
18215 forEach(['src', 'srcset', 'href'], function(attrName) {
18216 var normalized = directiveNormalize('ng-' + attrName);
18217 ngAttributeAliasDirectives[normalized] = function() {
18219 priority: 99, // it needs to run after the attributes are interpolated
18220 link: function(scope, element, attr) {
18221 var propName = attrName,
18224 if (attrName === 'href' &&
18225 toString.call(element.prop('href')) === '[object SVGAnimatedString]') {
18226 name = 'xlinkHref';
18227 attr.$attr[name] = 'xlink:href';
18231 attr.$observe(normalized, function(value) {
18233 if (attrName === 'href') {
18234 attr.$set(name, null);
18239 attr.$set(name, value);
18241 // on IE, if "ng:src" directive declaration is used and "src" attribute doesn't exist
18242 // then calling element.setAttribute('src', 'foo') doesn't do anything, so we need
18243 // to set the property as well to achieve the desired effect.
18244 // we use attr[attrName] value since $set can sanitize the url.
18245 if (msie && propName) element.prop(propName, attr[name]);
18252 /* global -nullFormCtrl, -SUBMITTED_CLASS, addSetValidityMethod: true
18254 var nullFormCtrl = {
18256 $$renameControl: nullFormRenameControl,
18257 $removeControl: noop,
18258 $setValidity: noop,
18260 $setPristine: noop,
18261 $setSubmitted: noop
18263 SUBMITTED_CLASS = 'ng-submitted';
18265 function nullFormRenameControl(control, name) {
18266 control.$name = name;
18271 * @name form.FormController
18273 * @property {boolean} $pristine True if user has not interacted with the form yet.
18274 * @property {boolean} $dirty True if user has already interacted with the form.
18275 * @property {boolean} $valid True if all of the containing forms and controls are valid.
18276 * @property {boolean} $invalid True if at least one containing control or form is invalid.
18277 * @property {boolean} $submitted True if user has submitted the form even if its invalid.
18279 * @property {Object} $error Is an object hash, containing references to controls or
18280 * forms with failing validators, where:
18282 * - keys are validation tokens (error names),
18283 * - values are arrays of controls or forms that have a failing validator for given error name.
18285 * Built-in validation tokens:
18297 * - `datetimelocal`
18303 * `FormController` keeps track of all its controls and nested forms as well as the state of them,
18304 * such as being valid/invalid or dirty/pristine.
18306 * Each {@link ng.directive:form form} directive creates an instance
18307 * of `FormController`.
18310 //asks for $scope to fool the BC controller module
18311 FormController.$inject = ['$element', '$attrs', '$scope', '$animate', '$interpolate'];
18312 function FormController(element, attrs, $scope, $animate, $interpolate) {
18316 var parentForm = form.$$parentForm = element.parent().controller('form') || nullFormCtrl;
18320 form.$$success = {};
18321 form.$pending = undefined;
18322 form.$name = $interpolate(attrs.name || attrs.ngForm || '')($scope);
18323 form.$dirty = false;
18324 form.$pristine = true;
18325 form.$valid = true;
18326 form.$invalid = false;
18327 form.$submitted = false;
18329 parentForm.$addControl(form);
18333 * @name form.FormController#$rollbackViewValue
18336 * Rollback all form controls pending updates to the `$modelValue`.
18338 * Updates may be pending by a debounced event or because the input is waiting for a some future
18339 * event defined in `ng-model-options`. This method is typically needed by the reset button of
18340 * a form that uses `ng-model-options` to pend updates.
18342 form.$rollbackViewValue = function() {
18343 forEach(controls, function(control) {
18344 control.$rollbackViewValue();
18350 * @name form.FormController#$commitViewValue
18353 * Commit all form controls pending updates to the `$modelValue`.
18355 * Updates may be pending by a debounced event or because the input is waiting for a some future
18356 * event defined in `ng-model-options`. This method is rarely needed as `NgModelController`
18357 * usually handles calling this in response to input events.
18359 form.$commitViewValue = function() {
18360 forEach(controls, function(control) {
18361 control.$commitViewValue();
18367 * @name form.FormController#$addControl
18370 * Register a control with the form.
18372 * Input elements using ngModelController do this automatically when they are linked.
18374 form.$addControl = function(control) {
18375 // Breaking change - before, inputs whose name was "hasOwnProperty" were quietly ignored
18376 // and not added to the scope. Now we throw an error.
18377 assertNotHasOwnProperty(control.$name, 'input');
18378 controls.push(control);
18380 if (control.$name) {
18381 form[control.$name] = control;
18385 // Private API: rename a form control
18386 form.$$renameControl = function(control, newName) {
18387 var oldName = control.$name;
18389 if (form[oldName] === control) {
18390 delete form[oldName];
18392 form[newName] = control;
18393 control.$name = newName;
18398 * @name form.FormController#$removeControl
18401 * Deregister a control from the form.
18403 * Input elements using ngModelController do this automatically when they are destroyed.
18405 form.$removeControl = function(control) {
18406 if (control.$name && form[control.$name] === control) {
18407 delete form[control.$name];
18409 forEach(form.$pending, function(value, name) {
18410 form.$setValidity(name, null, control);
18412 forEach(form.$error, function(value, name) {
18413 form.$setValidity(name, null, control);
18415 forEach(form.$$success, function(value, name) {
18416 form.$setValidity(name, null, control);
18419 arrayRemove(controls, control);
18425 * @name form.FormController#$setValidity
18428 * Sets the validity of a form control.
18430 * This method will also propagate to parent forms.
18432 addSetValidityMethod({
18435 set: function(object, property, controller) {
18436 var list = object[property];
18438 object[property] = [controller];
18440 var index = list.indexOf(controller);
18441 if (index === -1) {
18442 list.push(controller);
18446 unset: function(object, property, controller) {
18447 var list = object[property];
18451 arrayRemove(list, controller);
18452 if (list.length === 0) {
18453 delete object[property];
18456 parentForm: parentForm,
18462 * @name form.FormController#$setDirty
18465 * Sets the form to a dirty state.
18467 * This method can be called to add the 'ng-dirty' class and set the form to a dirty
18468 * state (ng-dirty class). This method will also propagate to parent forms.
18470 form.$setDirty = function() {
18471 $animate.removeClass(element, PRISTINE_CLASS);
18472 $animate.addClass(element, DIRTY_CLASS);
18473 form.$dirty = true;
18474 form.$pristine = false;
18475 parentForm.$setDirty();
18480 * @name form.FormController#$setPristine
18483 * Sets the form to its pristine state.
18485 * This method can be called to remove the 'ng-dirty' class and set the form to its pristine
18486 * state (ng-pristine class). This method will also propagate to all the controls contained
18489 * Setting a form back to a pristine state is often useful when we want to 'reuse' a form after
18490 * saving or resetting it.
18492 form.$setPristine = function() {
18493 $animate.setClass(element, PRISTINE_CLASS, DIRTY_CLASS + ' ' + SUBMITTED_CLASS);
18494 form.$dirty = false;
18495 form.$pristine = true;
18496 form.$submitted = false;
18497 forEach(controls, function(control) {
18498 control.$setPristine();
18504 * @name form.FormController#$setUntouched
18507 * Sets the form to its untouched state.
18509 * This method can be called to remove the 'ng-touched' class and set the form controls to their
18510 * untouched state (ng-untouched class).
18512 * Setting a form controls back to their untouched state is often useful when setting the form
18513 * back to its pristine state.
18515 form.$setUntouched = function() {
18516 forEach(controls, function(control) {
18517 control.$setUntouched();
18523 * @name form.FormController#$setSubmitted
18526 * Sets the form to its submitted state.
18528 form.$setSubmitted = function() {
18529 $animate.addClass(element, SUBMITTED_CLASS);
18530 form.$submitted = true;
18531 parentForm.$setSubmitted();
18541 * Nestable alias of {@link ng.directive:form `form`} directive. HTML
18542 * does not allow nesting of form elements. It is useful to nest forms, for example if the validity of a
18543 * sub-group of controls needs to be determined.
18545 * Note: the purpose of `ngForm` is to group controls,
18546 * but not to be a replacement for the `<form>` tag with all of its capabilities
18547 * (e.g. posting to the server, ...).
18549 * @param {string=} ngForm|name Name of the form. If specified, the form controller will be published into
18550 * related scope, under this name.
18560 * Directive that instantiates
18561 * {@link form.FormController FormController}.
18563 * If the `name` attribute is specified, the form controller is published onto the current scope under
18566 * # Alias: {@link ng.directive:ngForm `ngForm`}
18568 * In Angular, forms can be nested. This means that the outer form is valid when all of the child
18569 * forms are valid as well. However, browsers do not allow nesting of `<form>` elements, so
18570 * Angular provides the {@link ng.directive:ngForm `ngForm`} directive which behaves identically to
18571 * `<form>` but can be nested. This allows you to have nested forms, which is very useful when
18572 * using Angular validation directives in forms that are dynamically generated using the
18573 * {@link ng.directive:ngRepeat `ngRepeat`} directive. Since you cannot dynamically generate the `name`
18574 * attribute of input elements using interpolation, you have to wrap each set of repeated inputs in an
18575 * `ngForm` directive and nest these in an outer `form` element.
18579 * - `ng-valid` is set if the form is valid.
18580 * - `ng-invalid` is set if the form is invalid.
18581 * - `ng-pristine` is set if the form is pristine.
18582 * - `ng-dirty` is set if the form is dirty.
18583 * - `ng-submitted` is set if the form was submitted.
18585 * Keep in mind that ngAnimate can detect each of these classes when added and removed.
18588 * # Submitting a form and preventing the default action
18590 * Since the role of forms in client-side Angular applications is different than in classical
18591 * roundtrip apps, it is desirable for the browser not to translate the form submission into a full
18592 * page reload that sends the data to the server. Instead some javascript logic should be triggered
18593 * to handle the form submission in an application-specific way.
18595 * For this reason, Angular prevents the default action (form submission to the server) unless the
18596 * `<form>` element has an `action` attribute specified.
18598 * You can use one of the following two ways to specify what javascript method should be called when
18599 * a form is submitted:
18601 * - {@link ng.directive:ngSubmit ngSubmit} directive on the form element
18602 * - {@link ng.directive:ngClick ngClick} directive on the first
18603 * button or input field of type submit (input[type=submit])
18605 * To prevent double execution of the handler, use only one of the {@link ng.directive:ngSubmit ngSubmit}
18606 * or {@link ng.directive:ngClick ngClick} directives.
18607 * This is because of the following form submission rules in the HTML specification:
18609 * - If a form has only one input field then hitting enter in this field triggers form submit
18611 * - if a form has 2+ input fields and no buttons or input[type=submit] then hitting enter
18612 * doesn't trigger submit
18613 * - if a form has one or more input fields and one or more buttons or input[type=submit] then
18614 * hitting enter in any of the input fields will trigger the click handler on the *first* button or
18615 * input[type=submit] (`ngClick`) *and* a submit handler on the enclosing form (`ngSubmit`)
18617 * Any pending `ngModelOptions` changes will take place immediately when an enclosing form is
18618 * submitted. Note that `ngClick` events will occur before the model is updated. Use `ngSubmit`
18619 * to have access to the updated model.
18621 * ## Animation Hooks
18623 * Animations in ngForm are triggered when any of the associated CSS classes are added and removed.
18624 * These classes are: `.ng-pristine`, `.ng-dirty`, `.ng-invalid` and `.ng-valid` as well as any
18625 * other validations that are performed within the form. Animations in ngForm are similar to how
18626 * they work in ngClass and animations can be hooked into using CSS transitions, keyframes as well
18627 * as JS animations.
18629 * The following example shows a simple way to utilize CSS transitions to style a form element
18630 * that has been rendered as invalid after it has been validated:
18633 * //be sure to include ngAnimate as a module to hook into more
18634 * //advanced animations
18636 * transition:0.5s linear all;
18637 * background: white;
18639 * .my-form.ng-invalid {
18646 <example deps="angular-animate.js" animations="true" fixBase="true" module="formExample">
18647 <file name="index.html">
18649 angular.module('formExample', [])
18650 .controller('FormController', ['$scope', function($scope) {
18651 $scope.userType = 'guest';
18656 -webkit-transition:all linear 0.5s;
18657 transition:all linear 0.5s;
18658 background: transparent;
18660 .my-form.ng-invalid {
18664 <form name="myForm" ng-controller="FormController" class="my-form">
18665 userType: <input name="input" ng-model="userType" required>
18666 <span class="error" ng-show="myForm.input.$error.required">Required!</span><br>
18667 <tt>userType = {{userType}}</tt><br>
18668 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br>
18669 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br>
18670 <tt>myForm.$valid = {{myForm.$valid}}</tt><br>
18671 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br>
18674 <file name="protractor.js" type="protractor">
18675 it('should initialize to model', function() {
18676 var userType = element(by.binding('userType'));
18677 var valid = element(by.binding('myForm.input.$valid'));
18679 expect(userType.getText()).toContain('guest');
18680 expect(valid.getText()).toContain('true');
18683 it('should be invalid if empty', function() {
18684 var userType = element(by.binding('userType'));
18685 var valid = element(by.binding('myForm.input.$valid'));
18686 var userInput = element(by.model('userType'));
18689 userInput.sendKeys('');
18691 expect(userType.getText()).toEqual('userType =');
18692 expect(valid.getText()).toContain('false');
18697 * @param {string=} name Name of the form. If specified, the form controller will be published into
18698 * related scope, under this name.
18700 var formDirectiveFactory = function(isNgForm) {
18701 return ['$timeout', function($timeout) {
18702 var formDirective = {
18704 restrict: isNgForm ? 'EAC' : 'E',
18705 controller: FormController,
18706 compile: function ngFormCompile(formElement, attr) {
18707 // Setup initial state of the control
18708 formElement.addClass(PRISTINE_CLASS).addClass(VALID_CLASS);
18710 var nameAttr = attr.name ? 'name' : (isNgForm && attr.ngForm ? 'ngForm' : false);
18713 pre: function ngFormPreLink(scope, formElement, attr, controller) {
18714 // if `action` attr is not present on the form, prevent the default action (submission)
18715 if (!('action' in attr)) {
18716 // we can't use jq events because if a form is destroyed during submission the default
18717 // action is not prevented. see #1238
18719 // IE 9 is not affected because it doesn't fire a submit event and try to do a full
18720 // page reload if the form was destroyed by submission of the form via a click handler
18721 // on a button in the form. Looks like an IE9 specific bug.
18722 var handleFormSubmission = function(event) {
18723 scope.$apply(function() {
18724 controller.$commitViewValue();
18725 controller.$setSubmitted();
18728 event.preventDefault();
18731 addEventListenerFn(formElement[0], 'submit', handleFormSubmission);
18733 // unregister the preventDefault listener so that we don't not leak memory but in a
18734 // way that will achieve the prevention of the default action.
18735 formElement.on('$destroy', function() {
18736 $timeout(function() {
18737 removeEventListenerFn(formElement[0], 'submit', handleFormSubmission);
18742 var parentFormCtrl = controller.$$parentForm;
18745 setter(scope, null, controller.$name, controller, controller.$name);
18746 attr.$observe(nameAttr, function(newValue) {
18747 if (controller.$name === newValue) return;
18748 setter(scope, null, controller.$name, undefined, controller.$name);
18749 parentFormCtrl.$$renameControl(controller, newValue);
18750 setter(scope, null, controller.$name, controller, controller.$name);
18753 formElement.on('$destroy', function() {
18754 parentFormCtrl.$removeControl(controller);
18756 setter(scope, null, attr[nameAttr], undefined, controller.$name);
18758 extend(controller, nullFormCtrl); //stop propagating child destruction handlers upwards
18765 return formDirective;
18769 var formDirective = formDirectiveFactory();
18770 var ngFormDirective = formDirectiveFactory(true);
18772 /* global VALID_CLASS: false,
18773 INVALID_CLASS: false,
18774 PRISTINE_CLASS: false,
18775 DIRTY_CLASS: false,
18776 UNTOUCHED_CLASS: false,
18777 TOUCHED_CLASS: false,
18778 $ngModelMinErr: false,
18781 // Regex code is obtained from SO: https://stackoverflow.com/questions/3143070/javascript-regex-iso-datetime#answer-3143231
18782 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)/;
18783 var URL_REGEXP = /^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/;
18784 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;
18785 var NUMBER_REGEXP = /^\s*(\-|\+)?(\d+|(\d*(\.\d*)))\s*$/;
18786 var DATE_REGEXP = /^(\d{4})-(\d{2})-(\d{2})$/;
18787 var DATETIMELOCAL_REGEXP = /^(\d{4})-(\d\d)-(\d\d)T(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
18788 var WEEK_REGEXP = /^(\d{4})-W(\d\d)$/;
18789 var MONTH_REGEXP = /^(\d{4})-(\d\d)$/;
18790 var TIME_REGEXP = /^(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
18796 * @name input[text]
18799 * Standard HTML text input with angular data binding, inherited by most of the `input` elements.
18802 * @param {string} ngModel Assignable angular expression to data-bind to.
18803 * @param {string=} name Property name of the form under which the control is published.
18804 * @param {string=} required Adds `required` validation error key if the value is not entered.
18805 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
18806 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
18807 * `required` when you want to data-bind to the `required` attribute.
18808 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
18810 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
18811 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
18813 * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
18814 * that contains the regular expression body that will be converted to a regular expression
18815 * as in the ngPattern directive.
18816 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
18817 * a RegExp found by evaluating the Angular expression given in the attribute value.
18818 * If the expression evaluates to a RegExp object then this is used directly.
18819 * If the expression is a string then it will be converted to a RegExp after wrapping it in `^` and `$`
18820 * characters. For instance, `"abc"` will be converted to `new RegExp('^abc$')`.
18821 * @param {string=} ngChange Angular expression to be executed when input changes due to user
18822 * interaction with the input element.
18823 * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
18824 * This parameter is ignored for input[type=password] controls, which will never trim the
18828 <example name="text-input-directive" module="textInputExample">
18829 <file name="index.html">
18831 angular.module('textInputExample', [])
18832 .controller('ExampleController', ['$scope', function($scope) {
18835 word: /^\s*\w*\s*$/
18839 <form name="myForm" ng-controller="ExampleController">
18840 Single word: <input type="text" name="input" ng-model="example.text"
18841 ng-pattern="example.word" required ng-trim="false">
18842 <span class="error" ng-show="myForm.input.$error.required">
18844 <span class="error" ng-show="myForm.input.$error.pattern">
18845 Single word only!</span>
18847 <tt>text = {{example.text}}</tt><br/>
18848 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
18849 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
18850 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
18851 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
18854 <file name="protractor.js" type="protractor">
18855 var text = element(by.binding('example.text'));
18856 var valid = element(by.binding('myForm.input.$valid'));
18857 var input = element(by.model('example.text'));
18859 it('should initialize to model', function() {
18860 expect(text.getText()).toContain('guest');
18861 expect(valid.getText()).toContain('true');
18864 it('should be invalid if empty', function() {
18866 input.sendKeys('');
18868 expect(text.getText()).toEqual('text =');
18869 expect(valid.getText()).toContain('false');
18872 it('should be invalid if multi word', function() {
18874 input.sendKeys('hello world');
18876 expect(valid.getText()).toContain('false');
18881 'text': textInputType,
18885 * @name input[date]
18888 * Input with date validation and transformation. In browsers that do not yet support
18889 * the HTML5 date input, a text element will be used. In that case, text must be entered in a valid ISO-8601
18890 * date format (yyyy-MM-dd), for example: `2009-01-06`. Since many
18891 * modern browsers do not yet support this input type, it is important to provide cues to users on the
18892 * expected input format via a placeholder or label.
18894 * The model must always be a Date object, otherwise Angular will throw an error.
18895 * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
18897 * The timezone to be used to read/write the `Date` instance in the model can be defined using
18898 * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
18900 * @param {string} ngModel Assignable angular expression to data-bind to.
18901 * @param {string=} name Property name of the form under which the control is published.
18902 * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be a
18903 * valid ISO date string (yyyy-MM-dd).
18904 * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must be
18905 * a valid ISO date string (yyyy-MM-dd).
18906 * @param {string=} required Sets `required` validation error key if the value is not entered.
18907 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
18908 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
18909 * `required` when you want to data-bind to the `required` attribute.
18910 * @param {string=} ngChange Angular expression to be executed when input changes due to user
18911 * interaction with the input element.
18914 <example name="date-input-directive" module="dateInputExample">
18915 <file name="index.html">
18917 angular.module('dateInputExample', [])
18918 .controller('DateController', ['$scope', function($scope) {
18920 value: new Date(2013, 9, 22)
18924 <form name="myForm" ng-controller="DateController as dateCtrl">
18925 Pick a date in 2013:
18926 <input type="date" id="exampleInput" name="input" ng-model="example.value"
18927 placeholder="yyyy-MM-dd" min="2013-01-01" max="2013-12-31" required />
18928 <span class="error" ng-show="myForm.input.$error.required">
18930 <span class="error" ng-show="myForm.input.$error.date">
18931 Not a valid date!</span>
18932 <tt>value = {{example.value | date: "yyyy-MM-dd"}}</tt><br/>
18933 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
18934 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
18935 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
18936 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
18939 <file name="protractor.js" type="protractor">
18940 var value = element(by.binding('example.value | date: "yyyy-MM-dd"'));
18941 var valid = element(by.binding('myForm.input.$valid'));
18942 var input = element(by.model('example.value'));
18944 // currently protractor/webdriver does not support
18945 // sending keys to all known HTML5 input controls
18946 // for various browsers (see https://github.com/angular/protractor/issues/562).
18947 function setInput(val) {
18948 // set the value of the element and force validation.
18949 var scr = "var ipt = document.getElementById('exampleInput'); " +
18950 "ipt.value = '" + val + "';" +
18951 "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
18952 browser.executeScript(scr);
18955 it('should initialize to model', function() {
18956 expect(value.getText()).toContain('2013-10-22');
18957 expect(valid.getText()).toContain('myForm.input.$valid = true');
18960 it('should be invalid if empty', function() {
18962 expect(value.getText()).toEqual('value =');
18963 expect(valid.getText()).toContain('myForm.input.$valid = false');
18966 it('should be invalid if over max', function() {
18967 setInput('2015-01-01');
18968 expect(value.getText()).toContain('');
18969 expect(valid.getText()).toContain('myForm.input.$valid = false');
18974 'date': createDateInputType('date', DATE_REGEXP,
18975 createDateParser(DATE_REGEXP, ['yyyy', 'MM', 'dd']),
18980 * @name input[datetime-local]
18983 * Input with datetime validation and transformation. In browsers that do not yet support
18984 * the HTML5 date input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
18985 * local datetime format (yyyy-MM-ddTHH:mm:ss), for example: `2010-12-28T14:57:00`.
18987 * The model must always be a Date object, otherwise Angular will throw an error.
18988 * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
18990 * The timezone to be used to read/write the `Date` instance in the model can be defined using
18991 * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
18993 * @param {string} ngModel Assignable angular expression to data-bind to.
18994 * @param {string=} name Property name of the form under which the control is published.
18995 * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be a
18996 * valid ISO datetime format (yyyy-MM-ddTHH:mm:ss).
18997 * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must be
18998 * a valid ISO datetime format (yyyy-MM-ddTHH:mm:ss).
18999 * @param {string=} required Sets `required` validation error key if the value is not entered.
19000 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
19001 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
19002 * `required` when you want to data-bind to the `required` attribute.
19003 * @param {string=} ngChange Angular expression to be executed when input changes due to user
19004 * interaction with the input element.
19007 <example name="datetimelocal-input-directive" module="dateExample">
19008 <file name="index.html">
19010 angular.module('dateExample', [])
19011 .controller('DateController', ['$scope', function($scope) {
19013 value: new Date(2010, 11, 28, 14, 57)
19017 <form name="myForm" ng-controller="DateController as dateCtrl">
19018 Pick a date between in 2013:
19019 <input type="datetime-local" id="exampleInput" name="input" ng-model="example.value"
19020 placeholder="yyyy-MM-ddTHH:mm:ss" min="2001-01-01T00:00:00" max="2013-12-31T00:00:00" required />
19021 <span class="error" ng-show="myForm.input.$error.required">
19023 <span class="error" ng-show="myForm.input.$error.datetimelocal">
19024 Not a valid date!</span>
19025 <tt>value = {{example.value | date: "yyyy-MM-ddTHH:mm:ss"}}</tt><br/>
19026 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
19027 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
19028 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
19029 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
19032 <file name="protractor.js" type="protractor">
19033 var value = element(by.binding('example.value | date: "yyyy-MM-ddTHH:mm:ss"'));
19034 var valid = element(by.binding('myForm.input.$valid'));
19035 var input = element(by.model('example.value'));
19037 // currently protractor/webdriver does not support
19038 // sending keys to all known HTML5 input controls
19039 // for various browsers (https://github.com/angular/protractor/issues/562).
19040 function setInput(val) {
19041 // set the value of the element and force validation.
19042 var scr = "var ipt = document.getElementById('exampleInput'); " +
19043 "ipt.value = '" + val + "';" +
19044 "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
19045 browser.executeScript(scr);
19048 it('should initialize to model', function() {
19049 expect(value.getText()).toContain('2010-12-28T14:57:00');
19050 expect(valid.getText()).toContain('myForm.input.$valid = true');
19053 it('should be invalid if empty', function() {
19055 expect(value.getText()).toEqual('value =');
19056 expect(valid.getText()).toContain('myForm.input.$valid = false');
19059 it('should be invalid if over max', function() {
19060 setInput('2015-01-01T23:59:00');
19061 expect(value.getText()).toContain('');
19062 expect(valid.getText()).toContain('myForm.input.$valid = false');
19067 'datetime-local': createDateInputType('datetimelocal', DATETIMELOCAL_REGEXP,
19068 createDateParser(DATETIMELOCAL_REGEXP, ['yyyy', 'MM', 'dd', 'HH', 'mm', 'ss', 'sss']),
19069 'yyyy-MM-ddTHH:mm:ss.sss'),
19073 * @name input[time]
19076 * Input with time validation and transformation. In browsers that do not yet support
19077 * the HTML5 date input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
19078 * local time format (HH:mm:ss), for example: `14:57:00`. Model must be a Date object. This binding will always output a
19079 * Date object to the model of January 1, 1970, or local date `new Date(1970, 0, 1, HH, mm, ss)`.
19081 * The model must always be a Date object, otherwise Angular will throw an error.
19082 * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
19084 * The timezone to be used to read/write the `Date` instance in the model can be defined using
19085 * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
19087 * @param {string} ngModel Assignable angular expression to data-bind to.
19088 * @param {string=} name Property name of the form under which the control is published.
19089 * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be a
19090 * valid ISO time format (HH:mm:ss).
19091 * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must be a
19092 * valid ISO time format (HH:mm:ss).
19093 * @param {string=} required Sets `required` validation error key if the value is not entered.
19094 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
19095 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
19096 * `required` when you want to data-bind to the `required` attribute.
19097 * @param {string=} ngChange Angular expression to be executed when input changes due to user
19098 * interaction with the input element.
19101 <example name="time-input-directive" module="timeExample">
19102 <file name="index.html">
19104 angular.module('timeExample', [])
19105 .controller('DateController', ['$scope', function($scope) {
19107 value: new Date(1970, 0, 1, 14, 57, 0)
19111 <form name="myForm" ng-controller="DateController as dateCtrl">
19112 Pick a between 8am and 5pm:
19113 <input type="time" id="exampleInput" name="input" ng-model="example.value"
19114 placeholder="HH:mm:ss" min="08:00:00" max="17:00:00" required />
19115 <span class="error" ng-show="myForm.input.$error.required">
19117 <span class="error" ng-show="myForm.input.$error.time">
19118 Not a valid date!</span>
19119 <tt>value = {{example.value | date: "HH:mm:ss"}}</tt><br/>
19120 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
19121 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
19122 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
19123 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
19126 <file name="protractor.js" type="protractor">
19127 var value = element(by.binding('example.value | date: "HH:mm:ss"'));
19128 var valid = element(by.binding('myForm.input.$valid'));
19129 var input = element(by.model('example.value'));
19131 // currently protractor/webdriver does not support
19132 // sending keys to all known HTML5 input controls
19133 // for various browsers (https://github.com/angular/protractor/issues/562).
19134 function setInput(val) {
19135 // set the value of the element and force validation.
19136 var scr = "var ipt = document.getElementById('exampleInput'); " +
19137 "ipt.value = '" + val + "';" +
19138 "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
19139 browser.executeScript(scr);
19142 it('should initialize to model', function() {
19143 expect(value.getText()).toContain('14:57:00');
19144 expect(valid.getText()).toContain('myForm.input.$valid = true');
19147 it('should be invalid if empty', function() {
19149 expect(value.getText()).toEqual('value =');
19150 expect(valid.getText()).toContain('myForm.input.$valid = false');
19153 it('should be invalid if over max', function() {
19154 setInput('23:59:00');
19155 expect(value.getText()).toContain('');
19156 expect(valid.getText()).toContain('myForm.input.$valid = false');
19161 'time': createDateInputType('time', TIME_REGEXP,
19162 createDateParser(TIME_REGEXP, ['HH', 'mm', 'ss', 'sss']),
19167 * @name input[week]
19170 * Input with week-of-the-year validation and transformation to Date. In browsers that do not yet support
19171 * the HTML5 week input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
19172 * week format (yyyy-W##), for example: `2013-W02`.
19174 * The model must always be a Date object, otherwise Angular will throw an error.
19175 * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
19177 * The timezone to be used to read/write the `Date` instance in the model can be defined using
19178 * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
19180 * @param {string} ngModel Assignable angular expression to data-bind to.
19181 * @param {string=} name Property name of the form under which the control is published.
19182 * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be a
19183 * valid ISO week format (yyyy-W##).
19184 * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must be
19185 * a valid ISO week format (yyyy-W##).
19186 * @param {string=} required Sets `required` validation error key if the value is not entered.
19187 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
19188 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
19189 * `required` when you want to data-bind to the `required` attribute.
19190 * @param {string=} ngChange Angular expression to be executed when input changes due to user
19191 * interaction with the input element.
19194 <example name="week-input-directive" module="weekExample">
19195 <file name="index.html">
19197 angular.module('weekExample', [])
19198 .controller('DateController', ['$scope', function($scope) {
19200 value: new Date(2013, 0, 3)
19204 <form name="myForm" ng-controller="DateController as dateCtrl">
19205 Pick a date between in 2013:
19206 <input id="exampleInput" type="week" name="input" ng-model="example.value"
19207 placeholder="YYYY-W##" min="2012-W32" max="2013-W52" required />
19208 <span class="error" ng-show="myForm.input.$error.required">
19210 <span class="error" ng-show="myForm.input.$error.week">
19211 Not a valid date!</span>
19212 <tt>value = {{example.value | date: "yyyy-Www"}}</tt><br/>
19213 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
19214 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
19215 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
19216 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
19219 <file name="protractor.js" type="protractor">
19220 var value = element(by.binding('example.value | date: "yyyy-Www"'));
19221 var valid = element(by.binding('myForm.input.$valid'));
19222 var input = element(by.model('example.value'));
19224 // currently protractor/webdriver does not support
19225 // sending keys to all known HTML5 input controls
19226 // for various browsers (https://github.com/angular/protractor/issues/562).
19227 function setInput(val) {
19228 // set the value of the element and force validation.
19229 var scr = "var ipt = document.getElementById('exampleInput'); " +
19230 "ipt.value = '" + val + "';" +
19231 "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
19232 browser.executeScript(scr);
19235 it('should initialize to model', function() {
19236 expect(value.getText()).toContain('2013-W01');
19237 expect(valid.getText()).toContain('myForm.input.$valid = true');
19240 it('should be invalid if empty', function() {
19242 expect(value.getText()).toEqual('value =');
19243 expect(valid.getText()).toContain('myForm.input.$valid = false');
19246 it('should be invalid if over max', function() {
19247 setInput('2015-W01');
19248 expect(value.getText()).toContain('');
19249 expect(valid.getText()).toContain('myForm.input.$valid = false');
19254 'week': createDateInputType('week', WEEK_REGEXP, weekParser, 'yyyy-Www'),
19258 * @name input[month]
19261 * Input with month validation and transformation. In browsers that do not yet support
19262 * the HTML5 month input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
19263 * month format (yyyy-MM), for example: `2009-01`.
19265 * The model must always be a Date object, otherwise Angular will throw an error.
19266 * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
19267 * If the model is not set to the first of the month, the next view to model update will set it
19268 * to the first of the month.
19270 * The timezone to be used to read/write the `Date` instance in the model can be defined using
19271 * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
19273 * @param {string} ngModel Assignable angular expression to data-bind to.
19274 * @param {string=} name Property name of the form under which the control is published.
19275 * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be
19276 * a valid ISO month format (yyyy-MM).
19277 * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must
19278 * be a valid ISO month format (yyyy-MM).
19279 * @param {string=} required Sets `required` validation error key if the value is not entered.
19280 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
19281 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
19282 * `required` when you want to data-bind to the `required` attribute.
19283 * @param {string=} ngChange Angular expression to be executed when input changes due to user
19284 * interaction with the input element.
19287 <example name="month-input-directive" module="monthExample">
19288 <file name="index.html">
19290 angular.module('monthExample', [])
19291 .controller('DateController', ['$scope', function($scope) {
19293 value: new Date(2013, 9, 1)
19297 <form name="myForm" ng-controller="DateController as dateCtrl">
19298 Pick a month in 2013:
19299 <input id="exampleInput" type="month" name="input" ng-model="example.value"
19300 placeholder="yyyy-MM" min="2013-01" max="2013-12" required />
19301 <span class="error" ng-show="myForm.input.$error.required">
19303 <span class="error" ng-show="myForm.input.$error.month">
19304 Not a valid month!</span>
19305 <tt>value = {{example.value | date: "yyyy-MM"}}</tt><br/>
19306 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
19307 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
19308 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
19309 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
19312 <file name="protractor.js" type="protractor">
19313 var value = element(by.binding('example.value | date: "yyyy-MM"'));
19314 var valid = element(by.binding('myForm.input.$valid'));
19315 var input = element(by.model('example.value'));
19317 // currently protractor/webdriver does not support
19318 // sending keys to all known HTML5 input controls
19319 // for various browsers (https://github.com/angular/protractor/issues/562).
19320 function setInput(val) {
19321 // set the value of the element and force validation.
19322 var scr = "var ipt = document.getElementById('exampleInput'); " +
19323 "ipt.value = '" + val + "';" +
19324 "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
19325 browser.executeScript(scr);
19328 it('should initialize to model', function() {
19329 expect(value.getText()).toContain('2013-10');
19330 expect(valid.getText()).toContain('myForm.input.$valid = true');
19333 it('should be invalid if empty', function() {
19335 expect(value.getText()).toEqual('value =');
19336 expect(valid.getText()).toContain('myForm.input.$valid = false');
19339 it('should be invalid if over max', function() {
19340 setInput('2015-01');
19341 expect(value.getText()).toContain('');
19342 expect(valid.getText()).toContain('myForm.input.$valid = false');
19347 'month': createDateInputType('month', MONTH_REGEXP,
19348 createDateParser(MONTH_REGEXP, ['yyyy', 'MM']),
19353 * @name input[number]
19356 * Text input with number validation and transformation. Sets the `number` validation
19357 * error if not a valid number.
19359 * The model must always be a number, otherwise Angular will throw an error.
19361 * @param {string} ngModel Assignable angular expression to data-bind to.
19362 * @param {string=} name Property name of the form under which the control is published.
19363 * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
19364 * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
19365 * @param {string=} required Sets `required` validation error key if the value is not entered.
19366 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
19367 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
19368 * `required` when you want to data-bind to the `required` attribute.
19369 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
19371 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
19372 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
19374 * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
19375 * that contains the regular expression body that will be converted to a regular expression
19376 * as in the ngPattern directive.
19377 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
19378 * a RegExp found by evaluating the Angular expression given in the attribute value.
19379 * If the expression evaluates to a RegExp object then this is used directly.
19380 * If the expression is a string then it will be converted to a RegExp after wrapping it in `^` and `$`
19381 * characters. For instance, `"abc"` will be converted to `new RegExp('^abc$')`.
19382 * @param {string=} ngChange Angular expression to be executed when input changes due to user
19383 * interaction with the input element.
19386 <example name="number-input-directive" module="numberExample">
19387 <file name="index.html">
19389 angular.module('numberExample', [])
19390 .controller('ExampleController', ['$scope', function($scope) {
19396 <form name="myForm" ng-controller="ExampleController">
19397 Number: <input type="number" name="input" ng-model="example.value"
19398 min="0" max="99" required>
19399 <span class="error" ng-show="myForm.input.$error.required">
19401 <span class="error" ng-show="myForm.input.$error.number">
19402 Not valid number!</span>
19403 <tt>value = {{example.value}}</tt><br/>
19404 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
19405 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
19406 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
19407 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
19410 <file name="protractor.js" type="protractor">
19411 var value = element(by.binding('example.value'));
19412 var valid = element(by.binding('myForm.input.$valid'));
19413 var input = element(by.model('example.value'));
19415 it('should initialize to model', function() {
19416 expect(value.getText()).toContain('12');
19417 expect(valid.getText()).toContain('true');
19420 it('should be invalid if empty', function() {
19422 input.sendKeys('');
19423 expect(value.getText()).toEqual('value =');
19424 expect(valid.getText()).toContain('false');
19427 it('should be invalid if over max', function() {
19429 input.sendKeys('123');
19430 expect(value.getText()).toEqual('value =');
19431 expect(valid.getText()).toContain('false');
19436 'number': numberInputType,
19444 * Text input with URL validation. Sets the `url` validation error key if the content is not a
19447 * <div class="alert alert-warning">
19448 * **Note:** `input[url]` uses a regex to validate urls that is derived from the regex
19449 * used in Chromium. If you need stricter validation, you can use `ng-pattern` or modify
19450 * the built-in validators (see the {@link guide/forms Forms guide})
19453 * @param {string} ngModel Assignable angular expression to data-bind to.
19454 * @param {string=} name Property name of the form under which the control is published.
19455 * @param {string=} required Sets `required` validation error key if the value is not entered.
19456 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
19457 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
19458 * `required` when you want to data-bind to the `required` attribute.
19459 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
19461 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
19462 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
19464 * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
19465 * that contains the regular expression body that will be converted to a regular expression
19466 * as in the ngPattern directive.
19467 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
19468 * a RegExp found by evaluating the Angular expression given in the attribute value.
19469 * If the expression evaluates to a RegExp object then this is used directly.
19470 * If the expression is a string then it will be converted to a RegExp after wrapping it in `^` and `$`
19471 * characters. For instance, `"abc"` will be converted to `new RegExp('^abc$')`.
19472 * @param {string=} ngChange Angular expression to be executed when input changes due to user
19473 * interaction with the input element.
19476 <example name="url-input-directive" module="urlExample">
19477 <file name="index.html">
19479 angular.module('urlExample', [])
19480 .controller('ExampleController', ['$scope', function($scope) {
19482 text: 'http://google.com'
19486 <form name="myForm" ng-controller="ExampleController">
19487 URL: <input type="url" name="input" ng-model="url.text" required>
19488 <span class="error" ng-show="myForm.input.$error.required">
19490 <span class="error" ng-show="myForm.input.$error.url">
19491 Not valid url!</span>
19492 <tt>text = {{url.text}}</tt><br/>
19493 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
19494 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
19495 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
19496 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
19497 <tt>myForm.$error.url = {{!!myForm.$error.url}}</tt><br/>
19500 <file name="protractor.js" type="protractor">
19501 var text = element(by.binding('url.text'));
19502 var valid = element(by.binding('myForm.input.$valid'));
19503 var input = element(by.model('url.text'));
19505 it('should initialize to model', function() {
19506 expect(text.getText()).toContain('http://google.com');
19507 expect(valid.getText()).toContain('true');
19510 it('should be invalid if empty', function() {
19512 input.sendKeys('');
19514 expect(text.getText()).toEqual('text =');
19515 expect(valid.getText()).toContain('false');
19518 it('should be invalid if not url', function() {
19520 input.sendKeys('box');
19522 expect(valid.getText()).toContain('false');
19527 'url': urlInputType,
19532 * @name input[email]
19535 * Text input with email validation. Sets the `email` validation error key if not a valid email
19538 * <div class="alert alert-warning">
19539 * **Note:** `input[email]` uses a regex to validate email addresses that is derived from the regex
19540 * used in Chromium. If you need stricter validation (e.g. requiring a top-level domain), you can
19541 * use `ng-pattern` or modify the built-in validators (see the {@link guide/forms Forms guide})
19544 * @param {string} ngModel Assignable angular expression to data-bind to.
19545 * @param {string=} name Property name of the form under which the control is published.
19546 * @param {string=} required Sets `required` validation error key if the value is not entered.
19547 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
19548 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
19549 * `required` when you want to data-bind to the `required` attribute.
19550 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
19552 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
19553 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
19555 * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
19556 * that contains the regular expression body that will be converted to a regular expression
19557 * as in the ngPattern directive.
19558 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
19559 * a RegExp found by evaluating the Angular expression given in the attribute value.
19560 * If the expression evaluates to a RegExp object then this is used directly.
19561 * If the expression is a string then it will be converted to a RegExp after wrapping it in `^` and `$`
19562 * characters. For instance, `"abc"` will be converted to `new RegExp('^abc$')`.
19563 * @param {string=} ngChange Angular expression to be executed when input changes due to user
19564 * interaction with the input element.
19567 <example name="email-input-directive" module="emailExample">
19568 <file name="index.html">
19570 angular.module('emailExample', [])
19571 .controller('ExampleController', ['$scope', function($scope) {
19573 text: 'me@example.com'
19577 <form name="myForm" ng-controller="ExampleController">
19578 Email: <input type="email" name="input" ng-model="email.text" required>
19579 <span class="error" ng-show="myForm.input.$error.required">
19581 <span class="error" ng-show="myForm.input.$error.email">
19582 Not valid email!</span>
19583 <tt>text = {{email.text}}</tt><br/>
19584 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
19585 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
19586 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
19587 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
19588 <tt>myForm.$error.email = {{!!myForm.$error.email}}</tt><br/>
19591 <file name="protractor.js" type="protractor">
19592 var text = element(by.binding('email.text'));
19593 var valid = element(by.binding('myForm.input.$valid'));
19594 var input = element(by.model('email.text'));
19596 it('should initialize to model', function() {
19597 expect(text.getText()).toContain('me@example.com');
19598 expect(valid.getText()).toContain('true');
19601 it('should be invalid if empty', function() {
19603 input.sendKeys('');
19604 expect(text.getText()).toEqual('text =');
19605 expect(valid.getText()).toContain('false');
19608 it('should be invalid if not email', function() {
19610 input.sendKeys('xxx');
19612 expect(valid.getText()).toContain('false');
19617 'email': emailInputType,
19622 * @name input[radio]
19625 * HTML radio button.
19627 * @param {string} ngModel Assignable angular expression to data-bind to.
19628 * @param {string} value The value to which the expression should be set when selected.
19629 * @param {string=} name Property name of the form under which the control is published.
19630 * @param {string=} ngChange Angular expression to be executed when input changes due to user
19631 * interaction with the input element.
19632 * @param {string} ngValue Angular expression which sets the value to which the expression should
19633 * be set when selected.
19636 <example name="radio-input-directive" module="radioExample">
19637 <file name="index.html">
19639 angular.module('radioExample', [])
19640 .controller('ExampleController', ['$scope', function($scope) {
19644 $scope.specialValue = {
19650 <form name="myForm" ng-controller="ExampleController">
19651 <input type="radio" ng-model="color.name" value="red"> Red <br/>
19652 <input type="radio" ng-model="color.name" ng-value="specialValue"> Green <br/>
19653 <input type="radio" ng-model="color.name" value="blue"> Blue <br/>
19654 <tt>color = {{color.name | json}}</tt><br/>
19656 Note that `ng-value="specialValue"` sets radio item's value to be the value of `$scope.specialValue`.
19658 <file name="protractor.js" type="protractor">
19659 it('should change state', function() {
19660 var color = element(by.binding('color.name'));
19662 expect(color.getText()).toContain('blue');
19664 element.all(by.model('color.name')).get(0).click();
19666 expect(color.getText()).toContain('red');
19671 'radio': radioInputType,
19676 * @name input[checkbox]
19681 * @param {string} ngModel Assignable angular expression to data-bind to.
19682 * @param {string=} name Property name of the form under which the control is published.
19683 * @param {expression=} ngTrueValue The value to which the expression should be set when selected.
19684 * @param {expression=} ngFalseValue The value to which the expression should be set when not selected.
19685 * @param {string=} ngChange Angular expression to be executed when input changes due to user
19686 * interaction with the input element.
19689 <example name="checkbox-input-directive" module="checkboxExample">
19690 <file name="index.html">
19692 angular.module('checkboxExample', [])
19693 .controller('ExampleController', ['$scope', function($scope) {
19694 $scope.checkboxModel = {
19700 <form name="myForm" ng-controller="ExampleController">
19701 Value1: <input type="checkbox" ng-model="checkboxModel.value1"> <br/>
19702 Value2: <input type="checkbox" ng-model="checkboxModel.value2"
19703 ng-true-value="'YES'" ng-false-value="'NO'"> <br/>
19704 <tt>value1 = {{checkboxModel.value1}}</tt><br/>
19705 <tt>value2 = {{checkboxModel.value2}}</tt><br/>
19708 <file name="protractor.js" type="protractor">
19709 it('should change state', function() {
19710 var value1 = element(by.binding('checkboxModel.value1'));
19711 var value2 = element(by.binding('checkboxModel.value2'));
19713 expect(value1.getText()).toContain('true');
19714 expect(value2.getText()).toContain('YES');
19716 element(by.model('checkboxModel.value1')).click();
19717 element(by.model('checkboxModel.value2')).click();
19719 expect(value1.getText()).toContain('false');
19720 expect(value2.getText()).toContain('NO');
19725 'checkbox': checkboxInputType,
19734 function stringBasedInputType(ctrl) {
19735 ctrl.$formatters.push(function(value) {
19736 return ctrl.$isEmpty(value) ? value : value.toString();
19740 function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
19741 baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
19742 stringBasedInputType(ctrl);
19745 function baseInputType(scope, element, attr, ctrl, $sniffer, $browser) {
19746 var type = lowercase(element[0].type);
19748 // In composition mode, users are still inputing intermediate text buffer,
19749 // hold the listener until composition is done.
19750 // More about composition events: https://developer.mozilla.org/en-US/docs/Web/API/CompositionEvent
19751 if (!$sniffer.android) {
19752 var composing = false;
19754 element.on('compositionstart', function(data) {
19758 element.on('compositionend', function() {
19764 var listener = function(ev) {
19766 $browser.defer.cancel(timeout);
19769 if (composing) return;
19770 var value = element.val(),
19771 event = ev && ev.type;
19773 // By default we will trim the value
19774 // If the attribute ng-trim exists we will avoid trimming
19775 // If input type is 'password', the value is never trimmed
19776 if (type !== 'password' && (!attr.ngTrim || attr.ngTrim !== 'false')) {
19777 value = trim(value);
19780 // If a control is suffering from bad input (due to native validators), browsers discard its
19781 // value, so it may be necessary to revalidate (by calling $setViewValue again) even if the
19782 // control's value is the same empty value twice in a row.
19783 if (ctrl.$viewValue !== value || (value === '' && ctrl.$$hasNativeValidators)) {
19784 ctrl.$setViewValue(value, event);
19788 // if the browser does support "input" event, we are fine - except on IE9 which doesn't fire the
19789 // input event on backspace, delete or cut
19790 if ($sniffer.hasEvent('input')) {
19791 element.on('input', listener);
19795 var deferListener = function(ev, input, origValue) {
19797 timeout = $browser.defer(function() {
19799 if (!input || input.value !== origValue) {
19806 element.on('keydown', function(event) {
19807 var key = event.keyCode;
19810 // command modifiers arrows
19811 if (key === 91 || (15 < key && key < 19) || (37 <= key && key <= 40)) return;
19813 deferListener(event, this, this.value);
19816 // if user modifies input value using context menu in IE, we need "paste" and "cut" events to catch it
19817 if ($sniffer.hasEvent('paste')) {
19818 element.on('paste cut', deferListener);
19822 // if user paste into input using mouse on older browser
19823 // or form autocomplete on newer browser, we need "change" event to catch it
19824 element.on('change', listener);
19826 ctrl.$render = function() {
19827 element.val(ctrl.$isEmpty(ctrl.$viewValue) ? '' : ctrl.$viewValue);
19831 function weekParser(isoWeek, existingDate) {
19832 if (isDate(isoWeek)) {
19836 if (isString(isoWeek)) {
19837 WEEK_REGEXP.lastIndex = 0;
19838 var parts = WEEK_REGEXP.exec(isoWeek);
19840 var year = +parts[1],
19846 firstThurs = getFirstThursdayOfYear(year),
19847 addDays = (week - 1) * 7;
19849 if (existingDate) {
19850 hours = existingDate.getHours();
19851 minutes = existingDate.getMinutes();
19852 seconds = existingDate.getSeconds();
19853 milliseconds = existingDate.getMilliseconds();
19856 return new Date(year, 0, firstThurs.getDate() + addDays, hours, minutes, seconds, milliseconds);
19863 function createDateParser(regexp, mapping) {
19864 return function(iso, date) {
19871 if (isString(iso)) {
19872 // When a date is JSON'ified to wraps itself inside of an extra
19873 // set of double quotes. This makes the date parsing code unable
19874 // to match the date string and parse it as a date.
19875 if (iso.charAt(0) == '"' && iso.charAt(iso.length - 1) == '"') {
19876 iso = iso.substring(1, iso.length - 1);
19878 if (ISO_DATE_REGEXP.test(iso)) {
19879 return new Date(iso);
19881 regexp.lastIndex = 0;
19882 parts = regexp.exec(iso);
19888 yyyy: date.getFullYear(),
19889 MM: date.getMonth() + 1,
19890 dd: date.getDate(),
19891 HH: date.getHours(),
19892 mm: date.getMinutes(),
19893 ss: date.getSeconds(),
19894 sss: date.getMilliseconds() / 1000
19897 map = { yyyy: 1970, MM: 1, dd: 1, HH: 0, mm: 0, ss: 0, sss: 0 };
19900 forEach(parts, function(part, index) {
19901 if (index < mapping.length) {
19902 map[mapping[index]] = +part;
19905 return new Date(map.yyyy, map.MM - 1, map.dd, map.HH, map.mm, map.ss || 0, map.sss * 1000 || 0);
19913 function createDateInputType(type, regexp, parseDate, format) {
19914 return function dynamicDateInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter) {
19915 badInputChecker(scope, element, attr, ctrl);
19916 baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
19917 var timezone = ctrl && ctrl.$options && ctrl.$options.timezone;
19920 ctrl.$$parserName = type;
19921 ctrl.$parsers.push(function(value) {
19922 if (ctrl.$isEmpty(value)) return null;
19923 if (regexp.test(value)) {
19924 // Note: We cannot read ctrl.$modelValue, as there might be a different
19925 // parser/formatter in the processing chain so that the model
19926 // contains some different data format!
19927 var parsedDate = parseDate(value, previousDate);
19928 if (timezone === 'UTC') {
19929 parsedDate.setMinutes(parsedDate.getMinutes() - parsedDate.getTimezoneOffset());
19936 ctrl.$formatters.push(function(value) {
19937 if (value && !isDate(value)) {
19938 throw $ngModelMinErr('datefmt', 'Expected `{0}` to be a date', value);
19940 if (isValidDate(value)) {
19941 previousDate = value;
19942 if (previousDate && timezone === 'UTC') {
19943 var timezoneOffset = 60000 * previousDate.getTimezoneOffset();
19944 previousDate = new Date(previousDate.getTime() + timezoneOffset);
19946 return $filter('date')(value, format, timezone);
19948 previousDate = null;
19953 if (isDefined(attr.min) || attr.ngMin) {
19955 ctrl.$validators.min = function(value) {
19956 return !isValidDate(value) || isUndefined(minVal) || parseDate(value) >= minVal;
19958 attr.$observe('min', function(val) {
19959 minVal = parseObservedDateValue(val);
19964 if (isDefined(attr.max) || attr.ngMax) {
19966 ctrl.$validators.max = function(value) {
19967 return !isValidDate(value) || isUndefined(maxVal) || parseDate(value) <= maxVal;
19969 attr.$observe('max', function(val) {
19970 maxVal = parseObservedDateValue(val);
19975 function isValidDate(value) {
19976 // Invalid Date: getTime() returns NaN
19977 return value && !(value.getTime && value.getTime() !== value.getTime());
19980 function parseObservedDateValue(val) {
19981 return isDefined(val) ? (isDate(val) ? val : parseDate(val)) : undefined;
19986 function badInputChecker(scope, element, attr, ctrl) {
19987 var node = element[0];
19988 var nativeValidation = ctrl.$$hasNativeValidators = isObject(node.validity);
19989 if (nativeValidation) {
19990 ctrl.$parsers.push(function(value) {
19991 var validity = element.prop(VALIDITY_STATE_PROPERTY) || {};
19992 // Detect bug in FF35 for input[email] (https://bugzilla.mozilla.org/show_bug.cgi?id=1064430):
19993 // - also sets validity.badInput (should only be validity.typeMismatch).
19994 // - see http://www.whatwg.org/specs/web-apps/current-work/multipage/forms.html#e-mail-state-(type=email)
19995 // - can ignore this case as we can still read out the erroneous email...
19996 return validity.badInput && !validity.typeMismatch ? undefined : value;
20001 function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) {
20002 badInputChecker(scope, element, attr, ctrl);
20003 baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
20005 ctrl.$$parserName = 'number';
20006 ctrl.$parsers.push(function(value) {
20007 if (ctrl.$isEmpty(value)) return null;
20008 if (NUMBER_REGEXP.test(value)) return parseFloat(value);
20012 ctrl.$formatters.push(function(value) {
20013 if (!ctrl.$isEmpty(value)) {
20014 if (!isNumber(value)) {
20015 throw $ngModelMinErr('numfmt', 'Expected `{0}` to be a number', value);
20017 value = value.toString();
20022 if (isDefined(attr.min) || attr.ngMin) {
20024 ctrl.$validators.min = function(value) {
20025 return ctrl.$isEmpty(value) || isUndefined(minVal) || value >= minVal;
20028 attr.$observe('min', function(val) {
20029 if (isDefined(val) && !isNumber(val)) {
20030 val = parseFloat(val, 10);
20032 minVal = isNumber(val) && !isNaN(val) ? val : undefined;
20033 // TODO(matsko): implement validateLater to reduce number of validations
20038 if (isDefined(attr.max) || attr.ngMax) {
20040 ctrl.$validators.max = function(value) {
20041 return ctrl.$isEmpty(value) || isUndefined(maxVal) || value <= maxVal;
20044 attr.$observe('max', function(val) {
20045 if (isDefined(val) && !isNumber(val)) {
20046 val = parseFloat(val, 10);
20048 maxVal = isNumber(val) && !isNaN(val) ? val : undefined;
20049 // TODO(matsko): implement validateLater to reduce number of validations
20055 function urlInputType(scope, element, attr, ctrl, $sniffer, $browser) {
20056 // Note: no badInputChecker here by purpose as `url` is only a validation
20057 // in browsers, i.e. we can always read out input.value even if it is not valid!
20058 baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
20059 stringBasedInputType(ctrl);
20061 ctrl.$$parserName = 'url';
20062 ctrl.$validators.url = function(modelValue, viewValue) {
20063 var value = modelValue || viewValue;
20064 return ctrl.$isEmpty(value) || URL_REGEXP.test(value);
20068 function emailInputType(scope, element, attr, ctrl, $sniffer, $browser) {
20069 // Note: no badInputChecker here by purpose as `url` is only a validation
20070 // in browsers, i.e. we can always read out input.value even if it is not valid!
20071 baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
20072 stringBasedInputType(ctrl);
20074 ctrl.$$parserName = 'email';
20075 ctrl.$validators.email = function(modelValue, viewValue) {
20076 var value = modelValue || viewValue;
20077 return ctrl.$isEmpty(value) || EMAIL_REGEXP.test(value);
20081 function radioInputType(scope, element, attr, ctrl) {
20082 // make the name unique, if not defined
20083 if (isUndefined(attr.name)) {
20084 element.attr('name', nextUid());
20087 var listener = function(ev) {
20088 if (element[0].checked) {
20089 ctrl.$setViewValue(attr.value, ev && ev.type);
20093 element.on('click', listener);
20095 ctrl.$render = function() {
20096 var value = attr.value;
20097 element[0].checked = (value == ctrl.$viewValue);
20100 attr.$observe('value', ctrl.$render);
20103 function parseConstantExpr($parse, context, name, expression, fallback) {
20105 if (isDefined(expression)) {
20106 parseFn = $parse(expression);
20107 if (!parseFn.constant) {
20108 throw minErr('ngModel')('constexpr', 'Expected constant expression for `{0}`, but saw ' +
20109 '`{1}`.', name, expression);
20111 return parseFn(context);
20116 function checkboxInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter, $parse) {
20117 var trueValue = parseConstantExpr($parse, scope, 'ngTrueValue', attr.ngTrueValue, true);
20118 var falseValue = parseConstantExpr($parse, scope, 'ngFalseValue', attr.ngFalseValue, false);
20120 var listener = function(ev) {
20121 ctrl.$setViewValue(element[0].checked, ev && ev.type);
20124 element.on('click', listener);
20126 ctrl.$render = function() {
20127 element[0].checked = ctrl.$viewValue;
20130 // Override the standard `$isEmpty` because the $viewValue of an empty checkbox is always set to `false`
20131 // This is because of the parser below, which compares the `$modelValue` with `trueValue` to convert
20132 // it to a boolean.
20133 ctrl.$isEmpty = function(value) {
20134 return value === false;
20137 ctrl.$formatters.push(function(value) {
20138 return equals(value, trueValue);
20141 ctrl.$parsers.push(function(value) {
20142 return value ? trueValue : falseValue;
20153 * HTML textarea element control with angular data-binding. The data-binding and validation
20154 * properties of this element are exactly the same as those of the
20155 * {@link ng.directive:input input element}.
20157 * @param {string} ngModel Assignable angular expression to data-bind to.
20158 * @param {string=} name Property name of the form under which the control is published.
20159 * @param {string=} required Sets `required` validation error key if the value is not entered.
20160 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
20161 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
20162 * `required` when you want to data-bind to the `required` attribute.
20163 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
20165 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
20166 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any
20168 * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the
20169 * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for
20170 * patterns defined as scope expressions.
20171 * @param {string=} ngChange Angular expression to be executed when input changes due to user
20172 * interaction with the input element.
20173 * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
20183 * HTML input element control. When used together with {@link ngModel `ngModel`}, it provides data-binding,
20184 * input state control, and validation.
20185 * Input control follows HTML5 input types and polyfills the HTML5 validation behavior for older browsers.
20187 * <div class="alert alert-warning">
20188 * **Note:** Not every feature offered is available for all input types.
20189 * Specifically, data binding and event handling via `ng-model` is unsupported for `input[file]`.
20192 * @param {string} ngModel Assignable angular expression to data-bind to.
20193 * @param {string=} name Property name of the form under which the control is published.
20194 * @param {string=} required Sets `required` validation error key if the value is not entered.
20195 * @param {boolean=} ngRequired Sets `required` attribute if set to true
20196 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
20198 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
20199 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any
20201 * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the
20202 * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for
20203 * patterns defined as scope expressions.
20204 * @param {string=} ngChange Angular expression to be executed when input changes due to user
20205 * interaction with the input element.
20206 * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
20207 * This parameter is ignored for input[type=password] controls, which will never trim the
20211 <example name="input-directive" module="inputExample">
20212 <file name="index.html">
20214 angular.module('inputExample', [])
20215 .controller('ExampleController', ['$scope', function($scope) {
20216 $scope.user = {name: 'guest', last: 'visitor'};
20219 <div ng-controller="ExampleController">
20220 <form name="myForm">
20221 User name: <input type="text" name="userName" ng-model="user.name" required>
20222 <span class="error" ng-show="myForm.userName.$error.required">
20223 Required!</span><br>
20224 Last name: <input type="text" name="lastName" ng-model="user.last"
20225 ng-minlength="3" ng-maxlength="10">
20226 <span class="error" ng-show="myForm.lastName.$error.minlength">
20228 <span class="error" ng-show="myForm.lastName.$error.maxlength">
20229 Too long!</span><br>
20232 <tt>user = {{user}}</tt><br/>
20233 <tt>myForm.userName.$valid = {{myForm.userName.$valid}}</tt><br>
20234 <tt>myForm.userName.$error = {{myForm.userName.$error}}</tt><br>
20235 <tt>myForm.lastName.$valid = {{myForm.lastName.$valid}}</tt><br>
20236 <tt>myForm.lastName.$error = {{myForm.lastName.$error}}</tt><br>
20237 <tt>myForm.$valid = {{myForm.$valid}}</tt><br>
20238 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br>
20239 <tt>myForm.$error.minlength = {{!!myForm.$error.minlength}}</tt><br>
20240 <tt>myForm.$error.maxlength = {{!!myForm.$error.maxlength}}</tt><br>
20243 <file name="protractor.js" type="protractor">
20244 var user = element(by.exactBinding('user'));
20245 var userNameValid = element(by.binding('myForm.userName.$valid'));
20246 var lastNameValid = element(by.binding('myForm.lastName.$valid'));
20247 var lastNameError = element(by.binding('myForm.lastName.$error'));
20248 var formValid = element(by.binding('myForm.$valid'));
20249 var userNameInput = element(by.model('user.name'));
20250 var userLastInput = element(by.model('user.last'));
20252 it('should initialize to model', function() {
20253 expect(user.getText()).toContain('{"name":"guest","last":"visitor"}');
20254 expect(userNameValid.getText()).toContain('true');
20255 expect(formValid.getText()).toContain('true');
20258 it('should be invalid if empty when required', function() {
20259 userNameInput.clear();
20260 userNameInput.sendKeys('');
20262 expect(user.getText()).toContain('{"last":"visitor"}');
20263 expect(userNameValid.getText()).toContain('false');
20264 expect(formValid.getText()).toContain('false');
20267 it('should be valid if empty when min length is set', function() {
20268 userLastInput.clear();
20269 userLastInput.sendKeys('');
20271 expect(user.getText()).toContain('{"name":"guest","last":""}');
20272 expect(lastNameValid.getText()).toContain('true');
20273 expect(formValid.getText()).toContain('true');
20276 it('should be invalid if less than required min length', function() {
20277 userLastInput.clear();
20278 userLastInput.sendKeys('xx');
20280 expect(user.getText()).toContain('{"name":"guest"}');
20281 expect(lastNameValid.getText()).toContain('false');
20282 expect(lastNameError.getText()).toContain('minlength');
20283 expect(formValid.getText()).toContain('false');
20286 it('should be invalid if longer than max length', function() {
20287 userLastInput.clear();
20288 userLastInput.sendKeys('some ridiculously long name');
20290 expect(user.getText()).toContain('{"name":"guest"}');
20291 expect(lastNameValid.getText()).toContain('false');
20292 expect(lastNameError.getText()).toContain('maxlength');
20293 expect(formValid.getText()).toContain('false');
20298 var inputDirective = ['$browser', '$sniffer', '$filter', '$parse',
20299 function($browser, $sniffer, $filter, $parse) {
20302 require: ['?ngModel'],
20304 pre: function(scope, element, attr, ctrls) {
20306 (inputType[lowercase(attr.type)] || inputType.text)(scope, element, attr, ctrls[0], $sniffer,
20307 $browser, $filter, $parse);
20316 var CONSTANT_VALUE_REGEXP = /^(true|false|\d+)$/;
20322 * Binds the given expression to the value of `<option>` or {@link input[radio] `input[radio]`},
20323 * so that when the element is selected, the {@link ngModel `ngModel`} of that element is set to
20326 * `ngValue` is useful when dynamically generating lists of radio buttons using
20327 * {@link ngRepeat `ngRepeat`}, as shown below.
20329 * Likewise, `ngValue` can be used to generate `<option>` elements for
20330 * the {@link select `select`} element. In that case however, only strings are supported
20331 * for the `value `attribute, so the resulting `ngModel` will always be a string.
20332 * Support for `select` models with non-string values is available via `ngOptions`.
20335 * @param {string=} ngValue angular expression, whose value will be bound to the `value` attribute
20336 * of the `input` element
20339 <example name="ngValue-directive" module="valueExample">
20340 <file name="index.html">
20342 angular.module('valueExample', [])
20343 .controller('ExampleController', ['$scope', function($scope) {
20344 $scope.names = ['pizza', 'unicorns', 'robots'];
20345 $scope.my = { favorite: 'unicorns' };
20348 <form ng-controller="ExampleController">
20349 <h2>Which is your favorite?</h2>
20350 <label ng-repeat="name in names" for="{{name}}">
20352 <input type="radio"
20353 ng-model="my.favorite"
20358 <div>You chose {{my.favorite}}</div>
20361 <file name="protractor.js" type="protractor">
20362 var favorite = element(by.binding('my.favorite'));
20364 it('should initialize to model', function() {
20365 expect(favorite.getText()).toContain('unicorns');
20367 it('should bind the values to the inputs', function() {
20368 element.all(by.model('my.favorite')).get(0).click();
20369 expect(favorite.getText()).toContain('pizza');
20374 var ngValueDirective = function() {
20378 compile: function(tpl, tplAttr) {
20379 if (CONSTANT_VALUE_REGEXP.test(tplAttr.ngValue)) {
20380 return function ngValueConstantLink(scope, elm, attr) {
20381 attr.$set('value', scope.$eval(attr.ngValue));
20384 return function ngValueLink(scope, elm, attr) {
20385 scope.$watch(attr.ngValue, function valueWatchAction(value) {
20386 attr.$set('value', value);
20400 * The `ngBind` attribute tells Angular to replace the text content of the specified HTML element
20401 * with the value of a given expression, and to update the text content when the value of that
20402 * expression changes.
20404 * Typically, you don't use `ngBind` directly, but instead you use the double curly markup like
20405 * `{{ expression }}` which is similar but less verbose.
20407 * It is preferable to use `ngBind` instead of `{{ expression }}` if a template is momentarily
20408 * displayed by the browser in its raw state before Angular compiles it. Since `ngBind` is an
20409 * element attribute, it makes the bindings invisible to the user while the page is loading.
20411 * An alternative solution to this problem would be using the
20412 * {@link ng.directive:ngCloak ngCloak} directive.
20416 * @param {expression} ngBind {@link guide/expression Expression} to evaluate.
20419 * Enter a name in the Live Preview text box; the greeting below the text box changes instantly.
20420 <example module="bindExample">
20421 <file name="index.html">
20423 angular.module('bindExample', [])
20424 .controller('ExampleController', ['$scope', function($scope) {
20425 $scope.name = 'Whirled';
20428 <div ng-controller="ExampleController">
20429 Enter name: <input type="text" ng-model="name"><br>
20430 Hello <span ng-bind="name"></span>!
20433 <file name="protractor.js" type="protractor">
20434 it('should check ng-bind', function() {
20435 var nameInput = element(by.model('name'));
20437 expect(element(by.binding('name')).getText()).toBe('Whirled');
20439 nameInput.sendKeys('world');
20440 expect(element(by.binding('name')).getText()).toBe('world');
20445 var ngBindDirective = ['$compile', function($compile) {
20448 compile: function ngBindCompile(templateElement) {
20449 $compile.$$addBindingClass(templateElement);
20450 return function ngBindLink(scope, element, attr) {
20451 $compile.$$addBindingInfo(element, attr.ngBind);
20452 element = element[0];
20453 scope.$watch(attr.ngBind, function ngBindWatchAction(value) {
20454 element.textContent = value === undefined ? '' : value;
20464 * @name ngBindTemplate
20467 * The `ngBindTemplate` directive specifies that the element
20468 * text content should be replaced with the interpolation of the template
20469 * in the `ngBindTemplate` attribute.
20470 * Unlike `ngBind`, the `ngBindTemplate` can contain multiple `{{` `}}`
20471 * expressions. This directive is needed since some HTML elements
20472 * (such as TITLE and OPTION) cannot contain SPAN elements.
20475 * @param {string} ngBindTemplate template of form
20476 * <tt>{{</tt> <tt>expression</tt> <tt>}}</tt> to eval.
20479 * Try it here: enter text in text box and watch the greeting change.
20480 <example module="bindExample">
20481 <file name="index.html">
20483 angular.module('bindExample', [])
20484 .controller('ExampleController', ['$scope', function($scope) {
20485 $scope.salutation = 'Hello';
20486 $scope.name = 'World';
20489 <div ng-controller="ExampleController">
20490 Salutation: <input type="text" ng-model="salutation"><br>
20491 Name: <input type="text" ng-model="name"><br>
20492 <pre ng-bind-template="{{salutation}} {{name}}!"></pre>
20495 <file name="protractor.js" type="protractor">
20496 it('should check ng-bind', function() {
20497 var salutationElem = element(by.binding('salutation'));
20498 var salutationInput = element(by.model('salutation'));
20499 var nameInput = element(by.model('name'));
20501 expect(salutationElem.getText()).toBe('Hello World!');
20503 salutationInput.clear();
20504 salutationInput.sendKeys('Greetings');
20506 nameInput.sendKeys('user');
20508 expect(salutationElem.getText()).toBe('Greetings user!');
20513 var ngBindTemplateDirective = ['$interpolate', '$compile', function($interpolate, $compile) {
20515 compile: function ngBindTemplateCompile(templateElement) {
20516 $compile.$$addBindingClass(templateElement);
20517 return function ngBindTemplateLink(scope, element, attr) {
20518 var interpolateFn = $interpolate(element.attr(attr.$attr.ngBindTemplate));
20519 $compile.$$addBindingInfo(element, interpolateFn.expressions);
20520 element = element[0];
20521 attr.$observe('ngBindTemplate', function(value) {
20522 element.textContent = value === undefined ? '' : value;
20535 * Evaluates the expression and inserts the resulting HTML into the element in a secure way. By default,
20536 * the resulting HTML content will be sanitized using the {@link ngSanitize.$sanitize $sanitize} service.
20537 * To utilize this functionality, ensure that `$sanitize` is available, for example, by including {@link
20538 * ngSanitize} in your module's dependencies (not in core Angular). In order to use {@link ngSanitize}
20539 * in your module's dependencies, you need to include "angular-sanitize.js" in your application.
20541 * You may also bypass sanitization for values you know are safe. To do so, bind to
20542 * an explicitly trusted value via {@link ng.$sce#trustAsHtml $sce.trustAsHtml}. See the example
20543 * under {@link ng.$sce#show-me-an-example-using-sce- Strict Contextual Escaping (SCE)}.
20545 * Note: If a `$sanitize` service is unavailable and the bound value isn't explicitly trusted, you
20546 * will have an exception (instead of an exploit.)
20549 * @param {expression} ngBindHtml {@link guide/expression Expression} to evaluate.
20553 <example module="bindHtmlExample" deps="angular-sanitize.js">
20554 <file name="index.html">
20555 <div ng-controller="ExampleController">
20556 <p ng-bind-html="myHTML"></p>
20560 <file name="script.js">
20561 angular.module('bindHtmlExample', ['ngSanitize'])
20562 .controller('ExampleController', ['$scope', function($scope) {
20564 'I am an <code>HTML</code>string with ' +
20565 '<a href="#">links!</a> and other <em>stuff</em>';
20569 <file name="protractor.js" type="protractor">
20570 it('should check ng-bind-html', function() {
20571 expect(element(by.binding('myHTML')).getText()).toBe(
20572 'I am an HTMLstring with links! and other stuff');
20577 var ngBindHtmlDirective = ['$sce', '$parse', '$compile', function($sce, $parse, $compile) {
20580 compile: function ngBindHtmlCompile(tElement, tAttrs) {
20581 var ngBindHtmlGetter = $parse(tAttrs.ngBindHtml);
20582 var ngBindHtmlWatch = $parse(tAttrs.ngBindHtml, function getStringValue(value) {
20583 return (value || '').toString();
20585 $compile.$$addBindingClass(tElement);
20587 return function ngBindHtmlLink(scope, element, attr) {
20588 $compile.$$addBindingInfo(element, attr.ngBindHtml);
20590 scope.$watch(ngBindHtmlWatch, function ngBindHtmlWatchAction() {
20591 // we re-evaluate the expr because we want a TrustedValueHolderType
20592 // for $sce, not a string
20593 element.html($sce.getTrustedHtml(ngBindHtmlGetter(scope)) || '');
20605 * Evaluate the given expression when the user changes the input.
20606 * The expression is evaluated immediately, unlike the JavaScript onchange event
20607 * which only triggers at the end of a change (usually, when the user leaves the
20608 * form element or presses the return key).
20610 * The `ngChange` expression is only evaluated when a change in the input value causes
20611 * a new value to be committed to the model.
20613 * It will not be evaluated:
20614 * * if the value returned from the `$parsers` transformation pipeline has not changed
20615 * * if the input has continued to be invalid since the model will stay `null`
20616 * * if the model is changed programmatically and not by a change to the input value
20619 * Note, this directive requires `ngModel` to be present.
20622 * @param {expression} ngChange {@link guide/expression Expression} to evaluate upon change
20626 * <example name="ngChange-directive" module="changeExample">
20627 * <file name="index.html">
20629 * angular.module('changeExample', [])
20630 * .controller('ExampleController', ['$scope', function($scope) {
20631 * $scope.counter = 0;
20632 * $scope.change = function() {
20633 * $scope.counter++;
20637 * <div ng-controller="ExampleController">
20638 * <input type="checkbox" ng-model="confirmed" ng-change="change()" id="ng-change-example1" />
20639 * <input type="checkbox" ng-model="confirmed" id="ng-change-example2" />
20640 * <label for="ng-change-example2">Confirmed</label><br />
20641 * <tt>debug = {{confirmed}}</tt><br/>
20642 * <tt>counter = {{counter}}</tt><br/>
20645 * <file name="protractor.js" type="protractor">
20646 * var counter = element(by.binding('counter'));
20647 * var debug = element(by.binding('confirmed'));
20649 * it('should evaluate the expression if changing from view', function() {
20650 * expect(counter.getText()).toContain('0');
20652 * element(by.id('ng-change-example1')).click();
20654 * expect(counter.getText()).toContain('1');
20655 * expect(debug.getText()).toContain('true');
20658 * it('should not evaluate the expression if changing from model', function() {
20659 * element(by.id('ng-change-example2')).click();
20661 * expect(counter.getText()).toContain('0');
20662 * expect(debug.getText()).toContain('true');
20667 var ngChangeDirective = valueFn({
20669 require: 'ngModel',
20670 link: function(scope, element, attr, ctrl) {
20671 ctrl.$viewChangeListeners.push(function() {
20672 scope.$eval(attr.ngChange);
20677 function classDirective(name, selector) {
20678 name = 'ngClass' + name;
20679 return ['$animate', function($animate) {
20682 link: function(scope, element, attr) {
20685 scope.$watch(attr[name], ngClassWatchAction, true);
20687 attr.$observe('class', function(value) {
20688 ngClassWatchAction(scope.$eval(attr[name]));
20692 if (name !== 'ngClass') {
20693 scope.$watch('$index', function($index, old$index) {
20694 // jshint bitwise: false
20695 var mod = $index & 1;
20696 if (mod !== (old$index & 1)) {
20697 var classes = arrayClasses(scope.$eval(attr[name]));
20699 addClasses(classes) :
20700 removeClasses(classes);
20705 function addClasses(classes) {
20706 var newClasses = digestClassCounts(classes, 1);
20707 attr.$addClass(newClasses);
20710 function removeClasses(classes) {
20711 var newClasses = digestClassCounts(classes, -1);
20712 attr.$removeClass(newClasses);
20715 function digestClassCounts(classes, count) {
20716 var classCounts = element.data('$classCounts') || {};
20717 var classesToUpdate = [];
20718 forEach(classes, function(className) {
20719 if (count > 0 || classCounts[className]) {
20720 classCounts[className] = (classCounts[className] || 0) + count;
20721 if (classCounts[className] === +(count > 0)) {
20722 classesToUpdate.push(className);
20726 element.data('$classCounts', classCounts);
20727 return classesToUpdate.join(' ');
20730 function updateClasses(oldClasses, newClasses) {
20731 var toAdd = arrayDifference(newClasses, oldClasses);
20732 var toRemove = arrayDifference(oldClasses, newClasses);
20733 toAdd = digestClassCounts(toAdd, 1);
20734 toRemove = digestClassCounts(toRemove, -1);
20735 if (toAdd && toAdd.length) {
20736 $animate.addClass(element, toAdd);
20738 if (toRemove && toRemove.length) {
20739 $animate.removeClass(element, toRemove);
20743 function ngClassWatchAction(newVal) {
20744 if (selector === true || scope.$index % 2 === selector) {
20745 var newClasses = arrayClasses(newVal || []);
20747 addClasses(newClasses);
20748 } else if (!equals(newVal,oldVal)) {
20749 var oldClasses = arrayClasses(oldVal);
20750 updateClasses(oldClasses, newClasses);
20753 oldVal = shallowCopy(newVal);
20758 function arrayDifference(tokens1, tokens2) {
20762 for (var i = 0; i < tokens1.length; i++) {
20763 var token = tokens1[i];
20764 for (var j = 0; j < tokens2.length; j++) {
20765 if (token == tokens2[j]) continue outer;
20767 values.push(token);
20772 function arrayClasses(classVal) {
20773 if (isArray(classVal)) {
20775 } else if (isString(classVal)) {
20776 return classVal.split(' ');
20777 } else if (isObject(classVal)) {
20779 forEach(classVal, function(v, k) {
20781 classes = classes.concat(k.split(' '));
20797 * The `ngClass` directive allows you to dynamically set CSS classes on an HTML element by databinding
20798 * an expression that represents all classes to be added.
20800 * The directive operates in three different ways, depending on which of three types the expression
20803 * 1. If the expression evaluates to a string, the string should be one or more space-delimited class
20806 * 2. If the expression evaluates to an array, each element of the array should be a string that is
20807 * one or more space-delimited class names.
20809 * 3. If the expression evaluates to an object, then for each key-value pair of the
20810 * object with a truthy value the corresponding key is used as a class name.
20812 * The directive won't add duplicate classes if a particular class was already set.
20814 * When the expression changes, the previously added classes are removed and only then the
20815 * new classes are added.
20818 * **add** - happens just before the class is applied to the elements
20820 * **remove** - happens just before the class is removed from the element
20823 * @param {expression} ngClass {@link guide/expression Expression} to eval. The result
20824 * of the evaluation can be a string representing space delimited class
20825 * names, an array, or a map of class names to boolean values. In the case of a map, the
20826 * names of the properties whose values are truthy will be added as css classes to the
20829 * @example Example that demonstrates basic bindings via ngClass directive.
20831 <file name="index.html">
20832 <p ng-class="{strike: deleted, bold: important, red: error}">Map Syntax Example</p>
20833 <input type="checkbox" ng-model="deleted"> deleted (apply "strike" class)<br>
20834 <input type="checkbox" ng-model="important"> important (apply "bold" class)<br>
20835 <input type="checkbox" ng-model="error"> error (apply "red" class)
20837 <p ng-class="style">Using String Syntax</p>
20838 <input type="text" ng-model="style" placeholder="Type: bold strike red">
20840 <p ng-class="[style1, style2, style3]">Using Array Syntax</p>
20841 <input ng-model="style1" placeholder="Type: bold, strike or red"><br>
20842 <input ng-model="style2" placeholder="Type: bold, strike or red"><br>
20843 <input ng-model="style3" placeholder="Type: bold, strike or red"><br>
20845 <file name="style.css">
20847 text-decoration: line-through;
20856 <file name="protractor.js" type="protractor">
20857 var ps = element.all(by.css('p'));
20859 it('should let you toggle the class', function() {
20861 expect(ps.first().getAttribute('class')).not.toMatch(/bold/);
20862 expect(ps.first().getAttribute('class')).not.toMatch(/red/);
20864 element(by.model('important')).click();
20865 expect(ps.first().getAttribute('class')).toMatch(/bold/);
20867 element(by.model('error')).click();
20868 expect(ps.first().getAttribute('class')).toMatch(/red/);
20871 it('should let you toggle string example', function() {
20872 expect(ps.get(1).getAttribute('class')).toBe('');
20873 element(by.model('style')).clear();
20874 element(by.model('style')).sendKeys('red');
20875 expect(ps.get(1).getAttribute('class')).toBe('red');
20878 it('array example should have 3 classes', function() {
20879 expect(ps.last().getAttribute('class')).toBe('');
20880 element(by.model('style1')).sendKeys('bold');
20881 element(by.model('style2')).sendKeys('strike');
20882 element(by.model('style3')).sendKeys('red');
20883 expect(ps.last().getAttribute('class')).toBe('bold strike red');
20890 The example below demonstrates how to perform animations using ngClass.
20892 <example module="ngAnimate" deps="angular-animate.js" animations="true">
20893 <file name="index.html">
20894 <input id="setbtn" type="button" value="set" ng-click="myVar='my-class'">
20895 <input id="clearbtn" type="button" value="clear" ng-click="myVar=''">
20897 <span class="base-class" ng-class="myVar">Sample Text</span>
20899 <file name="style.css">
20901 -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
20902 transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
20905 .base-class.my-class {
20910 <file name="protractor.js" type="protractor">
20911 it('should check ng-class', function() {
20912 expect(element(by.css('.base-class')).getAttribute('class')).not.
20913 toMatch(/my-class/);
20915 element(by.id('setbtn')).click();
20917 expect(element(by.css('.base-class')).getAttribute('class')).
20918 toMatch(/my-class/);
20920 element(by.id('clearbtn')).click();
20922 expect(element(by.css('.base-class')).getAttribute('class')).not.
20923 toMatch(/my-class/);
20929 ## ngClass and pre-existing CSS3 Transitions/Animations
20930 The ngClass directive still supports CSS3 Transitions/Animations even if they do not follow the ngAnimate CSS naming structure.
20931 Upon animation ngAnimate will apply supplementary CSS classes to track the start and end of an animation, but this will not hinder
20932 any pre-existing CSS transitions already on the element. To get an idea of what happens during a class-based animation, be sure
20933 to view the step by step details of {@link ng.$animate#addClass $animate.addClass} and
20934 {@link ng.$animate#removeClass $animate.removeClass}.
20936 var ngClassDirective = classDirective('', true);
20944 * The `ngClassOdd` and `ngClassEven` directives work exactly as
20945 * {@link ng.directive:ngClass ngClass}, except they work in
20946 * conjunction with `ngRepeat` and take effect only on odd (even) rows.
20948 * This directive can be applied only within the scope of an
20949 * {@link ng.directive:ngRepeat ngRepeat}.
20952 * @param {expression} ngClassOdd {@link guide/expression Expression} to eval. The result
20953 * of the evaluation can be a string representing space delimited class names or an array.
20957 <file name="index.html">
20958 <ol ng-init="names=['John', 'Mary', 'Cate', 'Suz']">
20959 <li ng-repeat="name in names">
20960 <span ng-class-odd="'odd'" ng-class-even="'even'">
20966 <file name="style.css">
20974 <file name="protractor.js" type="protractor">
20975 it('should check ng-class-odd and ng-class-even', function() {
20976 expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')).
20978 expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')).
20984 var ngClassOddDirective = classDirective('Odd', 0);
20988 * @name ngClassEven
20992 * The `ngClassOdd` and `ngClassEven` directives work exactly as
20993 * {@link ng.directive:ngClass ngClass}, except they work in
20994 * conjunction with `ngRepeat` and take effect only on odd (even) rows.
20996 * This directive can be applied only within the scope of an
20997 * {@link ng.directive:ngRepeat ngRepeat}.
21000 * @param {expression} ngClassEven {@link guide/expression Expression} to eval. The
21001 * result of the evaluation can be a string representing space delimited class names or an array.
21005 <file name="index.html">
21006 <ol ng-init="names=['John', 'Mary', 'Cate', 'Suz']">
21007 <li ng-repeat="name in names">
21008 <span ng-class-odd="'odd'" ng-class-even="'even'">
21009 {{name}}
21014 <file name="style.css">
21022 <file name="protractor.js" type="protractor">
21023 it('should check ng-class-odd and ng-class-even', function() {
21024 expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')).
21026 expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')).
21032 var ngClassEvenDirective = classDirective('Even', 1);
21040 * The `ngCloak` directive is used to prevent the Angular html template from being briefly
21041 * displayed by the browser in its raw (uncompiled) form while your application is loading. Use this
21042 * directive to avoid the undesirable flicker effect caused by the html template display.
21044 * The directive can be applied to the `<body>` element, but the preferred usage is to apply
21045 * multiple `ngCloak` directives to small portions of the page to permit progressive rendering
21046 * of the browser view.
21048 * `ngCloak` works in cooperation with the following css rule embedded within `angular.js` and
21049 * `angular.min.js`.
21050 * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
21053 * [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
21054 * display: none !important;
21058 * When this css rule is loaded by the browser, all html elements (including their children) that
21059 * are tagged with the `ngCloak` directive are hidden. When Angular encounters this directive
21060 * during the compilation of the template it deletes the `ngCloak` element attribute, making
21061 * the compiled element visible.
21063 * For the best result, the `angular.js` script must be loaded in the head section of the html
21064 * document; alternatively, the css rule above must be included in the external stylesheet of the
21067 * Legacy browsers, like IE7, do not provide attribute selector support (added in CSS 2.1) so they
21068 * cannot match the `[ng\:cloak]` selector. To work around this limitation, you must add the css
21069 * class `ng-cloak` in addition to the `ngCloak` directive as shown in the example below.
21075 <file name="index.html">
21076 <div id="template1" ng-cloak>{{ 'hello' }}</div>
21077 <div id="template2" ng-cloak class="ng-cloak">{{ 'hello IE7' }}</div>
21079 <file name="protractor.js" type="protractor">
21080 it('should remove the template directive and css class', function() {
21081 expect($('#template1').getAttribute('ng-cloak')).
21083 expect($('#template2').getAttribute('ng-cloak')).
21090 var ngCloakDirective = ngDirective({
21091 compile: function(element, attr) {
21092 attr.$set('ngCloak', undefined);
21093 element.removeClass('ng-cloak');
21099 * @name ngController
21102 * The `ngController` directive attaches a controller class to the view. This is a key aspect of how angular
21103 * supports the principles behind the Model-View-Controller design pattern.
21105 * MVC components in angular:
21107 * * Model — Models are the properties of a scope; scopes are attached to the DOM where scope properties
21108 * are accessed through bindings.
21109 * * View — The template (HTML with data bindings) that is rendered into the View.
21110 * * Controller — The `ngController` directive specifies a Controller class; the class contains business
21111 * logic behind the application to decorate the scope with functions and values
21113 * Note that you can also attach controllers to the DOM by declaring it in a route definition
21114 * via the {@link ngRoute.$route $route} service. A common mistake is to declare the controller
21115 * again using `ng-controller` in the template itself. This will cause the controller to be attached
21116 * and executed twice.
21121 * @param {expression} ngController Name of a constructor function registered with the current
21122 * {@link ng.$controllerProvider $controllerProvider} or an {@link guide/expression expression}
21123 * that on the current scope evaluates to a constructor function.
21125 * The controller instance can be published into a scope property by specifying
21126 * `ng-controller="as propertyName"`.
21128 * If the current `$controllerProvider` is configured to use globals (via
21129 * {@link ng.$controllerProvider#allowGlobals `$controllerProvider.allowGlobals()` }), this may
21130 * also be the name of a globally accessible constructor function (not recommended).
21133 * Here is a simple form for editing user contact information. Adding, removing, clearing, and
21134 * greeting are methods declared on the controller (see source tab). These methods can
21135 * easily be called from the angular markup. Any changes to the data are automatically reflected
21136 * in the View without the need for a manual update.
21138 * Two different declaration styles are included below:
21140 * * one binds methods and properties directly onto the controller using `this`:
21141 * `ng-controller="SettingsController1 as settings"`
21142 * * one injects `$scope` into the controller:
21143 * `ng-controller="SettingsController2"`
21145 * The second option is more common in the Angular community, and is generally used in boilerplates
21146 * and in this guide. However, there are advantages to binding properties directly to the controller
21147 * and avoiding scope.
21149 * * Using `controller as` makes it obvious which controller you are accessing in the template when
21150 * multiple controllers apply to an element.
21151 * * If you are writing your controllers as classes you have easier access to the properties and
21152 * methods, which will appear on the scope, from inside the controller code.
21153 * * Since there is always a `.` in the bindings, you don't have to worry about prototypal
21154 * inheritance masking primitives.
21156 * This example demonstrates the `controller as` syntax.
21158 * <example name="ngControllerAs" module="controllerAsExample">
21159 * <file name="index.html">
21160 * <div id="ctrl-as-exmpl" ng-controller="SettingsController1 as settings">
21161 * Name: <input type="text" ng-model="settings.name"/>
21162 * [ <a href="" ng-click="settings.greet()">greet</a> ]<br/>
21165 * <li ng-repeat="contact in settings.contacts">
21166 * <select ng-model="contact.type">
21167 * <option>phone</option>
21168 * <option>email</option>
21170 * <input type="text" ng-model="contact.value"/>
21171 * [ <a href="" ng-click="settings.clearContact(contact)">clear</a>
21172 * | <a href="" ng-click="settings.removeContact(contact)">X</a> ]
21174 * <li>[ <a href="" ng-click="settings.addContact()">add</a> ]</li>
21178 * <file name="app.js">
21179 * angular.module('controllerAsExample', [])
21180 * .controller('SettingsController1', SettingsController1);
21182 * function SettingsController1() {
21183 * this.name = "John Smith";
21184 * this.contacts = [
21185 * {type: 'phone', value: '408 555 1212'},
21186 * {type: 'email', value: 'john.smith@example.org'} ];
21189 * SettingsController1.prototype.greet = function() {
21190 * alert(this.name);
21193 * SettingsController1.prototype.addContact = function() {
21194 * this.contacts.push({type: 'email', value: 'yourname@example.org'});
21197 * SettingsController1.prototype.removeContact = function(contactToRemove) {
21198 * var index = this.contacts.indexOf(contactToRemove);
21199 * this.contacts.splice(index, 1);
21202 * SettingsController1.prototype.clearContact = function(contact) {
21203 * contact.type = 'phone';
21204 * contact.value = '';
21207 * <file name="protractor.js" type="protractor">
21208 * it('should check controller as', function() {
21209 * var container = element(by.id('ctrl-as-exmpl'));
21210 * expect(container.element(by.model('settings.name'))
21211 * .getAttribute('value')).toBe('John Smith');
21213 * var firstRepeat =
21214 * container.element(by.repeater('contact in settings.contacts').row(0));
21215 * var secondRepeat =
21216 * container.element(by.repeater('contact in settings.contacts').row(1));
21218 * expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
21219 * .toBe('408 555 1212');
21221 * expect(secondRepeat.element(by.model('contact.value')).getAttribute('value'))
21222 * .toBe('john.smith@example.org');
21224 * firstRepeat.element(by.linkText('clear')).click();
21226 * expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
21229 * container.element(by.linkText('add')).click();
21231 * expect(container.element(by.repeater('contact in settings.contacts').row(2))
21232 * .element(by.model('contact.value'))
21233 * .getAttribute('value'))
21234 * .toBe('yourname@example.org');
21239 * This example demonstrates the "attach to `$scope`" style of controller.
21241 * <example name="ngController" module="controllerExample">
21242 * <file name="index.html">
21243 * <div id="ctrl-exmpl" ng-controller="SettingsController2">
21244 * Name: <input type="text" ng-model="name"/>
21245 * [ <a href="" ng-click="greet()">greet</a> ]<br/>
21248 * <li ng-repeat="contact in contacts">
21249 * <select ng-model="contact.type">
21250 * <option>phone</option>
21251 * <option>email</option>
21253 * <input type="text" ng-model="contact.value"/>
21254 * [ <a href="" ng-click="clearContact(contact)">clear</a>
21255 * | <a href="" ng-click="removeContact(contact)">X</a> ]
21257 * <li>[ <a href="" ng-click="addContact()">add</a> ]</li>
21261 * <file name="app.js">
21262 * angular.module('controllerExample', [])
21263 * .controller('SettingsController2', ['$scope', SettingsController2]);
21265 * function SettingsController2($scope) {
21266 * $scope.name = "John Smith";
21267 * $scope.contacts = [
21268 * {type:'phone', value:'408 555 1212'},
21269 * {type:'email', value:'john.smith@example.org'} ];
21271 * $scope.greet = function() {
21272 * alert($scope.name);
21275 * $scope.addContact = function() {
21276 * $scope.contacts.push({type:'email', value:'yourname@example.org'});
21279 * $scope.removeContact = function(contactToRemove) {
21280 * var index = $scope.contacts.indexOf(contactToRemove);
21281 * $scope.contacts.splice(index, 1);
21284 * $scope.clearContact = function(contact) {
21285 * contact.type = 'phone';
21286 * contact.value = '';
21290 * <file name="protractor.js" type="protractor">
21291 * it('should check controller', function() {
21292 * var container = element(by.id('ctrl-exmpl'));
21294 * expect(container.element(by.model('name'))
21295 * .getAttribute('value')).toBe('John Smith');
21297 * var firstRepeat =
21298 * container.element(by.repeater('contact in contacts').row(0));
21299 * var secondRepeat =
21300 * container.element(by.repeater('contact in contacts').row(1));
21302 * expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
21303 * .toBe('408 555 1212');
21304 * expect(secondRepeat.element(by.model('contact.value')).getAttribute('value'))
21305 * .toBe('john.smith@example.org');
21307 * firstRepeat.element(by.linkText('clear')).click();
21309 * expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
21312 * container.element(by.linkText('add')).click();
21314 * expect(container.element(by.repeater('contact in contacts').row(2))
21315 * .element(by.model('contact.value'))
21316 * .getAttribute('value'))
21317 * .toBe('yourname@example.org');
21323 var ngControllerDirective = [function() {
21338 * Enables [CSP (Content Security Policy)](https://developer.mozilla.org/en/Security/CSP) support.
21340 * This is necessary when developing things like Google Chrome Extensions or Universal Windows Apps.
21342 * CSP forbids apps to use `eval` or `Function(string)` generated functions (among other things).
21343 * For Angular to be CSP compatible there are only two things that we need to do differently:
21345 * - don't use `Function` constructor to generate optimized value getters
21346 * - don't inject custom stylesheet into the document
21348 * AngularJS uses `Function(string)` generated functions as a speed optimization. Applying the `ngCsp`
21349 * directive will cause Angular to use CSP compatibility mode. When this mode is on AngularJS will
21350 * evaluate all expressions up to 30% slower than in non-CSP mode, but no security violations will
21353 * CSP forbids JavaScript to inline stylesheet rules. In non CSP mode Angular automatically
21354 * includes some CSS rules (e.g. {@link ng.directive:ngCloak ngCloak}).
21355 * To make those directives work in CSP mode, include the `angular-csp.css` manually.
21357 * Angular tries to autodetect if CSP is active and automatically turn on the CSP-safe mode. This
21358 * autodetection however triggers a CSP error to be logged in the console:
21361 * Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of
21362 * script in the following Content Security Policy directive: "default-src 'self'". Note that
21363 * 'script-src' was not explicitly set, so 'default-src' is used as a fallback.
21366 * This error is harmless but annoying. To prevent the error from showing up, put the `ngCsp`
21367 * directive on the root element of the application or on the `angular.js` script tag, whichever
21368 * appears first in the html document.
21370 * *Note: This directive is only available in the `ng-csp` and `data-ng-csp` attribute form.*
21373 * This example shows how to apply the `ngCsp` directive to the `html` tag.
21376 <html ng-app ng-csp>
21382 // Note: the suffix `.csp` in the example name triggers
21383 // csp mode in our http server!
21384 <example name="example.csp" module="cspExample" ng-csp="true">
21385 <file name="index.html">
21386 <div ng-controller="MainController as ctrl">
21388 <button ng-click="ctrl.inc()" id="inc">Increment</button>
21389 <span id="counter">
21395 <button ng-click="ctrl.evil()" id="evil">Evil</button>
21396 <span id="evilError">
21402 <file name="script.js">
21403 angular.module('cspExample', [])
21404 .controller('MainController', function() {
21406 this.inc = function() {
21409 this.evil = function() {
21410 // jshint evil:true
21414 this.evilError = e.message;
21419 <file name="protractor.js" type="protractor">
21420 var util, webdriver;
21422 var incBtn = element(by.id('inc'));
21423 var counter = element(by.id('counter'));
21424 var evilBtn = element(by.id('evil'));
21425 var evilError = element(by.id('evilError'));
21427 function getAndClearSevereErrors() {
21428 return browser.manage().logs().get('browser').then(function(browserLog) {
21429 return browserLog.filter(function(logEntry) {
21430 return logEntry.level.value > webdriver.logging.Level.WARNING.value;
21435 function clearErrors() {
21436 getAndClearSevereErrors();
21439 function expectNoErrors() {
21440 getAndClearSevereErrors().then(function(filteredLog) {
21441 expect(filteredLog.length).toEqual(0);
21442 if (filteredLog.length) {
21443 console.log('browser console errors: ' + util.inspect(filteredLog));
21448 function expectError(regex) {
21449 getAndClearSevereErrors().then(function(filteredLog) {
21451 filteredLog.forEach(function(log) {
21452 if (log.message.match(regex)) {
21457 throw new Error('expected an error that matches ' + regex);
21462 beforeEach(function() {
21463 util = require('util');
21464 webdriver = require('protractor/node_modules/selenium-webdriver');
21467 // For now, we only test on Chrome,
21468 // as Safari does not load the page with Protractor's injected scripts,
21469 // and Firefox webdriver always disables content security policy (#6358)
21470 if (browser.params.browser !== 'chrome') {
21474 it('should not report errors when the page is loaded', function() {
21475 // clear errors so we are not dependent on previous tests
21477 // Need to reload the page as the page is already loaded when
21479 browser.driver.getCurrentUrl().then(function(url) {
21485 it('should evaluate expressions', function() {
21486 expect(counter.getText()).toEqual('0');
21488 expect(counter.getText()).toEqual('1');
21492 it('should throw and report an error when using "eval"', function() {
21494 expect(evilError.getText()).toMatch(/Content Security Policy/);
21495 expectError(/Content Security Policy/);
21501 // ngCsp is not implemented as a proper directive any more, because we need it be processed while we
21502 // bootstrap the system (before $parse is instantiated), for this reason we just have
21503 // the csp.isActive() fn that looks for ng-csp attribute anywhere in the current doc
21510 * The ngClick directive allows you to specify custom behavior when
21511 * an element is clicked.
21515 * @param {expression} ngClick {@link guide/expression Expression} to evaluate upon
21516 * click. ({@link guide/expression#-event- Event object is available as `$event`})
21520 <file name="index.html">
21521 <button ng-click="count = count + 1" ng-init="count=0">
21528 <file name="protractor.js" type="protractor">
21529 it('should check ng-click', function() {
21530 expect(element(by.binding('count')).getText()).toMatch('0');
21531 element(by.css('button')).click();
21532 expect(element(by.binding('count')).getText()).toMatch('1');
21538 * A collection of directives that allows creation of custom event handlers that are defined as
21539 * angular expressions and are compiled and executed within the current scope.
21541 var ngEventDirectives = {};
21543 // For events that might fire synchronously during DOM manipulation
21544 // we need to execute their event handlers asynchronously using $evalAsync,
21545 // so that they are not executed in an inconsistent state.
21546 var forceAsyncEvents = {
21551 'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste'.split(' '),
21552 function(eventName) {
21553 var directiveName = directiveNormalize('ng-' + eventName);
21554 ngEventDirectives[directiveName] = ['$parse', '$rootScope', function($parse, $rootScope) {
21557 compile: function($element, attr) {
21558 // We expose the powerful $event object on the scope that provides access to the Window,
21559 // etc. that isn't protected by the fast paths in $parse. We explicitly request better
21560 // checks at the cost of speed since event handler expressions are not executed as
21561 // frequently as regular change detection.
21562 var fn = $parse(attr[directiveName], /* interceptorFn */ null, /* expensiveChecks */ true);
21563 return function ngEventHandler(scope, element) {
21564 element.on(eventName, function(event) {
21565 var callback = function() {
21566 fn(scope, {$event:event});
21568 if (forceAsyncEvents[eventName] && $rootScope.$$phase) {
21569 scope.$evalAsync(callback);
21571 scope.$apply(callback);
21586 * The `ngDblclick` directive allows you to specify custom behavior on a dblclick event.
21590 * @param {expression} ngDblclick {@link guide/expression Expression} to evaluate upon
21591 * a dblclick. (The Event object is available as `$event`)
21595 <file name="index.html">
21596 <button ng-dblclick="count = count + 1" ng-init="count=0">
21597 Increment (on double click)
21607 * @name ngMousedown
21610 * The ngMousedown directive allows you to specify custom behavior on mousedown event.
21614 * @param {expression} ngMousedown {@link guide/expression Expression} to evaluate upon
21615 * mousedown. ({@link guide/expression#-event- Event object is available as `$event`})
21619 <file name="index.html">
21620 <button ng-mousedown="count = count + 1" ng-init="count=0">
21621 Increment (on mouse down)
21634 * Specify custom behavior on mouseup event.
21638 * @param {expression} ngMouseup {@link guide/expression Expression} to evaluate upon
21639 * mouseup. ({@link guide/expression#-event- Event object is available as `$event`})
21643 <file name="index.html">
21644 <button ng-mouseup="count = count + 1" ng-init="count=0">
21645 Increment (on mouse up)
21654 * @name ngMouseover
21657 * Specify custom behavior on mouseover event.
21661 * @param {expression} ngMouseover {@link guide/expression Expression} to evaluate upon
21662 * mouseover. ({@link guide/expression#-event- Event object is available as `$event`})
21666 <file name="index.html">
21667 <button ng-mouseover="count = count + 1" ng-init="count=0">
21668 Increment (when mouse is over)
21678 * @name ngMouseenter
21681 * Specify custom behavior on mouseenter event.
21685 * @param {expression} ngMouseenter {@link guide/expression Expression} to evaluate upon
21686 * mouseenter. ({@link guide/expression#-event- Event object is available as `$event`})
21690 <file name="index.html">
21691 <button ng-mouseenter="count = count + 1" ng-init="count=0">
21692 Increment (when mouse enters)
21702 * @name ngMouseleave
21705 * Specify custom behavior on mouseleave event.
21709 * @param {expression} ngMouseleave {@link guide/expression Expression} to evaluate upon
21710 * mouseleave. ({@link guide/expression#-event- Event object is available as `$event`})
21714 <file name="index.html">
21715 <button ng-mouseleave="count = count + 1" ng-init="count=0">
21716 Increment (when mouse leaves)
21726 * @name ngMousemove
21729 * Specify custom behavior on mousemove event.
21733 * @param {expression} ngMousemove {@link guide/expression Expression} to evaluate upon
21734 * mousemove. ({@link guide/expression#-event- Event object is available as `$event`})
21738 <file name="index.html">
21739 <button ng-mousemove="count = count + 1" ng-init="count=0">
21740 Increment (when mouse moves)
21753 * Specify custom behavior on keydown event.
21757 * @param {expression} ngKeydown {@link guide/expression Expression} to evaluate upon
21758 * keydown. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
21762 <file name="index.html">
21763 <input ng-keydown="count = count + 1" ng-init="count=0">
21764 key down count: {{count}}
21775 * Specify custom behavior on keyup event.
21779 * @param {expression} ngKeyup {@link guide/expression Expression} to evaluate upon
21780 * keyup. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
21784 <file name="index.html">
21785 <p>Typing in the input box below updates the key count</p>
21786 <input ng-keyup="count = count + 1" ng-init="count=0"> key up count: {{count}}
21788 <p>Typing in the input box below updates the keycode</p>
21789 <input ng-keyup="event=$event">
21790 <p>event keyCode: {{ event.keyCode }}</p>
21791 <p>event altKey: {{ event.altKey }}</p>
21802 * Specify custom behavior on keypress event.
21805 * @param {expression} ngKeypress {@link guide/expression Expression} to evaluate upon
21806 * keypress. ({@link guide/expression#-event- Event object is available as `$event`}
21807 * and can be interrogated for keyCode, altKey, etc.)
21811 <file name="index.html">
21812 <input ng-keypress="count = count + 1" ng-init="count=0">
21813 key press count: {{count}}
21824 * Enables binding angular expressions to onsubmit events.
21826 * Additionally it prevents the default action (which for form means sending the request to the
21827 * server and reloading the current page), but only if the form does not contain `action`,
21828 * `data-action`, or `x-action` attributes.
21830 * <div class="alert alert-warning">
21831 * **Warning:** Be careful not to cause "double-submission" by using both the `ngClick` and
21832 * `ngSubmit` handlers together. See the
21833 * {@link form#submitting-a-form-and-preventing-the-default-action `form` directive documentation}
21834 * for a detailed discussion of when `ngSubmit` may be triggered.
21839 * @param {expression} ngSubmit {@link guide/expression Expression} to eval.
21840 * ({@link guide/expression#-event- Event object is available as `$event`})
21843 <example module="submitExample">
21844 <file name="index.html">
21846 angular.module('submitExample', [])
21847 .controller('ExampleController', ['$scope', function($scope) {
21849 $scope.text = 'hello';
21850 $scope.submit = function() {
21852 $scope.list.push(this.text);
21858 <form ng-submit="submit()" ng-controller="ExampleController">
21859 Enter text and hit enter:
21860 <input type="text" ng-model="text" name="text" />
21861 <input type="submit" id="submit" value="Submit" />
21862 <pre>list={{list}}</pre>
21865 <file name="protractor.js" type="protractor">
21866 it('should check ng-submit', function() {
21867 expect(element(by.binding('list')).getText()).toBe('list=[]');
21868 element(by.css('#submit')).click();
21869 expect(element(by.binding('list')).getText()).toContain('hello');
21870 expect(element(by.model('text')).getAttribute('value')).toBe('');
21872 it('should ignore empty strings', function() {
21873 expect(element(by.binding('list')).getText()).toBe('list=[]');
21874 element(by.css('#submit')).click();
21875 element(by.css('#submit')).click();
21876 expect(element(by.binding('list')).getText()).toContain('hello');
21887 * Specify custom behavior on focus event.
21889 * Note: As the `focus` event is executed synchronously when calling `input.focus()`
21890 * AngularJS executes the expression using `scope.$evalAsync` if the event is fired
21891 * during an `$apply` to ensure a consistent state.
21893 * @element window, input, select, textarea, a
21895 * @param {expression} ngFocus {@link guide/expression Expression} to evaluate upon
21896 * focus. ({@link guide/expression#-event- Event object is available as `$event`})
21899 * See {@link ng.directive:ngClick ngClick}
21907 * Specify custom behavior on blur event.
21909 * A [blur event](https://developer.mozilla.org/en-US/docs/Web/Events/blur) fires when
21910 * an element has lost focus.
21912 * Note: As the `blur` event is executed synchronously also during DOM manipulations
21913 * (e.g. removing a focussed input),
21914 * AngularJS executes the expression using `scope.$evalAsync` if the event is fired
21915 * during an `$apply` to ensure a consistent state.
21917 * @element window, input, select, textarea, a
21919 * @param {expression} ngBlur {@link guide/expression Expression} to evaluate upon
21920 * blur. ({@link guide/expression#-event- Event object is available as `$event`})
21923 * See {@link ng.directive:ngClick ngClick}
21931 * Specify custom behavior on copy event.
21933 * @element window, input, select, textarea, a
21935 * @param {expression} ngCopy {@link guide/expression Expression} to evaluate upon
21936 * copy. ({@link guide/expression#-event- Event object is available as `$event`})
21940 <file name="index.html">
21941 <input ng-copy="copied=true" ng-init="copied=false; value='copy me'" ng-model="value">
21952 * Specify custom behavior on cut event.
21954 * @element window, input, select, textarea, a
21956 * @param {expression} ngCut {@link guide/expression Expression} to evaluate upon
21957 * cut. ({@link guide/expression#-event- Event object is available as `$event`})
21961 <file name="index.html">
21962 <input ng-cut="cut=true" ng-init="cut=false; value='cut me'" ng-model="value">
21973 * Specify custom behavior on paste event.
21975 * @element window, input, select, textarea, a
21977 * @param {expression} ngPaste {@link guide/expression Expression} to evaluate upon
21978 * paste. ({@link guide/expression#-event- Event object is available as `$event`})
21982 <file name="index.html">
21983 <input ng-paste="paste=true" ng-init="paste=false" placeholder='paste here'>
21995 * The `ngIf` directive removes or recreates a portion of the DOM tree based on an
21996 * {expression}. If the expression assigned to `ngIf` evaluates to a false
21997 * value then the element is removed from the DOM, otherwise a clone of the
21998 * element is reinserted into the DOM.
22000 * `ngIf` differs from `ngShow` and `ngHide` in that `ngIf` completely removes and recreates the
22001 * element in the DOM rather than changing its visibility via the `display` css property. A common
22002 * case when this difference is significant is when using css selectors that rely on an element's
22003 * position within the DOM, such as the `:first-child` or `:last-child` pseudo-classes.
22005 * Note that when an element is removed using `ngIf` its scope is destroyed and a new scope
22006 * is created when the element is restored. The scope created within `ngIf` inherits from
22007 * its parent scope using
22008 * [prototypal inheritance](https://github.com/angular/angular.js/wiki/Understanding-Scopes#javascript-prototypal-inheritance).
22009 * An important implication of this is if `ngModel` is used within `ngIf` to bind to
22010 * a javascript primitive defined in the parent scope. In this case any modifications made to the
22011 * variable within the child scope will override (hide) the value in the parent scope.
22013 * Also, `ngIf` recreates elements using their compiled state. An example of this behavior
22014 * is if an element's class attribute is directly modified after it's compiled, using something like
22015 * jQuery's `.addClass()` method, and the element is later removed. When `ngIf` recreates the element
22016 * the added class will be lost because the original compiled state is used to regenerate the element.
22018 * Additionally, you can provide animations via the `ngAnimate` module to animate the `enter`
22019 * and `leave` effects.
22022 * enter - happens just after the `ngIf` contents change and a new DOM element is created and injected into the `ngIf` container
22023 * leave - happens just before the `ngIf` contents are removed from the DOM
22028 * @param {expression} ngIf If the {@link guide/expression expression} is falsy then
22029 * the element is removed from the DOM tree. If it is truthy a copy of the compiled
22030 * element is added to the DOM tree.
22033 <example module="ngAnimate" deps="angular-animate.js" animations="true">
22034 <file name="index.html">
22035 Click me: <input type="checkbox" ng-model="checked" ng-init="checked=true" /><br/>
22037 <span ng-if="checked" class="animate-if">
22038 This is removed when the checkbox is unchecked.
22041 <file name="animations.css">
22044 border:1px solid black;
22048 .animate-if.ng-enter, .animate-if.ng-leave {
22049 -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
22050 transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
22053 .animate-if.ng-enter,
22054 .animate-if.ng-leave.ng-leave-active {
22058 .animate-if.ng-leave,
22059 .animate-if.ng-enter.ng-enter-active {
22065 var ngIfDirective = ['$animate', function($animate) {
22067 multiElement: true,
22068 transclude: 'element',
22073 link: function($scope, $element, $attr, ctrl, $transclude) {
22074 var block, childScope, previousElements;
22075 $scope.$watch($attr.ngIf, function ngIfWatchAction(value) {
22079 $transclude(function(clone, newScope) {
22080 childScope = newScope;
22081 clone[clone.length++] = document.createComment(' end ngIf: ' + $attr.ngIf + ' ');
22082 // Note: We only need the first/last node of the cloned nodes.
22083 // However, we need to keep the reference to the jqlite wrapper as it might be changed later
22084 // by a directive with templateUrl when its template arrives.
22088 $animate.enter(clone, $element.parent(), $element);
22092 if (previousElements) {
22093 previousElements.remove();
22094 previousElements = null;
22097 childScope.$destroy();
22101 previousElements = getBlockNodes(block.clone);
22102 $animate.leave(previousElements).then(function() {
22103 previousElements = null;
22119 * Fetches, compiles and includes an external HTML fragment.
22121 * By default, the template URL is restricted to the same domain and protocol as the
22122 * application document. This is done by calling {@link $sce#getTrustedResourceUrl
22123 * $sce.getTrustedResourceUrl} on it. To load templates from other domains or protocols
22124 * you may either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist them} or
22125 * {@link $sce#trustAsResourceUrl wrap them} as trusted values. Refer to Angular's {@link
22126 * ng.$sce Strict Contextual Escaping}.
22128 * In addition, the browser's
22129 * [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest)
22130 * and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/)
22131 * policy may further restrict whether the template is successfully loaded.
22132 * For example, `ngInclude` won't work for cross-domain requests on all browsers and for `file://`
22133 * access on some browsers.
22136 * enter - animation is used to bring new content into the browser.
22137 * leave - animation is used to animate existing content away.
22139 * The enter and leave animation occur concurrently.
22144 * @param {string} ngInclude|src angular expression evaluating to URL. If the source is a string constant,
22145 * make sure you wrap it in **single** quotes, e.g. `src="'myPartialTemplate.html'"`.
22146 * @param {string=} onload Expression to evaluate when a new partial is loaded.
22148 * @param {string=} autoscroll Whether `ngInclude` should call {@link ng.$anchorScroll
22149 * $anchorScroll} to scroll the viewport after the content is loaded.
22151 * - If the attribute is not set, disable scrolling.
22152 * - If the attribute is set without value, enable scrolling.
22153 * - Otherwise enable scrolling only if the expression evaluates to truthy value.
22156 <example module="includeExample" deps="angular-animate.js" animations="true">
22157 <file name="index.html">
22158 <div ng-controller="ExampleController">
22159 <select ng-model="template" ng-options="t.name for t in templates">
22160 <option value="">(blank)</option>
22162 url of the template: <code>{{template.url}}</code>
22164 <div class="slide-animate-container">
22165 <div class="slide-animate" ng-include="template.url"></div>
22169 <file name="script.js">
22170 angular.module('includeExample', ['ngAnimate'])
22171 .controller('ExampleController', ['$scope', function($scope) {
22173 [ { name: 'template1.html', url: 'template1.html'},
22174 { name: 'template2.html', url: 'template2.html'} ];
22175 $scope.template = $scope.templates[0];
22178 <file name="template1.html">
22179 Content of template1.html
22181 <file name="template2.html">
22182 Content of template2.html
22184 <file name="animations.css">
22185 .slide-animate-container {
22188 border:1px solid black;
22197 .slide-animate.ng-enter, .slide-animate.ng-leave {
22198 -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
22199 transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
22210 .slide-animate.ng-enter {
22213 .slide-animate.ng-enter.ng-enter-active {
22217 .slide-animate.ng-leave {
22220 .slide-animate.ng-leave.ng-leave-active {
22224 <file name="protractor.js" type="protractor">
22225 var templateSelect = element(by.model('template'));
22226 var includeElem = element(by.css('[ng-include]'));
22228 it('should load template1.html', function() {
22229 expect(includeElem.getText()).toMatch(/Content of template1.html/);
22232 it('should load template2.html', function() {
22233 if (browser.params.browser == 'firefox') {
22234 // Firefox can't handle using selects
22235 // See https://github.com/angular/protractor/issues/480
22238 templateSelect.click();
22239 templateSelect.all(by.css('option')).get(2).click();
22240 expect(includeElem.getText()).toMatch(/Content of template2.html/);
22243 it('should change to blank', function() {
22244 if (browser.params.browser == 'firefox') {
22245 // Firefox can't handle using selects
22248 templateSelect.click();
22249 templateSelect.all(by.css('option')).get(0).click();
22250 expect(includeElem.isPresent()).toBe(false);
22259 * @name ngInclude#$includeContentRequested
22260 * @eventType emit on the scope ngInclude was declared in
22262 * Emitted every time the ngInclude content is requested.
22264 * @param {Object} angularEvent Synthetic event object.
22265 * @param {String} src URL of content to load.
22271 * @name ngInclude#$includeContentLoaded
22272 * @eventType emit on the current ngInclude scope
22274 * Emitted every time the ngInclude content is reloaded.
22276 * @param {Object} angularEvent Synthetic event object.
22277 * @param {String} src URL of content to load.
22283 * @name ngInclude#$includeContentError
22284 * @eventType emit on the scope ngInclude was declared in
22286 * Emitted when a template HTTP request yields an erroneous response (status < 200 || status > 299)
22288 * @param {Object} angularEvent Synthetic event object.
22289 * @param {String} src URL of content to load.
22291 var ngIncludeDirective = ['$templateRequest', '$anchorScroll', '$animate', '$sce',
22292 function($templateRequest, $anchorScroll, $animate, $sce) {
22297 transclude: 'element',
22298 controller: angular.noop,
22299 compile: function(element, attr) {
22300 var srcExp = attr.ngInclude || attr.src,
22301 onloadExp = attr.onload || '',
22302 autoScrollExp = attr.autoscroll;
22304 return function(scope, $element, $attr, ctrl, $transclude) {
22305 var changeCounter = 0,
22310 var cleanupLastIncludeContent = function() {
22311 if (previousElement) {
22312 previousElement.remove();
22313 previousElement = null;
22315 if (currentScope) {
22316 currentScope.$destroy();
22317 currentScope = null;
22319 if (currentElement) {
22320 $animate.leave(currentElement).then(function() {
22321 previousElement = null;
22323 previousElement = currentElement;
22324 currentElement = null;
22328 scope.$watch($sce.parseAsResourceUrl(srcExp), function ngIncludeWatchAction(src) {
22329 var afterAnimation = function() {
22330 if (isDefined(autoScrollExp) && (!autoScrollExp || scope.$eval(autoScrollExp))) {
22334 var thisChangeId = ++changeCounter;
22337 //set the 2nd param to true to ignore the template request error so that the inner
22338 //contents and scope can be cleaned up.
22339 $templateRequest(src, true).then(function(response) {
22340 if (thisChangeId !== changeCounter) return;
22341 var newScope = scope.$new();
22342 ctrl.template = response;
22344 // Note: This will also link all children of ng-include that were contained in the original
22345 // html. If that content contains controllers, ... they could pollute/change the scope.
22346 // However, using ng-include on an element with additional content does not make sense...
22347 // Note: We can't remove them in the cloneAttchFn of $transclude as that
22348 // function is called before linking the content, which would apply child
22349 // directives to non existing elements.
22350 var clone = $transclude(newScope, function(clone) {
22351 cleanupLastIncludeContent();
22352 $animate.enter(clone, null, $element).then(afterAnimation);
22355 currentScope = newScope;
22356 currentElement = clone;
22358 currentScope.$emit('$includeContentLoaded', src);
22359 scope.$eval(onloadExp);
22361 if (thisChangeId === changeCounter) {
22362 cleanupLastIncludeContent();
22363 scope.$emit('$includeContentError', src);
22366 scope.$emit('$includeContentRequested', src);
22368 cleanupLastIncludeContent();
22369 ctrl.template = null;
22377 // This directive is called during the $transclude call of the first `ngInclude` directive.
22378 // It will replace and compile the content of the element with the loaded template.
22379 // We need this directive so that the element content is already filled when
22380 // the link function of another directive on the same element as ngInclude
22382 var ngIncludeFillContentDirective = ['$compile',
22383 function($compile) {
22387 require: 'ngInclude',
22388 link: function(scope, $element, $attr, ctrl) {
22389 if (/SVG/.test($element[0].toString())) {
22390 // WebKit: https://bugs.webkit.org/show_bug.cgi?id=135698 --- SVG elements do not
22391 // support innerHTML, so detect this here and try to generate the contents
22394 $compile(jqLiteBuildFragment(ctrl.template, document).childNodes)(scope,
22395 function namespaceAdaptedClone(clone) {
22396 $element.append(clone);
22397 }, {futureParentElement: $element});
22401 $element.html(ctrl.template);
22402 $compile($element.contents())(scope);
22413 * The `ngInit` directive allows you to evaluate an expression in the
22416 * <div class="alert alert-error">
22417 * The only appropriate use of `ngInit` is for aliasing special properties of
22418 * {@link ng.directive:ngRepeat `ngRepeat`}, as seen in the demo below. Besides this case, you
22419 * should use {@link guide/controller controllers} rather than `ngInit`
22420 * to initialize values on a scope.
22422 * <div class="alert alert-warning">
22423 * **Note**: If you have assignment in `ngInit` along with {@link ng.$filter `$filter`}, make
22424 * sure you have parenthesis for correct precedence:
22425 * <pre class="prettyprint">
22426 * `<div ng-init="test1 = (data | orderBy:'name')"></div>`
22433 * @param {expression} ngInit {@link guide/expression Expression} to eval.
22436 <example module="initExample">
22437 <file name="index.html">
22439 angular.module('initExample', [])
22440 .controller('ExampleController', ['$scope', function($scope) {
22441 $scope.list = [['a', 'b'], ['c', 'd']];
22444 <div ng-controller="ExampleController">
22445 <div ng-repeat="innerList in list" ng-init="outerIndex = $index">
22446 <div ng-repeat="value in innerList" ng-init="innerIndex = $index">
22447 <span class="example-init">list[ {{outerIndex}} ][ {{innerIndex}} ] = {{value}};</span>
22452 <file name="protractor.js" type="protractor">
22453 it('should alias index positions', function() {
22454 var elements = element.all(by.css('.example-init'));
22455 expect(elements.get(0).getText()).toBe('list[ 0 ][ 0 ] = a;');
22456 expect(elements.get(1).getText()).toBe('list[ 0 ][ 1 ] = b;');
22457 expect(elements.get(2).getText()).toBe('list[ 1 ][ 0 ] = c;');
22458 expect(elements.get(3).getText()).toBe('list[ 1 ][ 1 ] = d;');
22463 var ngInitDirective = ngDirective({
22465 compile: function() {
22467 pre: function(scope, element, attrs) {
22468 scope.$eval(attrs.ngInit);
22479 * Text input that converts between a delimited string and an array of strings. The default
22480 * delimiter is a comma followed by a space - equivalent to `ng-list=", "`. You can specify a custom
22481 * delimiter as the value of the `ngList` attribute - for example, `ng-list=" | "`.
22483 * The behaviour of the directive is affected by the use of the `ngTrim` attribute.
22484 * * If `ngTrim` is set to `"false"` then whitespace around both the separator and each
22485 * list item is respected. This implies that the user of the directive is responsible for
22486 * dealing with whitespace but also allows you to use whitespace as a delimiter, such as a
22487 * tab or newline character.
22488 * * Otherwise whitespace around the delimiter is ignored when splitting (although it is respected
22489 * when joining the list items back together) and whitespace around each list item is stripped
22490 * before it is added to the model.
22492 * ### Example with Validation
22494 * <example name="ngList-directive" module="listExample">
22495 * <file name="app.js">
22496 * angular.module('listExample', [])
22497 * .controller('ExampleController', ['$scope', function($scope) {
22498 * $scope.names = ['morpheus', 'neo', 'trinity'];
22501 * <file name="index.html">
22502 * <form name="myForm" ng-controller="ExampleController">
22503 * List: <input name="namesInput" ng-model="names" ng-list required>
22504 * <span class="error" ng-show="myForm.namesInput.$error.required">
22507 * <tt>names = {{names}}</tt><br/>
22508 * <tt>myForm.namesInput.$valid = {{myForm.namesInput.$valid}}</tt><br/>
22509 * <tt>myForm.namesInput.$error = {{myForm.namesInput.$error}}</tt><br/>
22510 * <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
22511 * <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
22514 * <file name="protractor.js" type="protractor">
22515 * var listInput = element(by.model('names'));
22516 * var names = element(by.exactBinding('names'));
22517 * var valid = element(by.binding('myForm.namesInput.$valid'));
22518 * var error = element(by.css('span.error'));
22520 * it('should initialize to model', function() {
22521 * expect(names.getText()).toContain('["morpheus","neo","trinity"]');
22522 * expect(valid.getText()).toContain('true');
22523 * expect(error.getCssValue('display')).toBe('none');
22526 * it('should be invalid if empty', function() {
22527 * listInput.clear();
22528 * listInput.sendKeys('');
22530 * expect(names.getText()).toContain('');
22531 * expect(valid.getText()).toContain('false');
22532 * expect(error.getCssValue('display')).not.toBe('none');
22537 * ### Example - splitting on whitespace
22538 * <example name="ngList-directive-newlines">
22539 * <file name="index.html">
22540 * <textarea ng-model="list" ng-list=" " ng-trim="false"></textarea>
22541 * <pre>{{ list | json }}</pre>
22543 * <file name="protractor.js" type="protractor">
22544 * it("should split the text by newlines", function() {
22545 * var listInput = element(by.model('list'));
22546 * var output = element(by.binding('list | json'));
22547 * listInput.sendKeys('abc\ndef\nghi');
22548 * expect(output.getText()).toContain('[\n "abc",\n "def",\n "ghi"\n]');
22554 * @param {string=} ngList optional delimiter that should be used to split the value.
22556 var ngListDirective = function() {
22560 require: 'ngModel',
22561 link: function(scope, element, attr, ctrl) {
22562 // We want to control whitespace trimming so we use this convoluted approach
22563 // to access the ngList attribute, which doesn't pre-trim the attribute
22564 var ngList = element.attr(attr.$attr.ngList) || ', ';
22565 var trimValues = attr.ngTrim !== 'false';
22566 var separator = trimValues ? trim(ngList) : ngList;
22568 var parse = function(viewValue) {
22569 // If the viewValue is invalid (say required but empty) it will be `undefined`
22570 if (isUndefined(viewValue)) return;
22575 forEach(viewValue.split(separator), function(value) {
22576 if (value) list.push(trimValues ? trim(value) : value);
22583 ctrl.$parsers.push(parse);
22584 ctrl.$formatters.push(function(value) {
22585 if (isArray(value)) {
22586 return value.join(ngList);
22592 // Override the standard $isEmpty because an empty array means the input is empty.
22593 ctrl.$isEmpty = function(value) {
22594 return !value || !value.length;
22600 /* global VALID_CLASS: true,
22601 INVALID_CLASS: true,
22602 PRISTINE_CLASS: true,
22604 UNTOUCHED_CLASS: true,
22605 TOUCHED_CLASS: true,
22608 var VALID_CLASS = 'ng-valid',
22609 INVALID_CLASS = 'ng-invalid',
22610 PRISTINE_CLASS = 'ng-pristine',
22611 DIRTY_CLASS = 'ng-dirty',
22612 UNTOUCHED_CLASS = 'ng-untouched',
22613 TOUCHED_CLASS = 'ng-touched',
22614 PENDING_CLASS = 'ng-pending';
22617 var $ngModelMinErr = new minErr('ngModel');
22621 * @name ngModel.NgModelController
22623 * @property {string} $viewValue Actual string value in the view.
22624 * @property {*} $modelValue The value in the model that the control is bound to.
22625 * @property {Array.<Function>} $parsers Array of functions to execute, as a pipeline, whenever
22626 the control reads value from the DOM. The functions are called in array order, each passing
22627 its return value through to the next. The last return value is forwarded to the
22628 {@link ngModel.NgModelController#$validators `$validators`} collection.
22630 Parsers are used to sanitize / convert the {@link ngModel.NgModelController#$viewValue
22633 Returning `undefined` from a parser means a parse error occurred. In that case,
22634 no {@link ngModel.NgModelController#$validators `$validators`} will run and the `ngModel`
22635 will be set to `undefined` unless {@link ngModelOptions `ngModelOptions.allowInvalid`}
22636 is set to `true`. The parse error is stored in `ngModel.$error.parse`.
22639 * @property {Array.<Function>} $formatters Array of functions to execute, as a pipeline, whenever
22640 the model value changes. The functions are called in reverse array order, each passing the value through to the
22641 next. The last return value is used as the actual DOM value.
22642 Used to format / convert values for display in the control.
22644 * function formatter(value) {
22646 * return value.toUpperCase();
22649 * ngModel.$formatters.push(formatter);
22652 * @property {Object.<string, function>} $validators A collection of validators that are applied
22653 * whenever the model value changes. The key value within the object refers to the name of the
22654 * validator while the function refers to the validation operation. The validation operation is
22655 * provided with the model value as an argument and must return a true or false value depending
22656 * on the response of that validation.
22659 * ngModel.$validators.validCharacters = function(modelValue, viewValue) {
22660 * var value = modelValue || viewValue;
22661 * return /[0-9]+/.test(value) &&
22662 * /[a-z]+/.test(value) &&
22663 * /[A-Z]+/.test(value) &&
22664 * /\W+/.test(value);
22668 * @property {Object.<string, function>} $asyncValidators A collection of validations that are expected to
22669 * perform an asynchronous validation (e.g. a HTTP request). The validation function that is provided
22670 * is expected to return a promise when it is run during the model validation process. Once the promise
22671 * is delivered then the validation status will be set to true when fulfilled and false when rejected.
22672 * When the asynchronous validators are triggered, each of the validators will run in parallel and the model
22673 * value will only be updated once all validators have been fulfilled. As long as an asynchronous validator
22674 * is unfulfilled, its key will be added to the controllers `$pending` property. Also, all asynchronous validators
22675 * will only run once all synchronous validators have passed.
22677 * Please note that if $http is used then it is important that the server returns a success HTTP response code
22678 * in order to fulfill the validation and a status level of `4xx` in order to reject the validation.
22681 * ngModel.$asyncValidators.uniqueUsername = function(modelValue, viewValue) {
22682 * var value = modelValue || viewValue;
22684 * // Lookup user by username
22685 * return $http.get('/api/users/' + value).
22686 * then(function resolved() {
22687 * //username exists, this means validation fails
22688 * return $q.reject('exists');
22689 * }, function rejected() {
22690 * //username does not exist, therefore this validation passes
22696 * @property {Array.<Function>} $viewChangeListeners Array of functions to execute whenever the
22697 * view value has changed. It is called with no arguments, and its return value is ignored.
22698 * This can be used in place of additional $watches against the model value.
22700 * @property {Object} $error An object hash with all failing validator ids as keys.
22701 * @property {Object} $pending An object hash with all pending validator ids as keys.
22703 * @property {boolean} $untouched True if control has not lost focus yet.
22704 * @property {boolean} $touched True if control has lost focus.
22705 * @property {boolean} $pristine True if user has not interacted with the control yet.
22706 * @property {boolean} $dirty True if user has already interacted with the control.
22707 * @property {boolean} $valid True if there is no error.
22708 * @property {boolean} $invalid True if at least one error on the control.
22709 * @property {string} $name The name attribute of the control.
22713 * `NgModelController` provides API for the {@link ngModel `ngModel`} directive.
22714 * The controller contains services for data-binding, validation, CSS updates, and value formatting
22715 * and parsing. It purposefully does not contain any logic which deals with DOM rendering or
22716 * listening to DOM events.
22717 * Such DOM related logic should be provided by other directives which make use of
22718 * `NgModelController` for data-binding to control elements.
22719 * Angular provides this DOM logic for most {@link input `input`} elements.
22720 * At the end of this page you can find a {@link ngModel.NgModelController#custom-control-example
22721 * custom control example} that uses `ngModelController` to bind to `contenteditable` elements.
22724 * ### Custom Control Example
22725 * This example shows how to use `NgModelController` with a custom control to achieve
22726 * data-binding. Notice how different directives (`contenteditable`, `ng-model`, and `required`)
22727 * collaborate together to achieve the desired result.
22729 * `contenteditable` is an HTML5 attribute, which tells the browser to let the element
22730 * contents be edited in place by the user.
22732 * We are using the {@link ng.service:$sce $sce} service here and include the {@link ngSanitize $sanitize}
22733 * module to automatically remove "bad" content like inline event listener (e.g. `<span onclick="...">`).
22734 * However, as we are using `$sce` the model can still decide to provide unsafe content if it marks
22735 * that content using the `$sce` service.
22737 * <example name="NgModelController" module="customControl" deps="angular-sanitize.js">
22738 <file name="style.css">
22739 [contenteditable] {
22740 border: 1px solid black;
22741 background-color: white;
22746 border: 1px solid red;
22750 <file name="script.js">
22751 angular.module('customControl', ['ngSanitize']).
22752 directive('contenteditable', ['$sce', function($sce) {
22754 restrict: 'A', // only activate on element attribute
22755 require: '?ngModel', // get a hold of NgModelController
22756 link: function(scope, element, attrs, ngModel) {
22757 if (!ngModel) return; // do nothing if no ng-model
22759 // Specify how UI should be updated
22760 ngModel.$render = function() {
22761 element.html($sce.getTrustedHtml(ngModel.$viewValue || ''));
22764 // Listen for change events to enable binding
22765 element.on('blur keyup change', function() {
22766 scope.$evalAsync(read);
22768 read(); // initialize
22770 // Write data to the model
22772 var html = element.html();
22773 // When we clear the content editable the browser leaves a <br> behind
22774 // If strip-br attribute is provided then we strip this out
22775 if ( attrs.stripBr && html == '<br>' ) {
22778 ngModel.$setViewValue(html);
22784 <file name="index.html">
22785 <form name="myForm">
22786 <div contenteditable
22787 name="myWidget" ng-model="userContent"
22789 required>Change me!</div>
22790 <span ng-show="myForm.myWidget.$error.required">Required!</span>
22792 <textarea ng-model="userContent"></textarea>
22795 <file name="protractor.js" type="protractor">
22796 it('should data-bind and become invalid', function() {
22797 if (browser.params.browser == 'safari' || browser.params.browser == 'firefox') {
22798 // SafariDriver can't handle contenteditable
22799 // and Firefox driver can't clear contenteditables very well
22802 var contentEditable = element(by.css('[contenteditable]'));
22803 var content = 'Change me!';
22805 expect(contentEditable.getText()).toEqual(content);
22807 contentEditable.clear();
22808 contentEditable.sendKeys(protractor.Key.BACK_SPACE);
22809 expect(contentEditable.getText()).toEqual('');
22810 expect(contentEditable.getAttribute('class')).toMatch(/ng-invalid-required/);
22817 var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$parse', '$animate', '$timeout', '$rootScope', '$q', '$interpolate',
22818 function($scope, $exceptionHandler, $attr, $element, $parse, $animate, $timeout, $rootScope, $q, $interpolate) {
22819 this.$viewValue = Number.NaN;
22820 this.$modelValue = Number.NaN;
22821 this.$$rawModelValue = undefined; // stores the parsed modelValue / model set from scope regardless of validity.
22822 this.$validators = {};
22823 this.$asyncValidators = {};
22824 this.$parsers = [];
22825 this.$formatters = [];
22826 this.$viewChangeListeners = [];
22827 this.$untouched = true;
22828 this.$touched = false;
22829 this.$pristine = true;
22830 this.$dirty = false;
22831 this.$valid = true;
22832 this.$invalid = false;
22833 this.$error = {}; // keep invalid keys here
22834 this.$$success = {}; // keep valid keys here
22835 this.$pending = undefined; // keep pending keys here
22836 this.$name = $interpolate($attr.name || '', false)($scope);
22839 var parsedNgModel = $parse($attr.ngModel),
22840 parsedNgModelAssign = parsedNgModel.assign,
22841 ngModelGet = parsedNgModel,
22842 ngModelSet = parsedNgModelAssign,
22843 pendingDebounce = null,
22847 this.$$setOptions = function(options) {
22848 ctrl.$options = options;
22849 if (options && options.getterSetter) {
22850 var invokeModelGetter = $parse($attr.ngModel + '()'),
22851 invokeModelSetter = $parse($attr.ngModel + '($$$p)');
22853 ngModelGet = function($scope) {
22854 var modelValue = parsedNgModel($scope);
22855 if (isFunction(modelValue)) {
22856 modelValue = invokeModelGetter($scope);
22860 ngModelSet = function($scope, newValue) {
22861 if (isFunction(parsedNgModel($scope))) {
22862 invokeModelSetter($scope, {$$$p: ctrl.$modelValue});
22864 parsedNgModelAssign($scope, ctrl.$modelValue);
22867 } else if (!parsedNgModel.assign) {
22868 throw $ngModelMinErr('nonassign', "Expression '{0}' is non-assignable. Element: {1}",
22869 $attr.ngModel, startingTag($element));
22875 * @name ngModel.NgModelController#$render
22878 * Called when the view needs to be updated. It is expected that the user of the ng-model
22879 * directive will implement this method.
22881 * The `$render()` method is invoked in the following situations:
22883 * * `$rollbackViewValue()` is called. If we are rolling back the view value to the last
22884 * committed value then `$render()` is called to update the input control.
22885 * * The value referenced by `ng-model` is changed programmatically and both the `$modelValue` and
22886 * the `$viewValue` are different to last time.
22888 * Since `ng-model` does not do a deep watch, `$render()` is only invoked if the values of
22889 * `$modelValue` and `$viewValue` are actually different to their previous value. If `$modelValue`
22890 * or `$viewValue` are objects (rather than a string or number) then `$render()` will not be
22891 * invoked if you only change a property on the objects.
22893 this.$render = noop;
22897 * @name ngModel.NgModelController#$isEmpty
22900 * This is called when we need to determine if the value of an input is empty.
22902 * For instance, the required directive does this to work out if the input has data or not.
22904 * The default `$isEmpty` function checks whether the value is `undefined`, `''`, `null` or `NaN`.
22906 * You can override this for input directives whose concept of being empty is different to the
22907 * default. The `checkboxInputType` directive does this because in its case a value of `false`
22910 * @param {*} value The value of the input to check for emptiness.
22911 * @returns {boolean} True if `value` is "empty".
22913 this.$isEmpty = function(value) {
22914 return isUndefined(value) || value === '' || value === null || value !== value;
22917 var parentForm = $element.inheritedData('$formController') || nullFormCtrl,
22918 currentValidationRunId = 0;
22922 * @name ngModel.NgModelController#$setValidity
22925 * Change the validity state, and notify the form.
22927 * This method can be called within $parsers/$formatters or a custom validation implementation.
22928 * However, in most cases it should be sufficient to use the `ngModel.$validators` and
22929 * `ngModel.$asyncValidators` collections which will call `$setValidity` automatically.
22931 * @param {string} validationErrorKey Name of the validator. The `validationErrorKey` will be assigned
22932 * to either `$error[validationErrorKey]` or `$pending[validationErrorKey]`
22933 * (for unfulfilled `$asyncValidators`), so that it is available for data-binding.
22934 * The `validationErrorKey` should be in camelCase and will get converted into dash-case
22935 * for class name. Example: `myError` will result in `ng-valid-my-error` and `ng-invalid-my-error`
22936 * class and can be bound to as `{{someForm.someControl.$error.myError}}` .
22937 * @param {boolean} isValid Whether the current state is valid (true), invalid (false), pending (undefined),
22938 * or skipped (null). Pending is used for unfulfilled `$asyncValidators`.
22939 * Skipped is used by Angular when validators do not run because of parse errors and
22940 * when `$asyncValidators` do not run because any of the `$validators` failed.
22942 addSetValidityMethod({
22944 $element: $element,
22945 set: function(object, property) {
22946 object[property] = true;
22948 unset: function(object, property) {
22949 delete object[property];
22951 parentForm: parentForm,
22957 * @name ngModel.NgModelController#$setPristine
22960 * Sets the control to its pristine state.
22962 * This method can be called to remove the `ng-dirty` class and set the control to its pristine
22963 * state (`ng-pristine` class). A model is considered to be pristine when the control
22964 * has not been changed from when first compiled.
22966 this.$setPristine = function() {
22967 ctrl.$dirty = false;
22968 ctrl.$pristine = true;
22969 $animate.removeClass($element, DIRTY_CLASS);
22970 $animate.addClass($element, PRISTINE_CLASS);
22975 * @name ngModel.NgModelController#$setDirty
22978 * Sets the control to its dirty state.
22980 * This method can be called to remove the `ng-pristine` class and set the control to its dirty
22981 * state (`ng-dirty` class). A model is considered to be dirty when the control has been changed
22982 * from when first compiled.
22984 this.$setDirty = function() {
22985 ctrl.$dirty = true;
22986 ctrl.$pristine = false;
22987 $animate.removeClass($element, PRISTINE_CLASS);
22988 $animate.addClass($element, DIRTY_CLASS);
22989 parentForm.$setDirty();
22994 * @name ngModel.NgModelController#$setUntouched
22997 * Sets the control to its untouched state.
22999 * This method can be called to remove the `ng-touched` class and set the control to its
23000 * untouched state (`ng-untouched` class). Upon compilation, a model is set as untouched
23001 * by default, however this function can be used to restore that state if the model has
23002 * already been touched by the user.
23004 this.$setUntouched = function() {
23005 ctrl.$touched = false;
23006 ctrl.$untouched = true;
23007 $animate.setClass($element, UNTOUCHED_CLASS, TOUCHED_CLASS);
23012 * @name ngModel.NgModelController#$setTouched
23015 * Sets the control to its touched state.
23017 * This method can be called to remove the `ng-untouched` class and set the control to its
23018 * touched state (`ng-touched` class). A model is considered to be touched when the user has
23019 * first focused the control element and then shifted focus away from the control (blur event).
23021 this.$setTouched = function() {
23022 ctrl.$touched = true;
23023 ctrl.$untouched = false;
23024 $animate.setClass($element, TOUCHED_CLASS, UNTOUCHED_CLASS);
23029 * @name ngModel.NgModelController#$rollbackViewValue
23032 * Cancel an update and reset the input element's value to prevent an update to the `$modelValue`,
23033 * which may be caused by a pending debounced event or because the input is waiting for a some
23036 * If you have an input that uses `ng-model-options` to set up debounced events or events such
23037 * as blur you can have a situation where there is a period when the `$viewValue`
23038 * is out of synch with the ngModel's `$modelValue`.
23040 * In this case, you can run into difficulties if you try to update the ngModel's `$modelValue`
23041 * programmatically before these debounced/future events have resolved/occurred, because Angular's
23042 * dirty checking mechanism is not able to tell whether the model has actually changed or not.
23044 * The `$rollbackViewValue()` method should be called before programmatically changing the model of an
23045 * input which may have such events pending. This is important in order to make sure that the
23046 * input field will be updated with the new model value and any pending operations are cancelled.
23048 * <example name="ng-model-cancel-update" module="cancel-update-example">
23049 * <file name="app.js">
23050 * angular.module('cancel-update-example', [])
23052 * .controller('CancelUpdateController', ['$scope', function($scope) {
23053 * $scope.resetWithCancel = function(e) {
23054 * if (e.keyCode == 27) {
23055 * $scope.myForm.myInput1.$rollbackViewValue();
23056 * $scope.myValue = '';
23059 * $scope.resetWithoutCancel = function(e) {
23060 * if (e.keyCode == 27) {
23061 * $scope.myValue = '';
23066 * <file name="index.html">
23067 * <div ng-controller="CancelUpdateController">
23068 * <p>Try typing something in each input. See that the model only updates when you
23069 * blur off the input.
23071 * <p>Now see what happens if you start typing then press the Escape key</p>
23073 * <form name="myForm" ng-model-options="{ updateOn: 'blur' }">
23074 * <p>With $rollbackViewValue()</p>
23075 * <input name="myInput1" ng-model="myValue" ng-keydown="resetWithCancel($event)"><br/>
23076 * myValue: "{{ myValue }}"
23078 * <p>Without $rollbackViewValue()</p>
23079 * <input name="myInput2" ng-model="myValue" ng-keydown="resetWithoutCancel($event)"><br/>
23080 * myValue: "{{ myValue }}"
23086 this.$rollbackViewValue = function() {
23087 $timeout.cancel(pendingDebounce);
23088 ctrl.$viewValue = ctrl.$$lastCommittedViewValue;
23094 * @name ngModel.NgModelController#$validate
23097 * Runs each of the registered validators (first synchronous validators and then
23098 * asynchronous validators).
23099 * If the validity changes to invalid, the model will be set to `undefined`,
23100 * unless {@link ngModelOptions `ngModelOptions.allowInvalid`} is `true`.
23101 * If the validity changes to valid, it will set the model to the last available valid
23102 * modelValue, i.e. either the last parsed value or the last value set from the scope.
23104 this.$validate = function() {
23105 // ignore $validate before model is initialized
23106 if (isNumber(ctrl.$modelValue) && isNaN(ctrl.$modelValue)) {
23110 var viewValue = ctrl.$$lastCommittedViewValue;
23111 // Note: we use the $$rawModelValue as $modelValue might have been
23112 // set to undefined during a view -> model update that found validation
23113 // errors. We can't parse the view here, since that could change
23114 // the model although neither viewValue nor the model on the scope changed
23115 var modelValue = ctrl.$$rawModelValue;
23117 var prevValid = ctrl.$valid;
23118 var prevModelValue = ctrl.$modelValue;
23120 var allowInvalid = ctrl.$options && ctrl.$options.allowInvalid;
23122 ctrl.$$runValidators(modelValue, viewValue, function(allValid) {
23123 // If there was no change in validity, don't update the model
23124 // This prevents changing an invalid modelValue to undefined
23125 if (!allowInvalid && prevValid !== allValid) {
23126 // Note: Don't check ctrl.$valid here, as we could have
23127 // external validators (e.g. calculated on the server),
23128 // that just call $setValidity and need the model value
23129 // to calculate their validity.
23130 ctrl.$modelValue = allValid ? modelValue : undefined;
23132 if (ctrl.$modelValue !== prevModelValue) {
23133 ctrl.$$writeModelToScope();
23140 this.$$runValidators = function(modelValue, viewValue, doneCallback) {
23141 currentValidationRunId++;
23142 var localValidationRunId = currentValidationRunId;
23144 // check parser error
23145 if (!processParseErrors()) {
23146 validationDone(false);
23149 if (!processSyncValidators()) {
23150 validationDone(false);
23153 processAsyncValidators();
23155 function processParseErrors() {
23156 var errorKey = ctrl.$$parserName || 'parse';
23157 if (parserValid === undefined) {
23158 setValidity(errorKey, null);
23160 if (!parserValid) {
23161 forEach(ctrl.$validators, function(v, name) {
23162 setValidity(name, null);
23164 forEach(ctrl.$asyncValidators, function(v, name) {
23165 setValidity(name, null);
23168 // Set the parse error last, to prevent unsetting it, should a $validators key == parserName
23169 setValidity(errorKey, parserValid);
23170 return parserValid;
23175 function processSyncValidators() {
23176 var syncValidatorsValid = true;
23177 forEach(ctrl.$validators, function(validator, name) {
23178 var result = validator(modelValue, viewValue);
23179 syncValidatorsValid = syncValidatorsValid && result;
23180 setValidity(name, result);
23182 if (!syncValidatorsValid) {
23183 forEach(ctrl.$asyncValidators, function(v, name) {
23184 setValidity(name, null);
23191 function processAsyncValidators() {
23192 var validatorPromises = [];
23193 var allValid = true;
23194 forEach(ctrl.$asyncValidators, function(validator, name) {
23195 var promise = validator(modelValue, viewValue);
23196 if (!isPromiseLike(promise)) {
23197 throw $ngModelMinErr("$asyncValidators",
23198 "Expected asynchronous validator to return a promise but got '{0}' instead.", promise);
23200 setValidity(name, undefined);
23201 validatorPromises.push(promise.then(function() {
23202 setValidity(name, true);
23203 }, function(error) {
23205 setValidity(name, false);
23208 if (!validatorPromises.length) {
23209 validationDone(true);
23211 $q.all(validatorPromises).then(function() {
23212 validationDone(allValid);
23217 function setValidity(name, isValid) {
23218 if (localValidationRunId === currentValidationRunId) {
23219 ctrl.$setValidity(name, isValid);
23223 function validationDone(allValid) {
23224 if (localValidationRunId === currentValidationRunId) {
23226 doneCallback(allValid);
23233 * @name ngModel.NgModelController#$commitViewValue
23236 * Commit a pending update to the `$modelValue`.
23238 * Updates may be pending by a debounced event or because the input is waiting for a some future
23239 * event defined in `ng-model-options`. this method is rarely needed as `NgModelController`
23240 * usually handles calling this in response to input events.
23242 this.$commitViewValue = function() {
23243 var viewValue = ctrl.$viewValue;
23245 $timeout.cancel(pendingDebounce);
23247 // If the view value has not changed then we should just exit, except in the case where there is
23248 // a native validator on the element. In this case the validation state may have changed even though
23249 // the viewValue has stayed empty.
23250 if (ctrl.$$lastCommittedViewValue === viewValue && (viewValue !== '' || !ctrl.$$hasNativeValidators)) {
23253 ctrl.$$lastCommittedViewValue = viewValue;
23256 if (ctrl.$pristine) {
23259 this.$$parseAndValidate();
23262 this.$$parseAndValidate = function() {
23263 var viewValue = ctrl.$$lastCommittedViewValue;
23264 var modelValue = viewValue;
23265 parserValid = isUndefined(modelValue) ? undefined : true;
23268 for (var i = 0; i < ctrl.$parsers.length; i++) {
23269 modelValue = ctrl.$parsers[i](modelValue);
23270 if (isUndefined(modelValue)) {
23271 parserValid = false;
23276 if (isNumber(ctrl.$modelValue) && isNaN(ctrl.$modelValue)) {
23277 // ctrl.$modelValue has not been touched yet...
23278 ctrl.$modelValue = ngModelGet($scope);
23280 var prevModelValue = ctrl.$modelValue;
23281 var allowInvalid = ctrl.$options && ctrl.$options.allowInvalid;
23282 ctrl.$$rawModelValue = modelValue;
23284 if (allowInvalid) {
23285 ctrl.$modelValue = modelValue;
23286 writeToModelIfNeeded();
23289 // Pass the $$lastCommittedViewValue here, because the cached viewValue might be out of date.
23290 // This can happen if e.g. $setViewValue is called from inside a parser
23291 ctrl.$$runValidators(modelValue, ctrl.$$lastCommittedViewValue, function(allValid) {
23292 if (!allowInvalid) {
23293 // Note: Don't check ctrl.$valid here, as we could have
23294 // external validators (e.g. calculated on the server),
23295 // that just call $setValidity and need the model value
23296 // to calculate their validity.
23297 ctrl.$modelValue = allValid ? modelValue : undefined;
23298 writeToModelIfNeeded();
23302 function writeToModelIfNeeded() {
23303 if (ctrl.$modelValue !== prevModelValue) {
23304 ctrl.$$writeModelToScope();
23309 this.$$writeModelToScope = function() {
23310 ngModelSet($scope, ctrl.$modelValue);
23311 forEach(ctrl.$viewChangeListeners, function(listener) {
23315 $exceptionHandler(e);
23322 * @name ngModel.NgModelController#$setViewValue
23325 * Update the view value.
23327 * This method should be called when an input directive want to change the view value; typically,
23328 * this is done from within a DOM event handler.
23330 * For example {@link ng.directive:input input} calls it when the value of the input changes and
23331 * {@link ng.directive:select select} calls it when an option is selected.
23333 * If the new `value` is an object (rather than a string or a number), we should make a copy of the
23334 * object before passing it to `$setViewValue`. This is because `ngModel` does not perform a deep
23335 * watch of objects, it only looks for a change of identity. If you only change the property of
23336 * the object then ngModel will not realise that the object has changed and will not invoke the
23337 * `$parsers` and `$validators` pipelines.
23339 * For this reason, you should not change properties of the copy once it has been passed to
23340 * `$setViewValue`. Otherwise you may cause the model value on the scope to change incorrectly.
23342 * When this method is called, the new `value` will be staged for committing through the `$parsers`
23343 * and `$validators` pipelines. If there are no special {@link ngModelOptions} specified then the staged
23344 * value sent directly for processing, finally to be applied to `$modelValue` and then the
23345 * **expression** specified in the `ng-model` attribute.
23347 * Lastly, all the registered change listeners, in the `$viewChangeListeners` list, are called.
23349 * In case the {@link ng.directive:ngModelOptions ngModelOptions} directive is used with `updateOn`
23350 * and the `default` trigger is not listed, all those actions will remain pending until one of the
23351 * `updateOn` events is triggered on the DOM element.
23352 * All these actions will be debounced if the {@link ng.directive:ngModelOptions ngModelOptions}
23353 * directive is used with a custom debounce for this particular event.
23355 * Note that calling this function does not trigger a `$digest`.
23357 * @param {string} value Value from the view.
23358 * @param {string} trigger Event that triggered the update.
23360 this.$setViewValue = function(value, trigger) {
23361 ctrl.$viewValue = value;
23362 if (!ctrl.$options || ctrl.$options.updateOnDefault) {
23363 ctrl.$$debounceViewValueCommit(trigger);
23367 this.$$debounceViewValueCommit = function(trigger) {
23368 var debounceDelay = 0,
23369 options = ctrl.$options,
23372 if (options && isDefined(options.debounce)) {
23373 debounce = options.debounce;
23374 if (isNumber(debounce)) {
23375 debounceDelay = debounce;
23376 } else if (isNumber(debounce[trigger])) {
23377 debounceDelay = debounce[trigger];
23378 } else if (isNumber(debounce['default'])) {
23379 debounceDelay = debounce['default'];
23383 $timeout.cancel(pendingDebounce);
23384 if (debounceDelay) {
23385 pendingDebounce = $timeout(function() {
23386 ctrl.$commitViewValue();
23388 } else if ($rootScope.$$phase) {
23389 ctrl.$commitViewValue();
23391 $scope.$apply(function() {
23392 ctrl.$commitViewValue();
23398 // Note: we cannot use a normal scope.$watch as we want to detect the following:
23399 // 1. scope value is 'a'
23400 // 2. user enters 'b'
23401 // 3. ng-change kicks in and reverts scope value to 'a'
23402 // -> scope value did not change since the last digest as
23403 // ng-change executes in apply phase
23404 // 4. view should be changed back to 'a'
23405 $scope.$watch(function ngModelWatch() {
23406 var modelValue = ngModelGet($scope);
23408 // if scope model value and ngModel value are out of sync
23409 // TODO(perf): why not move this to the action fn?
23410 if (modelValue !== ctrl.$modelValue) {
23411 ctrl.$modelValue = ctrl.$$rawModelValue = modelValue;
23412 parserValid = undefined;
23414 var formatters = ctrl.$formatters,
23415 idx = formatters.length;
23417 var viewValue = modelValue;
23419 viewValue = formatters[idx](viewValue);
23421 if (ctrl.$viewValue !== viewValue) {
23422 ctrl.$viewValue = ctrl.$$lastCommittedViewValue = viewValue;
23425 ctrl.$$runValidators(modelValue, viewValue, noop);
23442 * The `ngModel` directive binds an `input`,`select`, `textarea` (or custom form control) to a
23443 * property on the scope using {@link ngModel.NgModelController NgModelController},
23444 * which is created and exposed by this directive.
23446 * `ngModel` is responsible for:
23448 * - Binding the view into the model, which other directives such as `input`, `textarea` or `select`
23450 * - Providing validation behavior (i.e. required, number, email, url).
23451 * - Keeping the state of the control (valid/invalid, dirty/pristine, touched/untouched, validation errors).
23452 * - Setting related css classes on the element (`ng-valid`, `ng-invalid`, `ng-dirty`, `ng-pristine`, `ng-touched`, `ng-untouched`) including animations.
23453 * - Registering the control with its parent {@link ng.directive:form form}.
23455 * Note: `ngModel` will try to bind to the property given by evaluating the expression on the
23456 * current scope. If the property doesn't already exist on this scope, it will be created
23457 * implicitly and added to the scope.
23459 * For best practices on using `ngModel`, see:
23461 * - [Understanding Scopes](https://github.com/angular/angular.js/wiki/Understanding-Scopes)
23463 * For basic examples, how to use `ngModel`, see:
23465 * - {@link ng.directive:input input}
23466 * - {@link input[text] text}
23467 * - {@link input[checkbox] checkbox}
23468 * - {@link input[radio] radio}
23469 * - {@link input[number] number}
23470 * - {@link input[email] email}
23471 * - {@link input[url] url}
23472 * - {@link input[date] date}
23473 * - {@link input[datetime-local] datetime-local}
23474 * - {@link input[time] time}
23475 * - {@link input[month] month}
23476 * - {@link input[week] week}
23477 * - {@link ng.directive:select select}
23478 * - {@link ng.directive:textarea textarea}
23481 * The following CSS classes are added and removed on the associated input/select/textarea element
23482 * depending on the validity of the model.
23484 * - `ng-valid`: the model is valid
23485 * - `ng-invalid`: the model is invalid
23486 * - `ng-valid-[key]`: for each valid key added by `$setValidity`
23487 * - `ng-invalid-[key]`: for each invalid key added by `$setValidity`
23488 * - `ng-pristine`: the control hasn't been interacted with yet
23489 * - `ng-dirty`: the control has been interacted with
23490 * - `ng-touched`: the control has been blurred
23491 * - `ng-untouched`: the control hasn't been blurred
23492 * - `ng-pending`: any `$asyncValidators` are unfulfilled
23494 * Keep in mind that ngAnimate can detect each of these classes when added and removed.
23496 * ## Animation Hooks
23498 * Animations within models are triggered when any of the associated CSS classes are added and removed
23499 * on the input element which is attached to the model. These classes are: `.ng-pristine`, `.ng-dirty`,
23500 * `.ng-invalid` and `.ng-valid` as well as any other validations that are performed on the model itself.
23501 * The animations that are triggered within ngModel are similar to how they work in ngClass and
23502 * animations can be hooked into using CSS transitions, keyframes as well as JS animations.
23504 * The following example shows a simple way to utilize CSS transitions to style an input element
23505 * that has been rendered as invalid after it has been validated:
23508 * //be sure to include ngAnimate as a module to hook into more
23509 * //advanced animations
23511 * transition:0.5s linear all;
23512 * background: white;
23514 * .my-input.ng-invalid {
23521 * <example deps="angular-animate.js" animations="true" fixBase="true" module="inputExample">
23522 <file name="index.html">
23524 angular.module('inputExample', [])
23525 .controller('ExampleController', ['$scope', function($scope) {
23531 -webkit-transition:all linear 0.5s;
23532 transition:all linear 0.5s;
23533 background: transparent;
23535 .my-input.ng-invalid {
23540 Update input to see transitions when valid/invalid.
23541 Integer is a valid value.
23542 <form name="testForm" ng-controller="ExampleController">
23543 <input ng-model="val" ng-pattern="/^\d+$/" name="anim" class="my-input" />
23548 * ## Binding to a getter/setter
23550 * Sometimes it's helpful to bind `ngModel` to a getter/setter function. A getter/setter is a
23551 * function that returns a representation of the model when called with zero arguments, and sets
23552 * the internal state of a model when called with an argument. It's sometimes useful to use this
23553 * for models that have an internal representation that's different than what the model exposes
23556 * <div class="alert alert-success">
23557 * **Best Practice:** It's best to keep getters fast because Angular is likely to call them more
23558 * frequently than other parts of your code.
23561 * You use this behavior by adding `ng-model-options="{ getterSetter: true }"` to an element that
23562 * has `ng-model` attached to it. You can also add `ng-model-options="{ getterSetter: true }"` to
23563 * a `<form>`, which will enable this behavior for all `<input>`s within it. See
23564 * {@link ng.directive:ngModelOptions `ngModelOptions`} for more.
23566 * The following example shows how to use `ngModel` with a getter/setter:
23569 * <example name="ngModel-getter-setter" module="getterSetterExample">
23570 <file name="index.html">
23571 <div ng-controller="ExampleController">
23572 <form name="userForm">
23574 <input type="text" name="userName"
23575 ng-model="user.name"
23576 ng-model-options="{ getterSetter: true }" />
23578 <pre>user.name = <span ng-bind="user.name()"></span></pre>
23581 <file name="app.js">
23582 angular.module('getterSetterExample', [])
23583 .controller('ExampleController', ['$scope', function($scope) {
23584 var _name = 'Brian';
23586 name: function(newName) {
23587 if (angular.isDefined(newName)) {
23597 var ngModelDirective = ['$rootScope', function($rootScope) {
23600 require: ['ngModel', '^?form', '^?ngModelOptions'],
23601 controller: NgModelController,
23602 // Prelink needs to run before any input directive
23603 // so that we can set the NgModelOptions in NgModelController
23604 // before anyone else uses it.
23606 compile: function ngModelCompile(element) {
23607 // Setup initial state of the control
23608 element.addClass(PRISTINE_CLASS).addClass(UNTOUCHED_CLASS).addClass(VALID_CLASS);
23611 pre: function ngModelPreLink(scope, element, attr, ctrls) {
23612 var modelCtrl = ctrls[0],
23613 formCtrl = ctrls[1] || nullFormCtrl;
23615 modelCtrl.$$setOptions(ctrls[2] && ctrls[2].$options);
23617 // notify others, especially parent forms
23618 formCtrl.$addControl(modelCtrl);
23620 attr.$observe('name', function(newValue) {
23621 if (modelCtrl.$name !== newValue) {
23622 formCtrl.$$renameControl(modelCtrl, newValue);
23626 scope.$on('$destroy', function() {
23627 formCtrl.$removeControl(modelCtrl);
23630 post: function ngModelPostLink(scope, element, attr, ctrls) {
23631 var modelCtrl = ctrls[0];
23632 if (modelCtrl.$options && modelCtrl.$options.updateOn) {
23633 element.on(modelCtrl.$options.updateOn, function(ev) {
23634 modelCtrl.$$debounceViewValueCommit(ev && ev.type);
23638 element.on('blur', function(ev) {
23639 if (modelCtrl.$touched) return;
23641 if ($rootScope.$$phase) {
23642 scope.$evalAsync(modelCtrl.$setTouched);
23644 scope.$apply(modelCtrl.$setTouched);
23653 var DEFAULT_REGEXP = /(\s+|^)default(\s+|$)/;
23657 * @name ngModelOptions
23660 * Allows tuning how model updates are done. Using `ngModelOptions` you can specify a custom list of
23661 * events that will trigger a model update and/or a debouncing delay so that the actual update only
23662 * takes place when a timer expires; this timer will be reset after another change takes place.
23664 * Given the nature of `ngModelOptions`, the value displayed inside input fields in the view might
23665 * be different than the value in the actual model. This means that if you update the model you
23666 * should also invoke {@link ngModel.NgModelController `$rollbackViewValue`} on the relevant input field in
23667 * order to make sure it is synchronized with the model and that any debounced action is canceled.
23669 * The easiest way to reference the control's {@link ngModel.NgModelController `$rollbackViewValue`}
23670 * method is by making sure the input is placed inside a form that has a `name` attribute. This is
23671 * important because `form` controllers are published to the related scope under the name in their
23672 * `name` attribute.
23674 * Any pending changes will take place immediately when an enclosing form is submitted via the
23675 * `submit` event. Note that `ngClick` events will occur before the model is updated. Use `ngSubmit`
23676 * to have access to the updated model.
23678 * `ngModelOptions` has an effect on the element it's declared on and its descendants.
23680 * @param {Object} ngModelOptions options to apply to the current model. Valid keys are:
23681 * - `updateOn`: string specifying which event should the input be bound to. You can set several
23682 * events using an space delimited list. There is a special event called `default` that
23683 * matches the default events belonging of the control.
23684 * - `debounce`: integer value which contains the debounce model update value in milliseconds. A
23685 * value of 0 triggers an immediate update. If an object is supplied instead, you can specify a
23686 * custom value for each event. For example:
23687 * `ng-model-options="{ updateOn: 'default blur', debounce: {'default': 500, 'blur': 0} }"`
23688 * - `allowInvalid`: boolean value which indicates that the model can be set with values that did
23689 * not validate correctly instead of the default behavior of setting the model to undefined.
23690 * - `getterSetter`: boolean value which determines whether or not to treat functions bound to
23691 `ngModel` as getters/setters.
23692 * - `timezone`: Defines the timezone to be used to read/write the `Date` instance in the model for
23693 * `<input type="date">`, `<input type="time">`, ... . Right now, the only supported value is `'UTC'`,
23694 * otherwise the default timezone of the browser will be used.
23698 The following example shows how to override immediate updates. Changes on the inputs within the
23699 form will update the model only when the control loses focus (blur event). If `escape` key is
23700 pressed while the input field is focused, the value is reset to the value in the current model.
23702 <example name="ngModelOptions-directive-blur" module="optionsExample">
23703 <file name="index.html">
23704 <div ng-controller="ExampleController">
23705 <form name="userForm">
23707 <input type="text" name="userName"
23708 ng-model="user.name"
23709 ng-model-options="{ updateOn: 'blur' }"
23710 ng-keyup="cancel($event)" /><br />
23713 <input type="text" ng-model="user.data" /><br />
23715 <pre>user.name = <span ng-bind="user.name"></span></pre>
23718 <file name="app.js">
23719 angular.module('optionsExample', [])
23720 .controller('ExampleController', ['$scope', function($scope) {
23721 $scope.user = { name: 'say', data: '' };
23723 $scope.cancel = function(e) {
23724 if (e.keyCode == 27) {
23725 $scope.userForm.userName.$rollbackViewValue();
23730 <file name="protractor.js" type="protractor">
23731 var model = element(by.binding('user.name'));
23732 var input = element(by.model('user.name'));
23733 var other = element(by.model('user.data'));
23735 it('should allow custom events', function() {
23736 input.sendKeys(' hello');
23738 expect(model.getText()).toEqual('say');
23740 expect(model.getText()).toEqual('say hello');
23743 it('should $rollbackViewValue when model changes', function() {
23744 input.sendKeys(' hello');
23745 expect(input.getAttribute('value')).toEqual('say hello');
23746 input.sendKeys(protractor.Key.ESCAPE);
23747 expect(input.getAttribute('value')).toEqual('say');
23749 expect(model.getText()).toEqual('say');
23754 This one shows how to debounce model changes. Model will be updated only 1 sec after last change.
23755 If the `Clear` button is pressed, any debounced action is canceled and the value becomes empty.
23757 <example name="ngModelOptions-directive-debounce" module="optionsExample">
23758 <file name="index.html">
23759 <div ng-controller="ExampleController">
23760 <form name="userForm">
23762 <input type="text" name="userName"
23763 ng-model="user.name"
23764 ng-model-options="{ debounce: 1000 }" />
23765 <button ng-click="userForm.userName.$rollbackViewValue(); user.name=''">Clear</button><br />
23767 <pre>user.name = <span ng-bind="user.name"></span></pre>
23770 <file name="app.js">
23771 angular.module('optionsExample', [])
23772 .controller('ExampleController', ['$scope', function($scope) {
23773 $scope.user = { name: 'say' };
23778 This one shows how to bind to getter/setters:
23780 <example name="ngModelOptions-directive-getter-setter" module="getterSetterExample">
23781 <file name="index.html">
23782 <div ng-controller="ExampleController">
23783 <form name="userForm">
23785 <input type="text" name="userName"
23786 ng-model="user.name"
23787 ng-model-options="{ getterSetter: true }" />
23789 <pre>user.name = <span ng-bind="user.name()"></span></pre>
23792 <file name="app.js">
23793 angular.module('getterSetterExample', [])
23794 .controller('ExampleController', ['$scope', function($scope) {
23795 var _name = 'Brian';
23797 name: function(newName) {
23798 return angular.isDefined(newName) ? (_name = newName) : _name;
23805 var ngModelOptionsDirective = function() {
23808 controller: ['$scope', '$attrs', function($scope, $attrs) {
23810 this.$options = $scope.$eval($attrs.ngModelOptions);
23811 // Allow adding/overriding bound events
23812 if (this.$options.updateOn !== undefined) {
23813 this.$options.updateOnDefault = false;
23814 // extract "default" pseudo-event from list of events that can trigger a model update
23815 this.$options.updateOn = trim(this.$options.updateOn.replace(DEFAULT_REGEXP, function() {
23816 that.$options.updateOnDefault = true;
23820 this.$options.updateOnDefault = true;
23829 function addSetValidityMethod(context) {
23830 var ctrl = context.ctrl,
23831 $element = context.$element,
23834 unset = context.unset,
23835 parentForm = context.parentForm,
23836 $animate = context.$animate;
23838 classCache[INVALID_CLASS] = !(classCache[VALID_CLASS] = $element.hasClass(VALID_CLASS));
23840 ctrl.$setValidity = setValidity;
23842 function setValidity(validationErrorKey, state, controller) {
23843 if (state === undefined) {
23844 createAndSet('$pending', validationErrorKey, controller);
23846 unsetAndCleanup('$pending', validationErrorKey, controller);
23848 if (!isBoolean(state)) {
23849 unset(ctrl.$error, validationErrorKey, controller);
23850 unset(ctrl.$$success, validationErrorKey, controller);
23853 unset(ctrl.$error, validationErrorKey, controller);
23854 set(ctrl.$$success, validationErrorKey, controller);
23856 set(ctrl.$error, validationErrorKey, controller);
23857 unset(ctrl.$$success, validationErrorKey, controller);
23860 if (ctrl.$pending) {
23861 cachedToggleClass(PENDING_CLASS, true);
23862 ctrl.$valid = ctrl.$invalid = undefined;
23863 toggleValidationCss('', null);
23865 cachedToggleClass(PENDING_CLASS, false);
23866 ctrl.$valid = isObjectEmpty(ctrl.$error);
23867 ctrl.$invalid = !ctrl.$valid;
23868 toggleValidationCss('', ctrl.$valid);
23871 // re-read the state as the set/unset methods could have
23872 // combined state in ctrl.$error[validationError] (used for forms),
23873 // where setting/unsetting only increments/decrements the value,
23874 // and does not replace it.
23876 if (ctrl.$pending && ctrl.$pending[validationErrorKey]) {
23877 combinedState = undefined;
23878 } else if (ctrl.$error[validationErrorKey]) {
23879 combinedState = false;
23880 } else if (ctrl.$$success[validationErrorKey]) {
23881 combinedState = true;
23883 combinedState = null;
23886 toggleValidationCss(validationErrorKey, combinedState);
23887 parentForm.$setValidity(validationErrorKey, combinedState, ctrl);
23890 function createAndSet(name, value, controller) {
23894 set(ctrl[name], value, controller);
23897 function unsetAndCleanup(name, value, controller) {
23899 unset(ctrl[name], value, controller);
23901 if (isObjectEmpty(ctrl[name])) {
23902 ctrl[name] = undefined;
23906 function cachedToggleClass(className, switchValue) {
23907 if (switchValue && !classCache[className]) {
23908 $animate.addClass($element, className);
23909 classCache[className] = true;
23910 } else if (!switchValue && classCache[className]) {
23911 $animate.removeClass($element, className);
23912 classCache[className] = false;
23916 function toggleValidationCss(validationErrorKey, isValid) {
23917 validationErrorKey = validationErrorKey ? '-' + snake_case(validationErrorKey, '-') : '';
23919 cachedToggleClass(VALID_CLASS + validationErrorKey, isValid === true);
23920 cachedToggleClass(INVALID_CLASS + validationErrorKey, isValid === false);
23924 function isObjectEmpty(obj) {
23926 for (var prop in obj) {
23935 * @name ngNonBindable
23940 * The `ngNonBindable` directive tells Angular not to compile or bind the contents of the current
23941 * DOM element. This is useful if the element contains what appears to be Angular directives and
23942 * bindings but which should be ignored by Angular. This could be the case if you have a site that
23943 * displays snippets of code, for instance.
23948 * In this example there are two locations where a simple interpolation binding (`{{}}`) is present,
23949 * but the one wrapped in `ngNonBindable` is left alone.
23953 <file name="index.html">
23954 <div>Normal: {{1 + 2}}</div>
23955 <div ng-non-bindable>Ignored: {{1 + 2}}</div>
23957 <file name="protractor.js" type="protractor">
23958 it('should check ng-non-bindable', function() {
23959 expect(element(by.binding('1 + 2')).getText()).toContain('3');
23960 expect(element.all(by.css('div')).last().getText()).toMatch(/1 \+ 2/);
23965 var ngNonBindableDirective = ngDirective({ terminal: true, priority: 1000 });
23969 * @name ngPluralize
23973 * `ngPluralize` is a directive that displays messages according to en-US localization rules.
23974 * These rules are bundled with angular.js, but can be overridden
23975 * (see {@link guide/i18n Angular i18n} dev guide). You configure ngPluralize directive
23976 * by specifying the mappings between
23977 * [plural categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html)
23978 * and the strings to be displayed.
23980 * # Plural categories and explicit number rules
23982 * [plural categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html)
23983 * in Angular's default en-US locale: "one" and "other".
23985 * While a plural category may match many numbers (for example, in en-US locale, "other" can match
23986 * any number that is not 1), an explicit number rule can only match one number. For example, the
23987 * explicit number rule for "3" matches the number 3. There are examples of plural categories
23988 * and explicit number rules throughout the rest of this documentation.
23990 * # Configuring ngPluralize
23991 * You configure ngPluralize by providing 2 attributes: `count` and `when`.
23992 * You can also provide an optional attribute, `offset`.
23994 * The value of the `count` attribute can be either a string or an {@link guide/expression
23995 * Angular expression}; these are evaluated on the current scope for its bound value.
23997 * The `when` attribute specifies the mappings between plural categories and the actual
23998 * string to be displayed. The value of the attribute should be a JSON object.
24000 * The following example shows how to configure ngPluralize:
24003 * <ng-pluralize count="personCount"
24004 when="{'0': 'Nobody is viewing.',
24005 * 'one': '1 person is viewing.',
24006 * 'other': '{} people are viewing.'}">
24010 * In the example, `"0: Nobody is viewing."` is an explicit number rule. If you did not
24011 * specify this rule, 0 would be matched to the "other" category and "0 people are viewing"
24012 * would be shown instead of "Nobody is viewing". You can specify an explicit number rule for
24013 * other numbers, for example 12, so that instead of showing "12 people are viewing", you can
24014 * show "a dozen people are viewing".
24016 * You can use a set of closed braces (`{}`) as a placeholder for the number that you want substituted
24017 * into pluralized strings. In the previous example, Angular will replace `{}` with
24018 * <span ng-non-bindable>`{{personCount}}`</span>. The closed braces `{}` is a placeholder
24019 * for <span ng-non-bindable>{{numberExpression}}</span>.
24021 * # Configuring ngPluralize with offset
24022 * The `offset` attribute allows further customization of pluralized text, which can result in
24023 * a better user experience. For example, instead of the message "4 people are viewing this document",
24024 * you might display "John, Kate and 2 others are viewing this document".
24025 * The offset attribute allows you to offset a number by any desired value.
24026 * Let's take a look at an example:
24029 * <ng-pluralize count="personCount" offset=2
24030 * when="{'0': 'Nobody is viewing.',
24031 * '1': '{{person1}} is viewing.',
24032 * '2': '{{person1}} and {{person2}} are viewing.',
24033 * 'one': '{{person1}}, {{person2}} and one other person are viewing.',
24034 * 'other': '{{person1}}, {{person2}} and {} other people are viewing.'}">
24038 * Notice that we are still using two plural categories(one, other), but we added
24039 * three explicit number rules 0, 1 and 2.
24040 * When one person, perhaps John, views the document, "John is viewing" will be shown.
24041 * When three people view the document, no explicit number rule is found, so
24042 * an offset of 2 is taken off 3, and Angular uses 1 to decide the plural category.
24043 * In this case, plural category 'one' is matched and "John, Mary and one other person are viewing"
24046 * Note that when you specify offsets, you must provide explicit number rules for
24047 * numbers from 0 up to and including the offset. If you use an offset of 3, for example,
24048 * you must provide explicit number rules for 0, 1, 2 and 3. You must also provide plural strings for
24049 * plural categories "one" and "other".
24051 * @param {string|expression} count The variable to be bound to.
24052 * @param {string} when The mapping between plural category to its corresponding strings.
24053 * @param {number=} offset Offset to deduct from the total number.
24056 <example module="pluralizeExample">
24057 <file name="index.html">
24059 angular.module('pluralizeExample', [])
24060 .controller('ExampleController', ['$scope', function($scope) {
24061 $scope.person1 = 'Igor';
24062 $scope.person2 = 'Misko';
24063 $scope.personCount = 1;
24066 <div ng-controller="ExampleController">
24067 Person 1:<input type="text" ng-model="person1" value="Igor" /><br/>
24068 Person 2:<input type="text" ng-model="person2" value="Misko" /><br/>
24069 Number of People:<input type="text" ng-model="personCount" value="1" /><br/>
24071 <!--- Example with simple pluralization rules for en locale --->
24073 <ng-pluralize count="personCount"
24074 when="{'0': 'Nobody is viewing.',
24075 'one': '1 person is viewing.',
24076 'other': '{} people are viewing.'}">
24077 </ng-pluralize><br>
24079 <!--- Example with offset --->
24081 <ng-pluralize count="personCount" offset=2
24082 when="{'0': 'Nobody is viewing.',
24083 '1': '{{person1}} is viewing.',
24084 '2': '{{person1}} and {{person2}} are viewing.',
24085 'one': '{{person1}}, {{person2}} and one other person are viewing.',
24086 'other': '{{person1}}, {{person2}} and {} other people are viewing.'}">
24090 <file name="protractor.js" type="protractor">
24091 it('should show correct pluralized string', function() {
24092 var withoutOffset = element.all(by.css('ng-pluralize')).get(0);
24093 var withOffset = element.all(by.css('ng-pluralize')).get(1);
24094 var countInput = element(by.model('personCount'));
24096 expect(withoutOffset.getText()).toEqual('1 person is viewing.');
24097 expect(withOffset.getText()).toEqual('Igor is viewing.');
24099 countInput.clear();
24100 countInput.sendKeys('0');
24102 expect(withoutOffset.getText()).toEqual('Nobody is viewing.');
24103 expect(withOffset.getText()).toEqual('Nobody is viewing.');
24105 countInput.clear();
24106 countInput.sendKeys('2');
24108 expect(withoutOffset.getText()).toEqual('2 people are viewing.');
24109 expect(withOffset.getText()).toEqual('Igor and Misko are viewing.');
24111 countInput.clear();
24112 countInput.sendKeys('3');
24114 expect(withoutOffset.getText()).toEqual('3 people are viewing.');
24115 expect(withOffset.getText()).toEqual('Igor, Misko and one other person are viewing.');
24117 countInput.clear();
24118 countInput.sendKeys('4');
24120 expect(withoutOffset.getText()).toEqual('4 people are viewing.');
24121 expect(withOffset.getText()).toEqual('Igor, Misko and 2 other people are viewing.');
24123 it('should show data-bound names', function() {
24124 var withOffset = element.all(by.css('ng-pluralize')).get(1);
24125 var personCount = element(by.model('personCount'));
24126 var person1 = element(by.model('person1'));
24127 var person2 = element(by.model('person2'));
24128 personCount.clear();
24129 personCount.sendKeys('4');
24131 person1.sendKeys('Di');
24133 person2.sendKeys('Vojta');
24134 expect(withOffset.getText()).toEqual('Di, Vojta and 2 other people are viewing.');
24139 var ngPluralizeDirective = ['$locale', '$interpolate', function($locale, $interpolate) {
24141 IS_WHEN = /^when(Minus)?(.+)$/;
24145 link: function(scope, element, attr) {
24146 var numberExp = attr.count,
24147 whenExp = attr.$attr.when && element.attr(attr.$attr.when), // we have {{}} in attrs
24148 offset = attr.offset || 0,
24149 whens = scope.$eval(whenExp) || {},
24151 startSymbol = $interpolate.startSymbol(),
24152 endSymbol = $interpolate.endSymbol(),
24153 braceReplacement = startSymbol + numberExp + '-' + offset + endSymbol,
24154 watchRemover = angular.noop,
24157 forEach(attr, function(expression, attributeName) {
24158 var tmpMatch = IS_WHEN.exec(attributeName);
24160 var whenKey = (tmpMatch[1] ? '-' : '') + lowercase(tmpMatch[2]);
24161 whens[whenKey] = element.attr(attr.$attr[attributeName]);
24164 forEach(whens, function(expression, key) {
24165 whensExpFns[key] = $interpolate(expression.replace(BRACE, braceReplacement));
24169 scope.$watch(numberExp, function ngPluralizeWatchAction(newVal) {
24170 var count = parseFloat(newVal);
24171 var countIsNaN = isNaN(count);
24173 if (!countIsNaN && !(count in whens)) {
24174 // If an explicit number rule such as 1, 2, 3... is defined, just use it.
24175 // Otherwise, check it against pluralization rules in $locale service.
24176 count = $locale.pluralCat(count - offset);
24179 // If both `count` and `lastCount` are NaN, we don't need to re-register a watch.
24180 // In JS `NaN !== NaN`, so we have to exlicitly check.
24181 if ((count !== lastCount) && !(countIsNaN && isNaN(lastCount))) {
24183 watchRemover = scope.$watch(whensExpFns[count], updateElementText);
24188 function updateElementText(newText) {
24189 element.text(newText || '');
24200 * The `ngRepeat` directive instantiates a template once per item from a collection. Each template
24201 * instance gets its own scope, where the given loop variable is set to the current collection item,
24202 * and `$index` is set to the item index or key.
24204 * Special properties are exposed on the local scope of each template instance, including:
24206 * | Variable | Type | Details |
24207 * |-----------|-----------------|-----------------------------------------------------------------------------|
24208 * | `$index` | {@type number} | iterator offset of the repeated element (0..length-1) |
24209 * | `$first` | {@type boolean} | true if the repeated element is first in the iterator. |
24210 * | `$middle` | {@type boolean} | true if the repeated element is between the first and last in the iterator. |
24211 * | `$last` | {@type boolean} | true if the repeated element is last in the iterator. |
24212 * | `$even` | {@type boolean} | true if the iterator position `$index` is even (otherwise false). |
24213 * | `$odd` | {@type boolean} | true if the iterator position `$index` is odd (otherwise false). |
24215 * Creating aliases for these properties is possible with {@link ng.directive:ngInit `ngInit`}.
24216 * This may be useful when, for instance, nesting ngRepeats.
24218 * # Iterating over object properties
24220 * It is possible to get `ngRepeat` to iterate over the properties of an object using the following
24224 * <div ng-repeat="(key, value) in myObj"> ... </div>
24227 * You need to be aware that the JavaScript specification does not define what order
24228 * it will return the keys for an object. In order to have a guaranteed deterministic order
24229 * for the keys, Angular versions up to and including 1.3 **sort the keys alphabetically**.
24231 * If this is not desired, the recommended workaround is to convert your object into an array
24232 * that is sorted into the order that you prefer before providing it to `ngRepeat`. You could
24233 * do this with a filter such as [toArrayFilter](http://ngmodules.org/modules/angular-toArrayFilter)
24234 * or implement a `$watch` on the object yourself.
24236 * In version 1.4 we will remove the sorting, since it seems that browsers generally follow the
24237 * strategy of providing keys in the order in which they were defined, although there are exceptions
24238 * when keys are deleted and reinstated.
24241 * # Tracking and Duplicates
24243 * When the contents of the collection change, `ngRepeat` makes the corresponding changes to the DOM:
24245 * * When an item is added, a new instance of the template is added to the DOM.
24246 * * When an item is removed, its template instance is removed from the DOM.
24247 * * When items are reordered, their respective templates are reordered in the DOM.
24249 * By default, `ngRepeat` does not allow duplicate items in arrays. This is because when
24250 * there are duplicates, it is not possible to maintain a one-to-one mapping between collection
24251 * items and DOM elements.
24253 * If you do need to repeat duplicate items, you can substitute the default tracking behavior
24254 * with your own using the `track by` expression.
24256 * For example, you may track items by the index of each item in the collection, using the
24257 * special scope property `$index`:
24259 * <div ng-repeat="n in [42, 42, 43, 43] track by $index">
24264 * You may use arbitrary expressions in `track by`, including references to custom functions
24267 * <div ng-repeat="n in [42, 42, 43, 43] track by myTrackingFunction(n)">
24272 * If you are working with objects that have an identifier property, you can track
24273 * by the identifier instead of the whole object. Should you reload your data later, `ngRepeat`
24274 * will not have to rebuild the DOM elements for items it has already rendered, even if the
24275 * JavaScript objects in the collection have been substituted for new ones:
24277 * <div ng-repeat="model in collection track by model.id">
24282 * When no `track by` expression is provided, it is equivalent to tracking by the built-in
24283 * `$id` function, which tracks items by their identity:
24285 * <div ng-repeat="obj in collection track by $id(obj)">
24290 * # Special repeat start and end points
24291 * To repeat a series of elements instead of just one parent element, ngRepeat (as well as other ng directives) supports extending
24292 * the range of the repeater by defining explicit start and end points by using **ng-repeat-start** and **ng-repeat-end** respectively.
24293 * 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)
24294 * up to and including the ending HTML tag where **ng-repeat-end** is placed.
24296 * The example below makes use of this feature:
24298 * <header ng-repeat-start="item in items">
24299 * Header {{ item }}
24301 * <div class="body">
24304 * <footer ng-repeat-end>
24305 * Footer {{ item }}
24309 * And with an input of {@type ['A','B']} for the items variable in the example above, the output will evaluate to:
24314 * <div class="body">
24323 * <div class="body">
24331 * The custom start and end points for ngRepeat also support all other HTML directive syntax flavors provided in AngularJS (such
24332 * as **data-ng-repeat-start**, **x-ng-repeat-start** and **ng:repeat-start**).
24335 * **.enter** - when a new item is added to the list or when an item is revealed after a filter
24337 * **.leave** - when an item is removed from the list or when an item is filtered out
24339 * **.move** - when an adjacent item is filtered out causing a reorder or when the item contents are reordered
24344 * @param {repeat_expression} ngRepeat The expression indicating how to enumerate a collection. These
24345 * formats are currently supported:
24347 * * `variable in expression` – where variable is the user defined loop variable and `expression`
24348 * is a scope expression giving the collection to enumerate.
24350 * For example: `album in artist.albums`.
24352 * * `(key, value) in expression` – where `key` and `value` can be any user defined identifiers,
24353 * and `expression` is the scope expression giving the collection to enumerate.
24355 * For example: `(name, age) in {'adam':10, 'amalie':12}`.
24357 * * `variable in expression track by tracking_expression` – You can also provide an optional tracking expression
24358 * which can be used to associate the objects in the collection with the DOM elements. If no tracking expression
24359 * is specified, ng-repeat associates elements by identity. It is an error to have
24360 * more than one tracking expression value resolve to the same key. (This would mean that two distinct objects are
24361 * mapped to the same DOM element, which is not possible.) If filters are used in the expression, they should be
24362 * applied before the tracking expression.
24364 * For example: `item in items` is equivalent to `item in items track by $id(item)`. This implies that the DOM elements
24365 * will be associated by item identity in the array.
24367 * For example: `item in items track by $id(item)`. A built in `$id()` function can be used to assign a unique
24368 * `$$hashKey` property to each item in the array. This property is then used as a key to associated DOM elements
24369 * with the corresponding item in the array by identity. Moving the same object in array would move the DOM
24370 * element in the same way in the DOM.
24372 * For example: `item in items track by item.id` is a typical pattern when the items come from the database. In this
24373 * case the object identity does not matter. Two objects are considered equivalent as long as their `id`
24374 * property is same.
24376 * For example: `item in items | filter:searchText track by item.id` is a pattern that might be used to apply a filter
24377 * to items in conjunction with a tracking expression.
24379 * * `variable in expression as alias_expression` – You can also provide an optional alias expression which will then store the
24380 * intermediate results of the repeater after the filters have been applied. Typically this is used to render a special message
24381 * when a filter is active on the repeater, but the filtered result set is empty.
24383 * For example: `item in items | filter:x as results` will store the fragment of the repeated items as `results`, but only after
24384 * the items have been processed through the filter.
24387 * This example initializes the scope to a list of names and
24388 * then uses `ngRepeat` to display every person:
24389 <example module="ngAnimate" deps="angular-animate.js" animations="true">
24390 <file name="index.html">
24391 <div ng-init="friends = [
24392 {name:'John', age:25, gender:'boy'},
24393 {name:'Jessie', age:30, gender:'girl'},
24394 {name:'Johanna', age:28, gender:'girl'},
24395 {name:'Joy', age:15, gender:'girl'},
24396 {name:'Mary', age:28, gender:'girl'},
24397 {name:'Peter', age:95, gender:'boy'},
24398 {name:'Sebastian', age:50, gender:'boy'},
24399 {name:'Erika', age:27, gender:'girl'},
24400 {name:'Patrick', age:40, gender:'boy'},
24401 {name:'Samantha', age:60, gender:'girl'}
24403 I have {{friends.length}} friends. They are:
24404 <input type="search" ng-model="q" placeholder="filter friends..." />
24405 <ul class="example-animate-container">
24406 <li class="animate-repeat" ng-repeat="friend in friends | filter:q as results">
24407 [{{$index + 1}}] {{friend.name}} who is {{friend.age}} years old.
24409 <li class="animate-repeat" ng-if="results.length == 0">
24410 <strong>No results found...</strong>
24415 <file name="animations.css">
24416 .example-animate-container {
24418 border:1px solid black;
24427 box-sizing:border-box;
24430 .animate-repeat.ng-move,
24431 .animate-repeat.ng-enter,
24432 .animate-repeat.ng-leave {
24433 -webkit-transition:all linear 0.5s;
24434 transition:all linear 0.5s;
24437 .animate-repeat.ng-leave.ng-leave-active,
24438 .animate-repeat.ng-move,
24439 .animate-repeat.ng-enter {
24444 .animate-repeat.ng-leave,
24445 .animate-repeat.ng-move.ng-move-active,
24446 .animate-repeat.ng-enter.ng-enter-active {
24451 <file name="protractor.js" type="protractor">
24452 var friends = element.all(by.repeater('friend in friends'));
24454 it('should render initial data set', function() {
24455 expect(friends.count()).toBe(10);
24456 expect(friends.get(0).getText()).toEqual('[1] John who is 25 years old.');
24457 expect(friends.get(1).getText()).toEqual('[2] Jessie who is 30 years old.');
24458 expect(friends.last().getText()).toEqual('[10] Samantha who is 60 years old.');
24459 expect(element(by.binding('friends.length')).getText())
24460 .toMatch("I have 10 friends. They are:");
24463 it('should update repeater when filter predicate changes', function() {
24464 expect(friends.count()).toBe(10);
24466 element(by.model('q')).sendKeys('ma');
24468 expect(friends.count()).toBe(2);
24469 expect(friends.get(0).getText()).toEqual('[1] Mary who is 28 years old.');
24470 expect(friends.last().getText()).toEqual('[2] Samantha who is 60 years old.');
24475 var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
24476 var NG_REMOVED = '$$NG_REMOVED';
24477 var ngRepeatMinErr = minErr('ngRepeat');
24479 var updateScope = function(scope, index, valueIdentifier, value, keyIdentifier, key, arrayLength) {
24480 // TODO(perf): generate setters to shave off ~40ms or 1-1.5%
24481 scope[valueIdentifier] = value;
24482 if (keyIdentifier) scope[keyIdentifier] = key;
24483 scope.$index = index;
24484 scope.$first = (index === 0);
24485 scope.$last = (index === (arrayLength - 1));
24486 scope.$middle = !(scope.$first || scope.$last);
24487 // jshint bitwise: false
24488 scope.$odd = !(scope.$even = (index&1) === 0);
24489 // jshint bitwise: true
24492 var getBlockStart = function(block) {
24493 return block.clone[0];
24496 var getBlockEnd = function(block) {
24497 return block.clone[block.clone.length - 1];
24503 multiElement: true,
24504 transclude: 'element',
24508 compile: function ngRepeatCompile($element, $attr) {
24509 var expression = $attr.ngRepeat;
24510 var ngRepeatEndComment = document.createComment(' end ngRepeat: ' + expression + ' ');
24512 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*$/);
24515 throw ngRepeatMinErr('iexp', "Expected expression in form of '_item_ in _collection_[ track by _id_]' but got '{0}'.",
24519 var lhs = match[1];
24520 var rhs = match[2];
24521 var aliasAs = match[3];
24522 var trackByExp = match[4];
24524 match = lhs.match(/^(?:(\s*[\$\w]+)|\(\s*([\$\w]+)\s*,\s*([\$\w]+)\s*\))$/);
24527 throw ngRepeatMinErr('iidexp', "'_item_' in '_item_ in _collection_' should be an identifier or '(_key_, _value_)' expression, but got '{0}'.",
24530 var valueIdentifier = match[3] || match[1];
24531 var keyIdentifier = match[2];
24533 if (aliasAs && (!/^[$a-zA-Z_][$a-zA-Z0-9_]*$/.test(aliasAs) ||
24534 /^(null|undefined|this|\$index|\$first|\$middle|\$last|\$even|\$odd|\$parent|\$root|\$id)$/.test(aliasAs))) {
24535 throw ngRepeatMinErr('badident', "alias '{0}' is invalid --- must be a valid JS identifier which is not a reserved name.",
24539 var trackByExpGetter, trackByIdExpFn, trackByIdArrayFn, trackByIdObjFn;
24540 var hashFnLocals = {$id: hashKey};
24543 trackByExpGetter = $parse(trackByExp);
24545 trackByIdArrayFn = function(key, value) {
24546 return hashKey(value);
24548 trackByIdObjFn = function(key) {
24553 return function ngRepeatLink($scope, $element, $attr, ctrl, $transclude) {
24555 if (trackByExpGetter) {
24556 trackByIdExpFn = function(key, value, index) {
24557 // assign key, value, and $index to the locals so that they can be used in hash functions
24558 if (keyIdentifier) hashFnLocals[keyIdentifier] = key;
24559 hashFnLocals[valueIdentifier] = value;
24560 hashFnLocals.$index = index;
24561 return trackByExpGetter($scope, hashFnLocals);
24565 // Store a list of elements from previous run. This is a hash where key is the item from the
24566 // iterator, and the value is objects with following properties.
24567 // - scope: bound scope
24568 // - element: previous element.
24569 // - index: position
24571 // We are using no-proto object so that we don't need to guard against inherited props via
24573 var lastBlockMap = createMap();
24576 $scope.$watchCollection(rhs, function ngRepeatAction(collection) {
24578 previousNode = $element[0], // node that cloned nodes should be inserted after
24579 // initialized to the comment node anchor
24581 // Same as lastBlockMap but it has the current state. It will become the
24582 // lastBlockMap on the next iteration.
24583 nextBlockMap = createMap(),
24585 key, value, // key/value of iteration
24589 block, // last object information {scope, element, id}
24594 $scope[aliasAs] = collection;
24597 if (isArrayLike(collection)) {
24598 collectionKeys = collection;
24599 trackByIdFn = trackByIdExpFn || trackByIdArrayFn;
24601 trackByIdFn = trackByIdExpFn || trackByIdObjFn;
24602 // if object, extract keys, sort them and use to determine order of iteration over obj props
24603 collectionKeys = [];
24604 for (var itemKey in collection) {
24605 if (collection.hasOwnProperty(itemKey) && itemKey.charAt(0) != '$') {
24606 collectionKeys.push(itemKey);
24609 collectionKeys.sort();
24612 collectionLength = collectionKeys.length;
24613 nextBlockOrder = new Array(collectionLength);
24615 // locate existing items
24616 for (index = 0; index < collectionLength; index++) {
24617 key = (collection === collectionKeys) ? index : collectionKeys[index];
24618 value = collection[key];
24619 trackById = trackByIdFn(key, value, index);
24620 if (lastBlockMap[trackById]) {
24621 // found previously seen block
24622 block = lastBlockMap[trackById];
24623 delete lastBlockMap[trackById];
24624 nextBlockMap[trackById] = block;
24625 nextBlockOrder[index] = block;
24626 } else if (nextBlockMap[trackById]) {
24627 // if collision detected. restore lastBlockMap and throw an error
24628 forEach(nextBlockOrder, function(block) {
24629 if (block && block.scope) lastBlockMap[block.id] = block;
24631 throw ngRepeatMinErr('dupes',
24632 "Duplicates in a repeater are not allowed. Use 'track by' expression to specify unique keys. Repeater: {0}, Duplicate key: {1}, Duplicate value: {2}",
24633 expression, trackById, value);
24635 // new never before seen block
24636 nextBlockOrder[index] = {id: trackById, scope: undefined, clone: undefined};
24637 nextBlockMap[trackById] = true;
24641 // remove leftover items
24642 for (var blockKey in lastBlockMap) {
24643 block = lastBlockMap[blockKey];
24644 elementsToRemove = getBlockNodes(block.clone);
24645 $animate.leave(elementsToRemove);
24646 if (elementsToRemove[0].parentNode) {
24647 // if the element was not removed yet because of pending animation, mark it as deleted
24648 // so that we can ignore it later
24649 for (index = 0, length = elementsToRemove.length; index < length; index++) {
24650 elementsToRemove[index][NG_REMOVED] = true;
24653 block.scope.$destroy();
24656 // we are not using forEach for perf reasons (trying to avoid #call)
24657 for (index = 0; index < collectionLength; index++) {
24658 key = (collection === collectionKeys) ? index : collectionKeys[index];
24659 value = collection[key];
24660 block = nextBlockOrder[index];
24663 // if we have already seen this object, then we need to reuse the
24664 // associated scope/element
24666 nextNode = previousNode;
24668 // skip nodes that are already pending removal via leave animation
24670 nextNode = nextNode.nextSibling;
24671 } while (nextNode && nextNode[NG_REMOVED]);
24673 if (getBlockStart(block) != nextNode) {
24674 // existing item which got moved
24675 $animate.move(getBlockNodes(block.clone), null, jqLite(previousNode));
24677 previousNode = getBlockEnd(block);
24678 updateScope(block.scope, index, valueIdentifier, value, keyIdentifier, key, collectionLength);
24680 // new item which we don't know about
24681 $transclude(function ngRepeatTransclude(clone, scope) {
24682 block.scope = scope;
24683 // http://jsperf.com/clone-vs-createcomment
24684 var endNode = ngRepeatEndComment.cloneNode(false);
24685 clone[clone.length++] = endNode;
24687 // TODO(perf): support naked previousNode in `enter` to avoid creation of jqLite wrapper?
24688 $animate.enter(clone, null, jqLite(previousNode));
24689 previousNode = endNode;
24690 // Note: We only need the first/last node of the cloned nodes.
24691 // However, we need to keep the reference to the jqlite wrapper as it might be changed later
24692 // by a directive with templateUrl when its template arrives.
24693 block.clone = clone;
24694 nextBlockMap[block.id] = block;
24695 updateScope(block.scope, index, valueIdentifier, value, keyIdentifier, key, collectionLength);
24699 lastBlockMap = nextBlockMap;
24706 var NG_HIDE_CLASS = 'ng-hide';
24707 var NG_HIDE_IN_PROGRESS_CLASS = 'ng-hide-animate';
24713 * The `ngShow` directive shows or hides the given HTML element based on the expression
24714 * provided to the `ngShow` attribute. The element is shown or hidden by removing or adding
24715 * the `.ng-hide` CSS class onto the element. The `.ng-hide` CSS class is predefined
24716 * in AngularJS and sets the display style to none (using an !important flag).
24717 * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
24720 * <!-- when $scope.myValue is truthy (element is visible) -->
24721 * <div ng-show="myValue"></div>
24723 * <!-- when $scope.myValue is falsy (element is hidden) -->
24724 * <div ng-show="myValue" class="ng-hide"></div>
24727 * When the `ngShow` expression evaluates to a falsy value then the `.ng-hide` CSS class is added to the class
24728 * attribute on the element causing it to become hidden. When truthy, the `.ng-hide` CSS class is removed
24729 * from the element causing the element not to appear hidden.
24731 * ## Why is !important used?
24733 * You may be wondering why !important is used for the `.ng-hide` CSS class. This is because the `.ng-hide` selector
24734 * can be easily overridden by heavier selectors. For example, something as simple
24735 * as changing the display style on a HTML list item would make hidden elements appear visible.
24736 * This also becomes a bigger issue when dealing with CSS frameworks.
24738 * By using !important, the show and hide behavior will work as expected despite any clash between CSS selector
24739 * specificity (when !important isn't used with any conflicting styles). If a developer chooses to override the
24740 * styling to change how to hide an element then it is just a matter of using !important in their own CSS code.
24742 * ### Overriding `.ng-hide`
24744 * By default, the `.ng-hide` class will style the element with `display: none!important`. If you wish to change
24745 * the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide`
24746 * class CSS. Note that the selector that needs to be used is actually `.ng-hide:not(.ng-hide-animate)` to cope
24747 * with extra animation classes that can be added.
24750 * .ng-hide:not(.ng-hide-animate) {
24751 * /* this is just another form of hiding an element */
24752 * display: block!important;
24753 * position: absolute;
24759 * By default you don't need to override in CSS anything and the animations will work around the display style.
24761 * ## A note about animations with `ngShow`
24763 * Animations in ngShow/ngHide work with the show and hide events that are triggered when the directive expression
24764 * is true and false. This system works like the animation system present with ngClass except that
24765 * you must also include the !important flag to override the display property
24766 * so that you can perform an animation when the element is hidden during the time of the animation.
24770 * //a working example can be found at the bottom of this page
24772 * .my-element.ng-hide-add, .my-element.ng-hide-remove {
24773 * /* this is required as of 1.3x to properly
24774 * apply all styling in a show/hide animation */
24775 * transition: 0s linear all;
24778 * .my-element.ng-hide-add-active,
24779 * .my-element.ng-hide-remove-active {
24780 * /* the transition is defined in the active class */
24781 * transition: 1s linear all;
24784 * .my-element.ng-hide-add { ... }
24785 * .my-element.ng-hide-add.ng-hide-add-active { ... }
24786 * .my-element.ng-hide-remove { ... }
24787 * .my-element.ng-hide-remove.ng-hide-remove-active { ... }
24790 * Keep in mind that, as of AngularJS version 1.3.0-beta.11, there is no need to change the display
24791 * property to block during animation states--ngAnimate will handle the style toggling automatically for you.
24794 * addClass: `.ng-hide` - happens after the `ngShow` expression evaluates to a truthy value and the just before contents are set to visible
24795 * removeClass: `.ng-hide` - happens after the `ngShow` expression evaluates to a non truthy value and just before the contents are set to hidden
24798 * @param {expression} ngShow If the {@link guide/expression expression} is truthy
24799 * then the element is shown or hidden respectively.
24802 <example module="ngAnimate" deps="angular-animate.js" animations="true">
24803 <file name="index.html">
24804 Click me: <input type="checkbox" ng-model="checked"><br/>
24807 <div class="check-element animate-show" ng-show="checked">
24808 <span class="glyphicon glyphicon-thumbs-up"></span> I show up when your checkbox is checked.
24813 <div class="check-element animate-show" ng-hide="checked">
24814 <span class="glyphicon glyphicon-thumbs-down"></span> I hide when your checkbox is checked.
24818 <file name="glyphicons.css">
24819 @import url(../../components/bootstrap-3.1.1/css/bootstrap.css);
24821 <file name="animations.css">
24826 border: 1px solid black;
24830 .animate-show.ng-hide-add.ng-hide-add-active,
24831 .animate-show.ng-hide-remove.ng-hide-remove-active {
24832 -webkit-transition: all linear 0.5s;
24833 transition: all linear 0.5s;
24836 .animate-show.ng-hide {
24844 border: 1px solid black;
24848 <file name="protractor.js" type="protractor">
24849 var thumbsUp = element(by.css('span.glyphicon-thumbs-up'));
24850 var thumbsDown = element(by.css('span.glyphicon-thumbs-down'));
24852 it('should check ng-show / ng-hide', function() {
24853 expect(thumbsUp.isDisplayed()).toBeFalsy();
24854 expect(thumbsDown.isDisplayed()).toBeTruthy();
24856 element(by.model('checked')).click();
24858 expect(thumbsUp.isDisplayed()).toBeTruthy();
24859 expect(thumbsDown.isDisplayed()).toBeFalsy();
24864 var ngShowDirective = ['$animate', function($animate) {
24867 multiElement: true,
24868 link: function(scope, element, attr) {
24869 scope.$watch(attr.ngShow, function ngShowWatchAction(value) {
24870 // we're adding a temporary, animation-specific class for ng-hide since this way
24871 // we can control when the element is actually displayed on screen without having
24872 // to have a global/greedy CSS selector that breaks when other animations are run.
24873 // Read: https://github.com/angular/angular.js/issues/9103#issuecomment-58335845
24874 $animate[value ? 'removeClass' : 'addClass'](element, NG_HIDE_CLASS, {
24875 tempClasses: NG_HIDE_IN_PROGRESS_CLASS
24888 * The `ngHide` directive shows or hides the given HTML element based on the expression
24889 * provided to the `ngHide` attribute. The element is shown or hidden by removing or adding
24890 * the `ng-hide` CSS class onto the element. The `.ng-hide` CSS class is predefined
24891 * in AngularJS and sets the display style to none (using an !important flag).
24892 * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
24895 * <!-- when $scope.myValue is truthy (element is hidden) -->
24896 * <div ng-hide="myValue" class="ng-hide"></div>
24898 * <!-- when $scope.myValue is falsy (element is visible) -->
24899 * <div ng-hide="myValue"></div>
24902 * When the `ngHide` expression evaluates to a truthy value then the `.ng-hide` CSS class is added to the class
24903 * attribute on the element causing it to become hidden. When falsy, the `.ng-hide` CSS class is removed
24904 * from the element causing the element not to appear hidden.
24906 * ## Why is !important used?
24908 * You may be wondering why !important is used for the `.ng-hide` CSS class. This is because the `.ng-hide` selector
24909 * can be easily overridden by heavier selectors. For example, something as simple
24910 * as changing the display style on a HTML list item would make hidden elements appear visible.
24911 * This also becomes a bigger issue when dealing with CSS frameworks.
24913 * By using !important, the show and hide behavior will work as expected despite any clash between CSS selector
24914 * specificity (when !important isn't used with any conflicting styles). If a developer chooses to override the
24915 * styling to change how to hide an element then it is just a matter of using !important in their own CSS code.
24917 * ### Overriding `.ng-hide`
24919 * By default, the `.ng-hide` class will style the element with `display: none!important`. If you wish to change
24920 * the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide`
24925 * /* this is just another form of hiding an element */
24926 * display: block!important;
24927 * position: absolute;
24933 * By default you don't need to override in CSS anything and the animations will work around the display style.
24935 * ## A note about animations with `ngHide`
24937 * Animations in ngShow/ngHide work with the show and hide events that are triggered when the directive expression
24938 * is true and false. This system works like the animation system present with ngClass, except that the `.ng-hide`
24939 * CSS class is added and removed for you instead of your own CSS class.
24943 * //a working example can be found at the bottom of this page
24945 * .my-element.ng-hide-add, .my-element.ng-hide-remove {
24946 * transition: 0.5s linear all;
24949 * .my-element.ng-hide-add { ... }
24950 * .my-element.ng-hide-add.ng-hide-add-active { ... }
24951 * .my-element.ng-hide-remove { ... }
24952 * .my-element.ng-hide-remove.ng-hide-remove-active { ... }
24955 * Keep in mind that, as of AngularJS version 1.3.0-beta.11, there is no need to change the display
24956 * property to block during animation states--ngAnimate will handle the style toggling automatically for you.
24959 * removeClass: `.ng-hide` - happens after the `ngHide` expression evaluates to a truthy value and just before the contents are set to hidden
24960 * addClass: `.ng-hide` - happens after the `ngHide` expression evaluates to a non truthy value and just before the contents are set to visible
24963 * @param {expression} ngHide If the {@link guide/expression expression} is truthy then
24964 * the element is shown or hidden respectively.
24967 <example module="ngAnimate" deps="angular-animate.js" animations="true">
24968 <file name="index.html">
24969 Click me: <input type="checkbox" ng-model="checked"><br/>
24972 <div class="check-element animate-hide" ng-show="checked">
24973 <span class="glyphicon glyphicon-thumbs-up"></span> I show up when your checkbox is checked.
24978 <div class="check-element animate-hide" ng-hide="checked">
24979 <span class="glyphicon glyphicon-thumbs-down"></span> I hide when your checkbox is checked.
24983 <file name="glyphicons.css">
24984 @import url(../../components/bootstrap-3.1.1/css/bootstrap.css);
24986 <file name="animations.css">
24988 -webkit-transition: all linear 0.5s;
24989 transition: all linear 0.5s;
24993 border: 1px solid black;
24997 .animate-hide.ng-hide {
25005 border: 1px solid black;
25009 <file name="protractor.js" type="protractor">
25010 var thumbsUp = element(by.css('span.glyphicon-thumbs-up'));
25011 var thumbsDown = element(by.css('span.glyphicon-thumbs-down'));
25013 it('should check ng-show / ng-hide', function() {
25014 expect(thumbsUp.isDisplayed()).toBeFalsy();
25015 expect(thumbsDown.isDisplayed()).toBeTruthy();
25017 element(by.model('checked')).click();
25019 expect(thumbsUp.isDisplayed()).toBeTruthy();
25020 expect(thumbsDown.isDisplayed()).toBeFalsy();
25025 var ngHideDirective = ['$animate', function($animate) {
25028 multiElement: true,
25029 link: function(scope, element, attr) {
25030 scope.$watch(attr.ngHide, function ngHideWatchAction(value) {
25031 // The comment inside of the ngShowDirective explains why we add and
25032 // remove a temporary class for the show/hide animation
25033 $animate[value ? 'addClass' : 'removeClass'](element,NG_HIDE_CLASS, {
25034 tempClasses: NG_HIDE_IN_PROGRESS_CLASS
25047 * The `ngStyle` directive allows you to set CSS style on an HTML element conditionally.
25050 * @param {expression} ngStyle
25052 * {@link guide/expression Expression} which evals to an
25053 * object whose keys are CSS style names and values are corresponding values for those CSS
25056 * Since some CSS style names are not valid keys for an object, they must be quoted.
25057 * See the 'background-color' style in the example below.
25061 <file name="index.html">
25062 <input type="button" value="set color" ng-click="myStyle={color:'red'}">
25063 <input type="button" value="set background" ng-click="myStyle={'background-color':'blue'}">
25064 <input type="button" value="clear" ng-click="myStyle={}">
25066 <span ng-style="myStyle">Sample Text</span>
25067 <pre>myStyle={{myStyle}}</pre>
25069 <file name="style.css">
25074 <file name="protractor.js" type="protractor">
25075 var colorSpan = element(by.css('span'));
25077 it('should check ng-style', function() {
25078 expect(colorSpan.getCssValue('color')).toBe('rgba(0, 0, 0, 1)');
25079 element(by.css('input[value=\'set color\']')).click();
25080 expect(colorSpan.getCssValue('color')).toBe('rgba(255, 0, 0, 1)');
25081 element(by.css('input[value=clear]')).click();
25082 expect(colorSpan.getCssValue('color')).toBe('rgba(0, 0, 0, 1)');
25087 var ngStyleDirective = ngDirective(function(scope, element, attr) {
25088 scope.$watchCollection(attr.ngStyle, function ngStyleWatchAction(newStyles, oldStyles) {
25089 if (oldStyles && (newStyles !== oldStyles)) {
25090 forEach(oldStyles, function(val, style) { element.css(style, '');});
25092 if (newStyles) element.css(newStyles);
25102 * The `ngSwitch` directive is used to conditionally swap DOM structure on your template based on a scope expression.
25103 * Elements within `ngSwitch` but without `ngSwitchWhen` or `ngSwitchDefault` directives will be preserved at the location
25104 * as specified in the template.
25106 * The directive itself works similar to ngInclude, however, instead of downloading template code (or loading it
25107 * from the template cache), `ngSwitch` simply chooses one of the nested elements and makes it visible based on which element
25108 * matches the value obtained from the evaluated expression. In other words, you define a container element
25109 * (where you place the directive), place an expression on the **`on="..."` attribute**
25110 * (or the **`ng-switch="..."` attribute**), define any inner elements inside of the directive and place
25111 * a when attribute per element. The when attribute is used to inform ngSwitch which element to display when the on
25112 * expression is evaluated. If a matching expression is not found via a when attribute then an element with the default
25113 * attribute is displayed.
25115 * <div class="alert alert-info">
25116 * Be aware that the attribute values to match against cannot be expressions. They are interpreted
25117 * as literal string values to match against.
25118 * For example, **`ng-switch-when="someVal"`** will match against the string `"someVal"` not against the
25119 * value of the expression `$scope.someVal`.
25123 * enter - happens after the ngSwitch contents change and the matched child element is placed inside the container
25124 * leave - happens just after the ngSwitch contents change and just before the former contents are removed from the DOM
25129 * <ANY ng-switch="expression">
25130 * <ANY ng-switch-when="matchValue1">...</ANY>
25131 * <ANY ng-switch-when="matchValue2">...</ANY>
25132 * <ANY ng-switch-default>...</ANY>
25139 * @param {*} ngSwitch|on expression to match against <tt>ng-switch-when</tt>.
25140 * On child elements add:
25142 * * `ngSwitchWhen`: the case statement to match against. If match then this
25143 * case will be displayed. If the same match appears multiple times, all the
25144 * elements will be displayed.
25145 * * `ngSwitchDefault`: the default case when no other case match. If there
25146 * are multiple default cases, all of them will be displayed when no other
25151 <example module="switchExample" deps="angular-animate.js" animations="true">
25152 <file name="index.html">
25153 <div ng-controller="ExampleController">
25154 <select ng-model="selection" ng-options="item for item in items">
25156 <tt>selection={{selection}}</tt>
25158 <div class="animate-switch-container"
25159 ng-switch on="selection">
25160 <div class="animate-switch" ng-switch-when="settings">Settings Div</div>
25161 <div class="animate-switch" ng-switch-when="home">Home Span</div>
25162 <div class="animate-switch" ng-switch-default>default</div>
25166 <file name="script.js">
25167 angular.module('switchExample', ['ngAnimate'])
25168 .controller('ExampleController', ['$scope', function($scope) {
25169 $scope.items = ['settings', 'home', 'other'];
25170 $scope.selection = $scope.items[0];
25173 <file name="animations.css">
25174 .animate-switch-container {
25177 border:1px solid black;
25186 .animate-switch.ng-animate {
25187 -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
25188 transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
25197 .animate-switch.ng-leave.ng-leave-active,
25198 .animate-switch.ng-enter {
25201 .animate-switch.ng-leave,
25202 .animate-switch.ng-enter.ng-enter-active {
25206 <file name="protractor.js" type="protractor">
25207 var switchElem = element(by.css('[ng-switch]'));
25208 var select = element(by.model('selection'));
25210 it('should start in settings', function() {
25211 expect(switchElem.getText()).toMatch(/Settings Div/);
25213 it('should change to home', function() {
25214 select.all(by.css('option')).get(1).click();
25215 expect(switchElem.getText()).toMatch(/Home Span/);
25217 it('should select default', function() {
25218 select.all(by.css('option')).get(2).click();
25219 expect(switchElem.getText()).toMatch(/default/);
25224 var ngSwitchDirective = ['$animate', function($animate) {
25227 require: 'ngSwitch',
25229 // asks for $scope to fool the BC controller module
25230 controller: ['$scope', function ngSwitchController() {
25233 link: function(scope, element, attr, ngSwitchController) {
25234 var watchExpr = attr.ngSwitch || attr.on,
25235 selectedTranscludes = [],
25236 selectedElements = [],
25237 previousLeaveAnimations = [],
25238 selectedScopes = [];
25240 var spliceFactory = function(array, index) {
25241 return function() { array.splice(index, 1); };
25244 scope.$watch(watchExpr, function ngSwitchWatchAction(value) {
25246 for (i = 0, ii = previousLeaveAnimations.length; i < ii; ++i) {
25247 $animate.cancel(previousLeaveAnimations[i]);
25249 previousLeaveAnimations.length = 0;
25251 for (i = 0, ii = selectedScopes.length; i < ii; ++i) {
25252 var selected = getBlockNodes(selectedElements[i].clone);
25253 selectedScopes[i].$destroy();
25254 var promise = previousLeaveAnimations[i] = $animate.leave(selected);
25255 promise.then(spliceFactory(previousLeaveAnimations, i));
25258 selectedElements.length = 0;
25259 selectedScopes.length = 0;
25261 if ((selectedTranscludes = ngSwitchController.cases['!' + value] || ngSwitchController.cases['?'])) {
25262 forEach(selectedTranscludes, function(selectedTransclude) {
25263 selectedTransclude.transclude(function(caseElement, selectedScope) {
25264 selectedScopes.push(selectedScope);
25265 var anchor = selectedTransclude.element;
25266 caseElement[caseElement.length++] = document.createComment(' end ngSwitchWhen: ');
25267 var block = { clone: caseElement };
25269 selectedElements.push(block);
25270 $animate.enter(caseElement, anchor.parent(), anchor);
25279 var ngSwitchWhenDirective = ngDirective({
25280 transclude: 'element',
25282 require: '^ngSwitch',
25283 multiElement: true,
25284 link: function(scope, element, attrs, ctrl, $transclude) {
25285 ctrl.cases['!' + attrs.ngSwitchWhen] = (ctrl.cases['!' + attrs.ngSwitchWhen] || []);
25286 ctrl.cases['!' + attrs.ngSwitchWhen].push({ transclude: $transclude, element: element });
25290 var ngSwitchDefaultDirective = ngDirective({
25291 transclude: 'element',
25293 require: '^ngSwitch',
25294 multiElement: true,
25295 link: function(scope, element, attr, ctrl, $transclude) {
25296 ctrl.cases['?'] = (ctrl.cases['?'] || []);
25297 ctrl.cases['?'].push({ transclude: $transclude, element: element });
25303 * @name ngTransclude
25307 * Directive that marks the insertion point for the transcluded DOM of the nearest parent directive that uses transclusion.
25309 * Any existing content of the element that this directive is placed on will be removed before the transcluded content is inserted.
25314 <example module="transcludeExample">
25315 <file name="index.html">
25317 angular.module('transcludeExample', [])
25318 .directive('pane', function(){
25322 scope: { title:'@' },
25323 template: '<div style="border: 1px solid black;">' +
25324 '<div style="background-color: gray">{{title}}</div>' +
25325 '<ng-transclude></ng-transclude>' +
25329 .controller('ExampleController', ['$scope', function($scope) {
25330 $scope.title = 'Lorem Ipsum';
25331 $scope.text = 'Neque porro quisquam est qui dolorem ipsum quia dolor...';
25334 <div ng-controller="ExampleController">
25335 <input ng-model="title"> <br/>
25336 <textarea ng-model="text"></textarea> <br/>
25337 <pane title="{{title}}">{{text}}</pane>
25340 <file name="protractor.js" type="protractor">
25341 it('should have transcluded', function() {
25342 var titleElement = element(by.model('title'));
25343 titleElement.clear();
25344 titleElement.sendKeys('TITLE');
25345 var textElement = element(by.model('text'));
25346 textElement.clear();
25347 textElement.sendKeys('TEXT');
25348 expect(element(by.binding('title')).getText()).toEqual('TITLE');
25349 expect(element(by.binding('text')).getText()).toEqual('TEXT');
25355 var ngTranscludeDirective = ngDirective({
25357 link: function($scope, $element, $attrs, controller, $transclude) {
25358 if (!$transclude) {
25359 throw minErr('ngTransclude')('orphan',
25360 'Illegal use of ngTransclude directive in the template! ' +
25361 'No parent directive that requires a transclusion found. ' +
25363 startingTag($element));
25366 $transclude(function(clone) {
25368 $element.append(clone);
25379 * Load the content of a `<script>` element into {@link ng.$templateCache `$templateCache`}, so that the
25380 * template can be used by {@link ng.directive:ngInclude `ngInclude`},
25381 * {@link ngRoute.directive:ngView `ngView`}, or {@link guide/directive directives}. The type of the
25382 * `<script>` element must be specified as `text/ng-template`, and a cache name for the template must be
25383 * assigned through the element's `id`, which can then be used as a directive's `templateUrl`.
25385 * @param {string} type Must be set to `'text/ng-template'`.
25386 * @param {string} id Cache name of the template.
25390 <file name="index.html">
25391 <script type="text/ng-template" id="/tpl.html">
25392 Content of the template.
25395 <a ng-click="currentTpl='/tpl.html'" id="tpl-link">Load inlined template</a>
25396 <div id="tpl-content" ng-include src="currentTpl"></div>
25398 <file name="protractor.js" type="protractor">
25399 it('should load template defined inside script tag', function() {
25400 element(by.css('#tpl-link')).click();
25401 expect(element(by.css('#tpl-content')).getText()).toMatch(/Content of the template/);
25406 var scriptDirective = ['$templateCache', function($templateCache) {
25410 compile: function(element, attr) {
25411 if (attr.type == 'text/ng-template') {
25412 var templateUrl = attr.id,
25413 text = element[0].text;
25415 $templateCache.put(templateUrl, text);
25421 var ngOptionsMinErr = minErr('ngOptions');
25428 * HTML `SELECT` element with angular data-binding.
25432 * The `ngOptions` attribute can be used to dynamically generate a list of `<option>`
25433 * elements for the `<select>` element using the array or object obtained by evaluating the
25434 * `ngOptions` comprehension expression.
25436 * In many cases, `ngRepeat` can be used on `<option>` elements instead of `ngOptions` to achieve a
25437 * similar result. However, `ngOptions` provides some benefits such as reducing memory and
25438 * increasing speed by not creating a new scope for each repeated instance, as well as providing
25439 * more flexibility in how the `<select>`'s model is assigned via the `select` **`as`** part of the
25440 * comprehension expression. `ngOptions` should be used when the `<select>` model needs to be bound
25441 * to a non-string value. This is because an option element can only be bound to string values at
25444 * When an item in the `<select>` menu is selected, the array element or object property
25445 * represented by the selected option will be bound to the model identified by the `ngModel`
25448 * Optionally, a single hard-coded `<option>` element, with the value set to an empty string, can
25449 * be nested into the `<select>` element. This element will then represent the `null` or "not selected"
25450 * option. See example below for demonstration.
25452 * <div class="alert alert-warning">
25453 * **Note:** `ngModel` compares by reference, not value. This is important when binding to an
25454 * array of objects. See an example [in this jsfiddle](http://jsfiddle.net/qWzTb/).
25457 * ## `select` **`as`**
25459 * Using `select` **`as`** will bind the result of the `select` expression to the model, but
25460 * the value of the `<select>` and `<option>` html elements will be either the index (for array data sources)
25461 * or property name (for object data sources) of the value within the collection. If a **`track by`** expression
25462 * is used, the result of that expression will be set as the value of the `option` and `select` elements.
25465 * ### `select` **`as`** and **`track by`**
25467 * <div class="alert alert-warning">
25468 * Do not use `select` **`as`** and **`track by`** in the same expression. They are not designed to work together.
25471 * Consider the following example:
25474 * <select ng-options="item.subItem as item.label for item in values track by item.id" ng-model="selected">
25478 * $scope.values = [{
25481 * subItem: { name: 'aSubItem' }
25485 * subItem: { name: 'bSubItem' }
25488 * $scope.selected = { name: 'aSubItem' };
25491 * With the purpose of preserving the selection, the **`track by`** expression is always applied to the element
25492 * of the data source (to `item` in this example). To calculate whether an element is selected, we do the
25495 * 1. Apply **`track by`** to the elements in the array. In the example: `[1, 2]`
25496 * 2. Apply **`track by`** to the already selected value in `ngModel`.
25497 * In the example: this is not possible as **`track by`** refers to `item.id`, but the selected
25498 * value from `ngModel` is `{name: 'aSubItem'}`, so the **`track by`** expression is applied to
25499 * a wrong object, the selected element can't be found, `<select>` is always reset to the "not
25500 * selected" option.
25503 * @param {string} ngModel Assignable angular expression to data-bind to.
25504 * @param {string=} name Property name of the form under which the control is published.
25505 * @param {string=} required The control is considered valid only if value is entered.
25506 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
25507 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
25508 * `required` when you want to data-bind to the `required` attribute.
25509 * @param {comprehension_expression=} ngOptions in one of the following forms:
25511 * * for array data sources:
25512 * * `label` **`for`** `value` **`in`** `array`
25513 * * `select` **`as`** `label` **`for`** `value` **`in`** `array`
25514 * * `label` **`group by`** `group` **`for`** `value` **`in`** `array`
25515 * * `label` **`group by`** `group` **`for`** `value` **`in`** `array` **`track by`** `trackexpr`
25516 * * `label` **`for`** `value` **`in`** `array` | orderBy:`orderexpr` **`track by`** `trackexpr`
25517 * (for including a filter with `track by`)
25518 * * for object data sources:
25519 * * `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
25520 * * `select` **`as`** `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
25521 * * `label` **`group by`** `group` **`for (`**`key`**`,`** `value`**`) in`** `object`
25522 * * `select` **`as`** `label` **`group by`** `group`
25523 * **`for` `(`**`key`**`,`** `value`**`) in`** `object`
25527 * * `array` / `object`: an expression which evaluates to an array / object to iterate over.
25528 * * `value`: local variable which will refer to each item in the `array` or each property value
25529 * of `object` during iteration.
25530 * * `key`: local variable which will refer to a property name in `object` during iteration.
25531 * * `label`: The result of this expression will be the label for `<option>` element. The
25532 * `expression` will most likely refer to the `value` variable (e.g. `value.propertyName`).
25533 * * `select`: The result of this expression will be bound to the model of the parent `<select>`
25534 * element. If not specified, `select` expression will default to `value`.
25535 * * `group`: The result of this expression will be used to group options using the `<optgroup>`
25537 * * `trackexpr`: Used when working with an array of objects. The result of this expression will be
25538 * used to identify the objects in the array. The `trackexpr` will most likely refer to the
25539 * `value` variable (e.g. `value.propertyName`). With this the selection is preserved
25540 * even when the options are recreated (e.g. reloaded from the server).
25543 <example module="selectExample">
25544 <file name="index.html">
25546 angular.module('selectExample', [])
25547 .controller('ExampleController', ['$scope', function($scope) {
25549 {name:'black', shade:'dark'},
25550 {name:'white', shade:'light'},
25551 {name:'red', shade:'dark'},
25552 {name:'blue', shade:'dark'},
25553 {name:'yellow', shade:'light'}
25555 $scope.myColor = $scope.colors[2]; // red
25558 <div ng-controller="ExampleController">
25560 <li ng-repeat="color in colors">
25561 Name: <input ng-model="color.name">
25562 [<a href ng-click="colors.splice($index, 1)">X</a>]
25565 [<a href ng-click="colors.push({})">add</a>]
25569 Color (null not allowed):
25570 <select ng-model="myColor" ng-options="color.name for color in colors"></select><br>
25572 Color (null allowed):
25573 <span class="nullable">
25574 <select ng-model="myColor" ng-options="color.name for color in colors">
25575 <option value="">-- choose color --</option>
25579 Color grouped by shade:
25580 <select ng-model="myColor" ng-options="color.name group by color.shade for color in colors">
25584 Select <a href ng-click="myColor = { name:'not in list', shade: 'other' }">bogus</a>.<br>
25586 Currently selected: {{ {selected_color:myColor} }}
25587 <div style="border:solid 1px black; height:20px"
25588 ng-style="{'background-color':myColor.name}">
25592 <file name="protractor.js" type="protractor">
25593 it('should check ng-options', function() {
25594 expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('red');
25595 element.all(by.model('myColor')).first().click();
25596 element.all(by.css('select[ng-model="myColor"] option')).first().click();
25597 expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('black');
25598 element(by.css('.nullable select[ng-model="myColor"]')).click();
25599 element.all(by.css('.nullable select[ng-model="myColor"] option')).first().click();
25600 expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('null');
25606 var ngOptionsDirective = valueFn({
25611 // jshint maxlen: false
25612 var selectDirective = ['$compile', '$parse', function($compile, $parse) {
25613 //000011111111110000000000022222222220000000000000000000003333333333000000000000004444444444444440000000005555555555555550000000666666666666666000000000000000777777777700000000000000000008888888888
25614 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]+?))?$/,
25615 nullModelCtrl = {$setViewValue: noop};
25616 // jshint maxlen: 100
25620 require: ['select', '?ngModel'],
25621 controller: ['$element', '$scope', '$attrs', function($element, $scope, $attrs) {
25624 ngModelCtrl = nullModelCtrl,
25629 self.databound = $attrs.ngModel;
25632 self.init = function(ngModelCtrl_, nullOption_, unknownOption_) {
25633 ngModelCtrl = ngModelCtrl_;
25634 nullOption = nullOption_;
25635 unknownOption = unknownOption_;
25639 self.addOption = function(value, element) {
25640 assertNotHasOwnProperty(value, '"option value"');
25641 optionsMap[value] = true;
25643 if (ngModelCtrl.$viewValue == value) {
25644 $element.val(value);
25645 if (unknownOption.parent()) unknownOption.remove();
25647 // Workaround for https://code.google.com/p/chromium/issues/detail?id=381459
25648 // Adding an <option selected="selected"> element to a <select required="required"> should
25649 // automatically select the new element
25650 if (element && element[0].hasAttribute('selected')) {
25651 element[0].selected = true;
25656 self.removeOption = function(value) {
25657 if (this.hasOption(value)) {
25658 delete optionsMap[value];
25659 if (ngModelCtrl.$viewValue === value) {
25660 this.renderUnknownOption(value);
25666 self.renderUnknownOption = function(val) {
25667 var unknownVal = '? ' + hashKey(val) + ' ?';
25668 unknownOption.val(unknownVal);
25669 $element.prepend(unknownOption);
25670 $element.val(unknownVal);
25671 unknownOption.prop('selected', true); // needed for IE
25675 self.hasOption = function(value) {
25676 return optionsMap.hasOwnProperty(value);
25679 $scope.$on('$destroy', function() {
25680 // disable unknown option so that we don't do work when the whole select is being destroyed
25681 self.renderUnknownOption = noop;
25685 link: function(scope, element, attr, ctrls) {
25686 // if ngModel is not defined, we don't need to do anything
25687 if (!ctrls[1]) return;
25689 var selectCtrl = ctrls[0],
25690 ngModelCtrl = ctrls[1],
25691 multiple = attr.multiple,
25692 optionsExp = attr.ngOptions,
25693 nullOption = false, // if false, user will not be able to select it (used by ngOptions)
25695 renderScheduled = false,
25696 // we can't just jqLite('<option>') since jqLite is not smart enough
25697 // to create it in <select> and IE barfs otherwise.
25698 optionTemplate = jqLite(document.createElement('option')),
25699 optGroupTemplate =jqLite(document.createElement('optgroup')),
25700 unknownOption = optionTemplate.clone();
25702 // find "null" option
25703 for (var i = 0, children = element.children(), ii = children.length; i < ii; i++) {
25704 if (children[i].value === '') {
25705 emptyOption = nullOption = children.eq(i);
25710 selectCtrl.init(ngModelCtrl, nullOption, unknownOption);
25712 // required validator
25714 ngModelCtrl.$isEmpty = function(value) {
25715 return !value || value.length === 0;
25719 if (optionsExp) setupAsOptions(scope, element, ngModelCtrl);
25720 else if (multiple) setupAsMultiple(scope, element, ngModelCtrl);
25721 else setupAsSingle(scope, element, ngModelCtrl, selectCtrl);
25724 ////////////////////////////
25728 function setupAsSingle(scope, selectElement, ngModelCtrl, selectCtrl) {
25729 ngModelCtrl.$render = function() {
25730 var viewValue = ngModelCtrl.$viewValue;
25732 if (selectCtrl.hasOption(viewValue)) {
25733 if (unknownOption.parent()) unknownOption.remove();
25734 selectElement.val(viewValue);
25735 if (viewValue === '') emptyOption.prop('selected', true); // to make IE9 happy
25737 if (isUndefined(viewValue) && emptyOption) {
25738 selectElement.val('');
25740 selectCtrl.renderUnknownOption(viewValue);
25745 selectElement.on('change', function() {
25746 scope.$apply(function() {
25747 if (unknownOption.parent()) unknownOption.remove();
25748 ngModelCtrl.$setViewValue(selectElement.val());
25753 function setupAsMultiple(scope, selectElement, ctrl) {
25755 ctrl.$render = function() {
25756 var items = new HashMap(ctrl.$viewValue);
25757 forEach(selectElement.find('option'), function(option) {
25758 option.selected = isDefined(items.get(option.value));
25762 // we have to do it on each watch since ngModel watches reference, but
25763 // we need to work of an array, so we need to see if anything was inserted/removed
25764 scope.$watch(function selectMultipleWatch() {
25765 if (!equals(lastView, ctrl.$viewValue)) {
25766 lastView = shallowCopy(ctrl.$viewValue);
25771 selectElement.on('change', function() {
25772 scope.$apply(function() {
25774 forEach(selectElement.find('option'), function(option) {
25775 if (option.selected) {
25776 array.push(option.value);
25779 ctrl.$setViewValue(array);
25784 function setupAsOptions(scope, selectElement, ctrl) {
25787 if (!(match = optionsExp.match(NG_OPTIONS_REGEXP))) {
25788 throw ngOptionsMinErr('iexp',
25789 "Expected expression in form of " +
25790 "'_select_ (as _label_)? for (_key_,)?_value_ in _collection_'" +
25791 " but got '{0}'. Element: {1}",
25792 optionsExp, startingTag(selectElement));
25795 var displayFn = $parse(match[2] || match[1]),
25796 valueName = match[4] || match[6],
25797 selectAs = / as /.test(match[0]) && match[1],
25798 selectAsFn = selectAs ? $parse(selectAs) : null,
25799 keyName = match[5],
25800 groupByFn = $parse(match[3] || ''),
25801 valueFn = $parse(match[2] ? match[1] : valueName),
25802 valuesFn = $parse(match[7]),
25804 trackFn = track ? $parse(match[8]) : null,
25805 trackKeysCache = {},
25806 // This is an array of array of existing option groups in DOM.
25807 // We try to reuse these if possible
25808 // - optionGroupsCache[0] is the options with no option group
25809 // - optionGroupsCache[?][0] is the parent: either the SELECT or OPTGROUP element
25810 optionGroupsCache = [[{element: selectElement, label:''}]],
25811 //re-usable object to represent option's locals
25815 // compile the element since there might be bindings in it
25816 $compile(nullOption)(scope);
25818 // remove the class, which is added automatically because we recompile the element and it
25819 // becomes the compilation root
25820 nullOption.removeClass('ng-scope');
25822 // we need to remove it before calling selectElement.empty() because otherwise IE will
25823 // remove the label from the element. wtf?
25824 nullOption.remove();
25827 // clear contents, we'll add what's needed based on the model
25828 selectElement.empty();
25830 selectElement.on('change', selectionChanged);
25832 ctrl.$render = render;
25834 scope.$watchCollection(valuesFn, scheduleRendering);
25835 scope.$watchCollection(getLabels, scheduleRendering);
25838 scope.$watchCollection(function() { return ctrl.$modelValue; }, scheduleRendering);
25841 // ------------------------------------------------------------------ //
25843 function callExpression(exprFn, key, value) {
25844 locals[valueName] = value;
25845 if (keyName) locals[keyName] = key;
25846 return exprFn(scope, locals);
25849 function selectionChanged() {
25850 scope.$apply(function() {
25851 var collection = valuesFn(scope) || [];
25855 forEach(selectElement.val(), function(selectedKey) {
25856 selectedKey = trackFn ? trackKeysCache[selectedKey] : selectedKey;
25857 viewValue.push(getViewValue(selectedKey, collection[selectedKey]));
25860 var selectedKey = trackFn ? trackKeysCache[selectElement.val()] : selectElement.val();
25861 viewValue = getViewValue(selectedKey, collection[selectedKey]);
25863 ctrl.$setViewValue(viewValue);
25868 function getViewValue(key, value) {
25871 } else if (key === '') {
25874 var viewValueFn = selectAsFn ? selectAsFn : valueFn;
25875 return callExpression(viewValueFn, key, value);
25879 function getLabels() {
25880 var values = valuesFn(scope);
25882 if (values && isArray(values)) {
25883 toDisplay = new Array(values.length);
25884 for (var i = 0, ii = values.length; i < ii; i++) {
25885 toDisplay[i] = callExpression(displayFn, i, values[i]);
25888 } else if (values) {
25889 // TODO: Add a test for this case
25891 for (var prop in values) {
25892 if (values.hasOwnProperty(prop)) {
25893 toDisplay[prop] = callExpression(displayFn, prop, values[prop]);
25900 function createIsSelectedFn(viewValue) {
25903 if (trackFn && isArray(viewValue)) {
25905 selectedSet = new HashMap([]);
25906 for (var trackIndex = 0; trackIndex < viewValue.length; trackIndex++) {
25908 selectedSet.put(callExpression(trackFn, null, viewValue[trackIndex]), true);
25911 selectedSet = new HashMap(viewValue);
25913 } else if (trackFn) {
25914 viewValue = callExpression(trackFn, null, viewValue);
25917 return function isSelected(key, value) {
25918 var compareValueFn;
25920 compareValueFn = trackFn;
25921 } else if (selectAsFn) {
25922 compareValueFn = selectAsFn;
25924 compareValueFn = valueFn;
25928 return isDefined(selectedSet.remove(callExpression(compareValueFn, key, value)));
25930 return viewValue === callExpression(compareValueFn, key, value);
25935 function scheduleRendering() {
25936 if (!renderScheduled) {
25937 scope.$$postDigest(render);
25938 renderScheduled = true;
25943 * A new labelMap is created with each render.
25944 * This function is called for each existing option with added=false,
25945 * and each new option with added=true.
25946 * - Labels that are passed to this method twice,
25947 * (once with added=true and once with added=false) will end up with a value of 0, and
25948 * will cause no change to happen to the corresponding option.
25949 * - Labels that are passed to this method only once with added=false will end up with a
25950 * value of -1 and will eventually be passed to selectCtrl.removeOption()
25951 * - Labels that are passed to this method only once with added=true will end up with a
25952 * value of 1 and will eventually be passed to selectCtrl.addOption()
25954 function updateLabelMap(labelMap, label, added) {
25955 labelMap[label] = labelMap[label] || 0;
25956 labelMap[label] += (added ? 1 : -1);
25959 function render() {
25960 renderScheduled = false;
25962 // Temporary location for the option groups before we render them
25963 var optionGroups = {'':[]},
25964 optionGroupNames = [''],
25968 existingParent, existingOptions, existingOption,
25969 viewValue = ctrl.$viewValue,
25970 values = valuesFn(scope) || [],
25971 keys = keyName ? sortedKeys(values) : values,
25974 groupLength, length,
25978 isSelected = createIsSelectedFn(viewValue),
25979 anySelected = false,
25985 trackKeysCache = {};
25987 // We now build up the list of options we need (we merge later)
25988 for (index = 0; length = keys.length, index < length; index++) {
25992 if (key.charAt(0) === '$') continue;
25994 value = values[key];
25996 optionGroupName = callExpression(groupByFn, key, value) || '';
25997 if (!(optionGroup = optionGroups[optionGroupName])) {
25998 optionGroup = optionGroups[optionGroupName] = [];
25999 optionGroupNames.push(optionGroupName);
26002 selected = isSelected(key, value);
26003 anySelected = anySelected || selected;
26005 label = callExpression(displayFn, key, value); // what will be seen by the user
26007 // doing displayFn(scope, locals) || '' overwrites zero values
26008 label = isDefined(label) ? label : '';
26009 optionId = trackFn ? trackFn(scope, locals) : (keyName ? keys[index] : index);
26011 trackKeysCache[optionId] = key;
26015 // either the index into array or key from object
26018 selected: selected // determine if we should be selected
26022 if (nullOption || viewValue === null) {
26023 // insert null option if we have a placeholder, or the model is null
26024 optionGroups[''].unshift({id:'', label:'', selected:!anySelected});
26025 } else if (!anySelected) {
26026 // option could not be found, we have to insert the undefined item
26027 optionGroups[''].unshift({id:'?', label:'', selected:true});
26031 // Now we need to update the list of DOM nodes to match the optionGroups we computed above
26032 for (groupIndex = 0, groupLength = optionGroupNames.length;
26033 groupIndex < groupLength;
26035 // current option group name or '' if no group
26036 optionGroupName = optionGroupNames[groupIndex];
26038 // list of options for that group. (first item has the parent)
26039 optionGroup = optionGroups[optionGroupName];
26041 if (optionGroupsCache.length <= groupIndex) {
26042 // we need to grow the optionGroups
26044 element: optGroupTemplate.clone().attr('label', optionGroupName),
26045 label: optionGroup.label
26047 existingOptions = [existingParent];
26048 optionGroupsCache.push(existingOptions);
26049 selectElement.append(existingParent.element);
26051 existingOptions = optionGroupsCache[groupIndex];
26052 existingParent = existingOptions[0]; // either SELECT (no group) or OPTGROUP element
26054 // update the OPTGROUP label if not the same.
26055 if (existingParent.label != optionGroupName) {
26056 existingParent.element.attr('label', existingParent.label = optionGroupName);
26060 lastElement = null; // start at the beginning
26061 for (index = 0, length = optionGroup.length; index < length; index++) {
26062 option = optionGroup[index];
26063 if ((existingOption = existingOptions[index + 1])) {
26065 lastElement = existingOption.element;
26066 if (existingOption.label !== option.label) {
26067 updateLabelMap(labelMap, existingOption.label, false);
26068 updateLabelMap(labelMap, option.label, true);
26069 lastElement.text(existingOption.label = option.label);
26070 lastElement.prop('label', existingOption.label);
26072 if (existingOption.id !== option.id) {
26073 lastElement.val(existingOption.id = option.id);
26075 // lastElement.prop('selected') provided by jQuery has side-effects
26076 if (lastElement[0].selected !== option.selected) {
26077 lastElement.prop('selected', (existingOption.selected = option.selected));
26080 // The selected item wouldn't visually update on IE without this.
26081 // Tested on Win7: IE9, IE10 and IE11. Future IEs should be tested as well
26082 lastElement.prop('selected', existingOption.selected);
26088 // if it's a null option
26089 if (option.id === '' && nullOption) {
26090 // put back the pre-compiled element
26091 element = nullOption;
26093 // jQuery(v1.4.2) Bug: We should be able to chain the method calls, but
26094 // in this version of jQuery on some browser the .text() returns a string
26095 // rather then the element.
26096 (element = optionTemplate.clone())
26098 .prop('selected', option.selected)
26099 .attr('selected', option.selected)
26100 .prop('label', option.label)
26101 .text(option.label);
26104 existingOptions.push(existingOption = {
26106 label: option.label,
26108 selected: option.selected
26110 updateLabelMap(labelMap, option.label, true);
26112 lastElement.after(element);
26114 existingParent.element.append(element);
26116 lastElement = element;
26119 // remove any excessive OPTIONs in a group
26120 index++; // increment since the existingOptions[0] is parent element not OPTION
26121 while (existingOptions.length > index) {
26122 option = existingOptions.pop();
26123 updateLabelMap(labelMap, option.label, false);
26124 option.element.remove();
26127 // remove any excessive OPTGROUPs from select
26128 while (optionGroupsCache.length > groupIndex) {
26129 // remove all the labels in the option group
26130 optionGroup = optionGroupsCache.pop();
26131 for (index = 1; index < optionGroup.length; ++index) {
26132 updateLabelMap(labelMap, optionGroup[index].label, false);
26134 optionGroup[0].element.remove();
26136 forEach(labelMap, function(count, label) {
26138 selectCtrl.addOption(label);
26139 } else if (count < 0) {
26140 selectCtrl.removeOption(label);
26149 var optionDirective = ['$interpolate', function($interpolate) {
26150 var nullSelectCtrl = {
26158 compile: function(element, attr) {
26159 if (isUndefined(attr.value)) {
26160 var interpolateFn = $interpolate(element.text(), true);
26161 if (!interpolateFn) {
26162 attr.$set('value', element.text());
26166 return function(scope, element, attr) {
26167 var selectCtrlName = '$selectController',
26168 parent = element.parent(),
26169 selectCtrl = parent.data(selectCtrlName) ||
26170 parent.parent().data(selectCtrlName); // in case we are in optgroup
26172 if (!selectCtrl || !selectCtrl.databound) {
26173 selectCtrl = nullSelectCtrl;
26176 if (interpolateFn) {
26177 scope.$watch(interpolateFn, function interpolateWatchAction(newVal, oldVal) {
26178 attr.$set('value', newVal);
26179 if (oldVal !== newVal) {
26180 selectCtrl.removeOption(oldVal);
26182 selectCtrl.addOption(newVal, element);
26185 selectCtrl.addOption(attr.value, element);
26188 element.on('$destroy', function() {
26189 selectCtrl.removeOption(attr.value);
26196 var styleDirective = valueFn({
26201 var requiredDirective = function() {
26204 require: '?ngModel',
26205 link: function(scope, elm, attr, ctrl) {
26207 attr.required = true; // force truthy in case we are on non input element
26209 ctrl.$validators.required = function(modelValue, viewValue) {
26210 return !attr.required || !ctrl.$isEmpty(viewValue);
26213 attr.$observe('required', function() {
26221 var patternDirective = function() {
26224 require: '?ngModel',
26225 link: function(scope, elm, attr, ctrl) {
26228 var regexp, patternExp = attr.ngPattern || attr.pattern;
26229 attr.$observe('pattern', function(regex) {
26230 if (isString(regex) && regex.length > 0) {
26231 regex = new RegExp('^' + regex + '$');
26234 if (regex && !regex.test) {
26235 throw minErr('ngPattern')('noregexp',
26236 'Expected {0} to be a RegExp but was {1}. Element: {2}', patternExp,
26237 regex, startingTag(elm));
26240 regexp = regex || undefined;
26244 ctrl.$validators.pattern = function(value) {
26245 return ctrl.$isEmpty(value) || isUndefined(regexp) || regexp.test(value);
26252 var maxlengthDirective = function() {
26255 require: '?ngModel',
26256 link: function(scope, elm, attr, ctrl) {
26259 var maxlength = -1;
26260 attr.$observe('maxlength', function(value) {
26261 var intVal = int(value);
26262 maxlength = isNaN(intVal) ? -1 : intVal;
26265 ctrl.$validators.maxlength = function(modelValue, viewValue) {
26266 return (maxlength < 0) || ctrl.$isEmpty(viewValue) || (viewValue.length <= maxlength);
26272 var minlengthDirective = function() {
26275 require: '?ngModel',
26276 link: function(scope, elm, attr, ctrl) {
26280 attr.$observe('minlength', function(value) {
26281 minlength = int(value) || 0;
26284 ctrl.$validators.minlength = function(modelValue, viewValue) {
26285 return ctrl.$isEmpty(viewValue) || viewValue.length >= minlength;
26291 if (window.angular.bootstrap) {
26292 //AngularJS is already loaded, so we can return here...
26293 console.log('WARNING: Tried to load angular more than once.');
26297 //try to bind to jquery now so that one can write jqLite(document).ready()
26298 //but we will rebind on bootstrap again.
26301 publishExternalAPI(angular);
26303 jqLite(document).ready(function() {
26304 angularInit(document, bootstrap);
26307 })(window, document);
26309 !window.angular.$$csp() && window.angular.element(document).find('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>');