2 * @license AngularJS v1.2.25
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 * @returns {function(code:string, template:string, ...templateArgs): Error} minErr instance
36 function minErr(module) {
38 var code = arguments[0],
39 prefix = '[' + (module ? module + ':' : '') + code + '] ',
40 template = arguments[1],
41 templateArgs = arguments,
42 stringify = function (obj) {
43 if (typeof obj === 'function') {
44 return obj.toString().replace(/ \{[\s\S]*$/, '');
45 } else if (typeof obj === 'undefined') {
47 } else if (typeof obj !== 'string') {
48 return JSON.stringify(obj);
54 message = prefix + template.replace(/\{\d+\}/g, function (match) {
55 var index = +match.slice(1, -1), arg;
57 if (index + 2 < templateArgs.length) {
58 arg = templateArgs[index + 2];
59 if (typeof arg === 'function') {
60 return arg.toString().replace(/ ?\{[\s\S]*$/, '');
61 } else if (typeof arg === 'undefined') {
63 } else if (typeof arg !== 'string') {
71 message = message + '\nhttp://errors.angularjs.org/1.2.25/' +
72 (module ? module + '/' : '') + code;
73 for (i = 2; i < arguments.length; i++) {
74 message = message + (i == 2 ? '?' : '&') + 'p' + (i-2) + '=' +
75 encodeURIComponent(stringify(arguments[i]));
78 return new Error(message);
82 /* We need to tell jshint what variables are being exported */
83 /* global angular: true,
94 VALIDITY_STATE_PROPERTY: true,
98 manualLowercase: true,
99 manualUppercase: true,
145 toJsonReplacer: true,
150 tryDecodeURIComponent: true,
153 encodeUriSegment: true,
154 encodeUriQuery: true,
161 assertNotHasOwnProperty: true,
163 getBlockElements: true,
164 hasOwnProperty: true,
167 ////////////////////////////////////
176 * The ng module is loaded by default when an AngularJS application is started. The module itself
177 * contains the essential components for an AngularJS application to function. The table below
178 * lists a high level breakdown of each of the services/factories, filters, directives and testing
179 * components available within this core module.
181 * <div doc-module-components="ng"></div>
184 // The name of a form control's ValidityState property.
185 // This is used so that it's possible for internal tests to create mock ValidityStates.
186 var VALIDITY_STATE_PROPERTY = 'validity';
190 * @name angular.lowercase
194 * @description Converts the specified string to lowercase.
195 * @param {string} string String to be converted to lowercase.
196 * @returns {string} Lowercased string.
198 var lowercase = function(string){return isString(string) ? string.toLowerCase() : string;};
199 var hasOwnProperty = Object.prototype.hasOwnProperty;
203 * @name angular.uppercase
207 * @description Converts the specified string to uppercase.
208 * @param {string} string String to be converted to uppercase.
209 * @returns {string} Uppercased string.
211 var uppercase = function(string){return isString(string) ? string.toUpperCase() : string;};
214 var manualLowercase = function(s) {
215 /* jshint bitwise: false */
217 ? s.replace(/[A-Z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) | 32);})
220 var manualUppercase = function(s) {
221 /* jshint bitwise: false */
223 ? s.replace(/[a-z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) & ~32);})
228 // String#toLowerCase and String#toUpperCase don't produce correct results in browsers with Turkish
229 // locale, for this reason we need to detect this case and redefine lowercase/uppercase methods
230 // with correct but slower alternatives.
231 if ('i' !== 'I'.toLowerCase()) {
232 lowercase = manualLowercase;
233 uppercase = manualUppercase;
237 var /** holds major version number for IE or NaN for real browsers */
239 jqLite, // delay binding since jQuery could be loaded after us.
240 jQuery, // delay binding
243 toString = Object.prototype.toString,
244 ngMinErr = minErr('ng'),
247 angular = window.angular || (window.angular = {}),
250 uid = ['0', '0', '0'];
253 * IE 11 changed the format of the UserAgent string.
254 * See http://msdn.microsoft.com/en-us/library/ms537503.aspx
256 msie = int((/msie (\d+)/.exec(lowercase(navigator.userAgent)) || [])[1]);
258 msie = int((/trident\/.*; rv:(\d+)/.exec(lowercase(navigator.userAgent)) || [])[1]);
265 * @return {boolean} Returns true if `obj` is an array or array-like object (NodeList, Arguments,
268 function isArrayLike(obj) {
269 if (obj == null || isWindow(obj)) {
273 var length = obj.length;
275 if (obj.nodeType === 1 && length) {
279 return isString(obj) || isArray(obj) || length === 0 ||
280 typeof length === 'number' && length > 0 && (length - 1) in obj;
285 * @name angular.forEach
290 * Invokes the `iterator` function once for each item in `obj` collection, which can be either an
291 * object or an array. The `iterator` function is invoked with `iterator(value, key)`, where `value`
292 * is the value of an object property or an array element and `key` is the object property key or
293 * array element index. Specifying a `context` for the function is optional.
295 * It is worth noting that `.forEach` does not iterate over inherited properties because it filters
296 * using the `hasOwnProperty` method.
299 var values = {name: 'misko', gender: 'male'};
301 angular.forEach(values, function(value, key) {
302 this.push(key + ': ' + value);
304 expect(log).toEqual(['name: misko', 'gender: male']);
307 * @param {Object|Array} obj Object to iterate over.
308 * @param {Function} iterator Iterator function.
309 * @param {Object=} context Object to become context (`this`) for the iterator function.
310 * @returns {Object|Array} Reference to `obj`.
312 function forEach(obj, iterator, context) {
315 if (isFunction(obj)) {
317 // Need to check if hasOwnProperty exists,
318 // as on IE8 the result of querySelectorAll is an object without a hasOwnProperty function
319 if (key != 'prototype' && key != 'length' && key != 'name' && (!obj.hasOwnProperty || obj.hasOwnProperty(key))) {
320 iterator.call(context, obj[key], key);
323 } else if (isArray(obj) || isArrayLike(obj)) {
324 for (key = 0; key < obj.length; key++) {
325 iterator.call(context, obj[key], key);
327 } else if (obj.forEach && obj.forEach !== forEach) {
328 obj.forEach(iterator, context);
331 if (obj.hasOwnProperty(key)) {
332 iterator.call(context, obj[key], key);
340 function sortedKeys(obj) {
342 for (var key in obj) {
343 if (obj.hasOwnProperty(key)) {
350 function forEachSorted(obj, iterator, context) {
351 var keys = sortedKeys(obj);
352 for ( var i = 0; i < keys.length; i++) {
353 iterator.call(context, obj[keys[i]], keys[i]);
360 * when using forEach the params are value, key, but it is often useful to have key, value.
361 * @param {function(string, *)} iteratorFn
362 * @returns {function(*, string)}
364 function reverseParams(iteratorFn) {
365 return function(value, key) { iteratorFn(key, value); };
369 * A consistent way of creating unique IDs in angular. The ID is a sequence of alpha numeric
370 * characters such as '012ABC'. The reason why we are not using simply a number counter is that
371 * the number string gets longer over time, and it can also overflow, where as the nextId
372 * will grow much slower, it is a string, and it will never overflow.
374 * @returns {string} an unique alpha-numeric string
377 var index = uid.length;
382 digit = uid[index].charCodeAt(0);
383 if (digit == 57 /*'9'*/) {
387 if (digit == 90 /*'Z'*/) {
390 uid[index] = String.fromCharCode(digit + 1);
400 * Set or clear the hashkey for an object.
402 * @param h the hashkey (!truthy to delete the hashkey)
404 function setHashKey(obj, h) {
409 delete obj.$$hashKey;
415 * @name angular.extend
420 * Extends the destination object `dst` by copying all of the properties from the `src` object(s)
421 * to `dst`. You can specify multiple `src` objects.
423 * @param {Object} dst Destination object.
424 * @param {...Object} src Source object(s).
425 * @returns {Object} Reference to `dst`.
427 function extend(dst) {
428 var h = dst.$$hashKey;
429 forEach(arguments, function(obj) {
431 forEach(obj, function(value, key) {
442 return parseInt(str, 10);
446 function inherit(parent, extra) {
447 return extend(new (extend(function() {}, {prototype:parent}))(), extra);
457 * A function that performs no operations. This function can be useful when writing code in the
460 function foo(callback) {
461 var result = calculateResult();
462 (callback || angular.noop)(result);
472 * @name angular.identity
477 * A function that returns its first argument. This function is useful when writing code in the
481 function transformer(transformationFn, value) {
482 return (transformationFn || angular.identity)(value);
486 function identity($) {return $;}
487 identity.$inject = [];
490 function valueFn(value) {return function() {return value;};}
494 * @name angular.isUndefined
499 * Determines if a reference is undefined.
501 * @param {*} value Reference to check.
502 * @returns {boolean} True if `value` is undefined.
504 function isUndefined(value){return typeof value === 'undefined';}
509 * @name angular.isDefined
514 * Determines if a reference is defined.
516 * @param {*} value Reference to check.
517 * @returns {boolean} True if `value` is defined.
519 function isDefined(value){return typeof value !== 'undefined';}
524 * @name angular.isObject
529 * Determines if a reference is an `Object`. Unlike `typeof` in JavaScript, `null`s are not
530 * considered to be objects. Note that JavaScript arrays are objects.
532 * @param {*} value Reference to check.
533 * @returns {boolean} True if `value` is an `Object` but not `null`.
535 function isObject(value){return value != null && typeof value === 'object';}
540 * @name angular.isString
545 * Determines if a reference is a `String`.
547 * @param {*} value Reference to check.
548 * @returns {boolean} True if `value` is a `String`.
550 function isString(value){return typeof value === 'string';}
555 * @name angular.isNumber
560 * Determines if a reference is a `Number`.
562 * @param {*} value Reference to check.
563 * @returns {boolean} True if `value` is a `Number`.
565 function isNumber(value){return typeof value === 'number';}
570 * @name angular.isDate
575 * Determines if a value is a date.
577 * @param {*} value Reference to check.
578 * @returns {boolean} True if `value` is a `Date`.
580 function isDate(value) {
581 return toString.call(value) === '[object Date]';
587 * @name angular.isArray
592 * Determines if a reference is an `Array`.
594 * @param {*} value Reference to check.
595 * @returns {boolean} True if `value` is an `Array`.
597 var isArray = (function() {
598 if (!isFunction(Array.isArray)) {
599 return function(value) {
600 return toString.call(value) === '[object Array]';
603 return Array.isArray;
608 * @name angular.isFunction
613 * Determines if a reference is a `Function`.
615 * @param {*} value Reference to check.
616 * @returns {boolean} True if `value` is a `Function`.
618 function isFunction(value){return typeof value === 'function';}
622 * Determines if a value is a regular expression object.
625 * @param {*} value Reference to check.
626 * @returns {boolean} True if `value` is a `RegExp`.
628 function isRegExp(value) {
629 return toString.call(value) === '[object RegExp]';
634 * Checks if `obj` is a window object.
637 * @param {*} obj Object to check
638 * @returns {boolean} True if `obj` is a window obj.
640 function isWindow(obj) {
641 return obj && obj.document && obj.location && obj.alert && obj.setInterval;
645 function isScope(obj) {
646 return obj && obj.$evalAsync && obj.$watch;
650 function isFile(obj) {
651 return toString.call(obj) === '[object File]';
655 function isBlob(obj) {
656 return toString.call(obj) === '[object Blob]';
660 function isBoolean(value) {
661 return typeof value === 'boolean';
665 function isPromiseLike(obj) {
666 return obj && isFunction(obj.then);
670 var trim = (function() {
671 // native trim is way faster: http://jsperf.com/angular-trim-test
672 // but IE doesn't have it... :-(
673 // TODO: we should move this into IE/ES5 polyfill
674 if (!String.prototype.trim) {
675 return function(value) {
676 return isString(value) ? value.replace(/^\s\s*/, '').replace(/\s\s*$/, '') : value;
679 return function(value) {
680 return isString(value) ? value.trim() : value;
687 * @name angular.isElement
692 * Determines if a reference is a DOM element (or wrapped jQuery element).
694 * @param {*} value Reference to check.
695 * @returns {boolean} True if `value` is a DOM element (or wrapped jQuery element).
697 function isElement(node) {
699 (node.nodeName // we are a direct element
700 || (node.prop && node.attr && node.find))); // we have an on and find method part of jQuery API
704 * @param str 'key1,key2,...'
705 * @returns {object} in the form of {key1:true, key2:true, ...}
707 function makeMap(str) {
708 var obj = {}, items = str.split(","), i;
709 for ( i = 0; i < items.length; i++ )
710 obj[ items[i] ] = true;
716 nodeName_ = function(element) {
717 element = element.nodeName ? element : element[0];
718 return (element.scopeName && element.scopeName != 'HTML')
719 ? uppercase(element.scopeName + ':' + element.nodeName) : element.nodeName;
722 nodeName_ = function(element) {
723 return element.nodeName ? element.nodeName : element[0].nodeName;
728 function map(obj, iterator, context) {
730 forEach(obj, function(value, index, list) {
731 results.push(iterator.call(context, value, index, list));
739 * Determines the number of elements in an array, the number of properties an object has, or
740 * the length of a string.
742 * Note: This function is used to augment the Object type in Angular expressions. See
743 * {@link angular.Object} for more information about Angular arrays.
745 * @param {Object|Array|string} obj Object, array, or string to inspect.
746 * @param {boolean} [ownPropsOnly=false] Count only "own" properties in an object
747 * @returns {number} The size of `obj` or `0` if `obj` is neither an object nor an array.
749 function size(obj, ownPropsOnly) {
752 if (isArray(obj) || isString(obj)) {
754 } else if (isObject(obj)) {
756 if (!ownPropsOnly || obj.hasOwnProperty(key))
764 function includes(array, obj) {
765 return indexOf(array, obj) != -1;
768 function indexOf(array, obj) {
769 if (array.indexOf) return array.indexOf(obj);
771 for (var i = 0; i < array.length; i++) {
772 if (obj === array[i]) return i;
777 function arrayRemove(array, value) {
778 var index = indexOf(array, value);
780 array.splice(index, 1);
784 function isLeafNode (node) {
786 switch (node.nodeName) {
803 * Creates a deep copy of `source`, which should be an object or an array.
805 * * If no destination is supplied, a copy of the object or array is created.
806 * * If a destination is provided, all of its elements (for array) or properties (for objects)
807 * are deleted and then all elements/properties from the source are copied to it.
808 * * If `source` is not an object or array (inc. `null` and `undefined`), `source` is returned.
809 * * If `source` is identical to 'destination' an exception will be thrown.
811 * @param {*} source The source that will be used to make a copy.
812 * Can be any type, including primitives, `null`, and `undefined`.
813 * @param {(Object|Array)=} destination Destination into which the source is copied. If
814 * provided, must be of the same type as `source`.
815 * @returns {*} The copy or updated `destination`, if `destination` was specified.
818 <example module="copyExample">
819 <file name="index.html">
820 <div ng-controller="ExampleController">
821 <form novalidate class="simple-form">
822 Name: <input type="text" ng-model="user.name" /><br />
823 E-mail: <input type="email" ng-model="user.email" /><br />
824 Gender: <input type="radio" ng-model="user.gender" value="male" />male
825 <input type="radio" ng-model="user.gender" value="female" />female<br />
826 <button ng-click="reset()">RESET</button>
827 <button ng-click="update(user)">SAVE</button>
829 <pre>form = {{user | json}}</pre>
830 <pre>master = {{master | json}}</pre>
834 angular.module('copyExample', [])
835 .controller('ExampleController', ['$scope', function($scope) {
838 $scope.update = function(user) {
839 // Example with 1 argument
840 $scope.master= angular.copy(user);
843 $scope.reset = function() {
844 // Example with 2 arguments
845 angular.copy($scope.master, $scope.user);
854 function copy(source, destination, stackSource, stackDest) {
855 if (isWindow(source) || isScope(source)) {
856 throw ngMinErr('cpws',
857 "Can't copy! Making copies of Window or Scope instances is not supported.");
861 destination = source;
863 if (isArray(source)) {
864 destination = copy(source, [], stackSource, stackDest);
865 } else if (isDate(source)) {
866 destination = new Date(source.getTime());
867 } else if (isRegExp(source)) {
868 destination = new RegExp(source.source, source.toString().match(/[^\/]*$/)[0]);
869 destination.lastIndex = source.lastIndex;
870 } else if (isObject(source)) {
871 destination = copy(source, {}, stackSource, stackDest);
875 if (source === destination) throw ngMinErr('cpi',
876 "Can't copy! Source and destination are identical.");
878 stackSource = stackSource || [];
879 stackDest = stackDest || [];
881 if (isObject(source)) {
882 var index = indexOf(stackSource, source);
883 if (index !== -1) return stackDest[index];
885 stackSource.push(source);
886 stackDest.push(destination);
890 if (isArray(source)) {
891 destination.length = 0;
892 for ( var i = 0; i < source.length; i++) {
893 result = copy(source[i], null, stackSource, stackDest);
894 if (isObject(source[i])) {
895 stackSource.push(source[i]);
896 stackDest.push(result);
898 destination.push(result);
901 var h = destination.$$hashKey;
902 if (isArray(destination)) {
903 destination.length = 0;
905 forEach(destination, function(value, key) {
906 delete destination[key];
909 for ( var key in source) {
910 result = copy(source[key], null, stackSource, stackDest);
911 if (isObject(source[key])) {
912 stackSource.push(source[key]);
913 stackDest.push(result);
915 destination[key] = result;
917 setHashKey(destination,h);
925 * Creates a shallow copy of an object, an array or a primitive
927 function shallowCopy(src, dst) {
931 for ( var i = 0; i < src.length; i++) {
934 } else if (isObject(src)) {
937 for (var key in src) {
938 if (hasOwnProperty.call(src, key) && !(key.charAt(0) === '$' && key.charAt(1) === '$')) {
950 * @name angular.equals
955 * Determines if two objects or two values are equivalent. Supports value types, regular
956 * expressions, arrays and objects.
958 * Two objects or values are considered equivalent if at least one of the following is true:
960 * * Both objects or values pass `===` comparison.
961 * * Both objects or values are of the same type and all of their properties are equal by
962 * comparing them with `angular.equals`.
963 * * Both values are NaN. (In JavaScript, NaN == NaN => false. But we consider two NaN as equal)
964 * * Both values represent the same regular expression (In JavaScript,
965 * /abc/ == /abc/ => false. But we consider two regular expressions as equal when their textual
966 * representation matches).
968 * During a property comparison, properties of `function` type and properties with names
969 * that begin with `$` are ignored.
971 * Scope and DOMWindow objects are being compared only by identify (`===`).
973 * @param {*} o1 Object or value to compare.
974 * @param {*} o2 Object or value to compare.
975 * @returns {boolean} True if arguments are equal.
977 function equals(o1, o2) {
978 if (o1 === o2) return true;
979 if (o1 === null || o2 === null) return false;
980 if (o1 !== o1 && o2 !== o2) return true; // NaN === NaN
981 var t1 = typeof o1, t2 = typeof o2, length, key, keySet;
983 if (t1 == 'object') {
985 if (!isArray(o2)) return false;
986 if ((length = o1.length) == o2.length) {
987 for(key=0; key<length; key++) {
988 if (!equals(o1[key], o2[key])) return false;
992 } else if (isDate(o1)) {
993 if (!isDate(o2)) return false;
994 return (isNaN(o1.getTime()) && isNaN(o2.getTime())) || (o1.getTime() === o2.getTime());
995 } else if (isRegExp(o1) && isRegExp(o2)) {
996 return o1.toString() == o2.toString();
998 if (isScope(o1) || isScope(o2) || isWindow(o1) || isWindow(o2) || isArray(o2)) return false;
1001 if (key.charAt(0) === '$' || isFunction(o1[key])) continue;
1002 if (!equals(o1[key], o2[key])) return false;
1006 if (!keySet.hasOwnProperty(key) &&
1007 key.charAt(0) !== '$' &&
1008 o2[key] !== undefined &&
1009 !isFunction(o2[key])) return false;
1018 var csp = function() {
1019 if (isDefined(csp.isActive_)) return csp.isActive_;
1021 var active = !!(document.querySelector('[ng-csp]') ||
1022 document.querySelector('[data-ng-csp]'));
1026 /* jshint -W031, -W054 */
1028 /* jshint +W031, +W054 */
1034 return (csp.isActive_ = active);
1039 function concat(array1, array2, index) {
1040 return array1.concat(slice.call(array2, index));
1043 function sliceArgs(args, startIndex) {
1044 return slice.call(args, startIndex || 0);
1051 * @name angular.bind
1056 * Returns a function which calls function `fn` bound to `self` (`self` becomes the `this` for
1057 * `fn`). You can supply optional `args` that are prebound to the function. This feature is also
1058 * known as [partial application](http://en.wikipedia.org/wiki/Partial_application), as
1059 * distinguished from [function currying](http://en.wikipedia.org/wiki/Currying#Contrast_with_partial_function_application).
1061 * @param {Object} self Context which `fn` should be evaluated in.
1062 * @param {function()} fn Function to be bound.
1063 * @param {...*} args Optional arguments to be prebound to the `fn` function call.
1064 * @returns {function()} Function that wraps the `fn` with all the specified bindings.
1067 function bind(self, fn) {
1068 var curryArgs = arguments.length > 2 ? sliceArgs(arguments, 2) : [];
1069 if (isFunction(fn) && !(fn instanceof RegExp)) {
1070 return curryArgs.length
1072 return arguments.length
1073 ? fn.apply(self, curryArgs.concat(slice.call(arguments, 0)))
1074 : fn.apply(self, curryArgs);
1077 return arguments.length
1078 ? fn.apply(self, arguments)
1082 // in IE, native methods are not functions so they cannot be bound (note: they don't need to be)
1088 function toJsonReplacer(key, value) {
1091 if (typeof key === 'string' && key.charAt(0) === '$') {
1093 } else if (isWindow(value)) {
1095 } else if (value && document === value) {
1097 } else if (isScope(value)) {
1107 * @name angular.toJson
1112 * Serializes input into a JSON-formatted string. Properties with leading $ characters will be
1113 * stripped since angular uses this notation internally.
1115 * @param {Object|Array|Date|string|number} obj Input to be serialized into JSON.
1116 * @param {boolean=} pretty If set to true, the JSON output will contain newlines and whitespace.
1117 * @returns {string|undefined} JSON-ified string representing `obj`.
1119 function toJson(obj, pretty) {
1120 if (typeof obj === 'undefined') return undefined;
1121 return JSON.stringify(obj, toJsonReplacer, pretty ? ' ' : null);
1127 * @name angular.fromJson
1132 * Deserializes a JSON string.
1134 * @param {string} json JSON string to deserialize.
1135 * @returns {Object|Array|string|number} Deserialized thingy.
1137 function fromJson(json) {
1138 return isString(json)
1144 function toBoolean(value) {
1145 if (typeof value === 'function') {
1147 } else if (value && value.length !== 0) {
1148 var v = lowercase("" + value);
1149 value = !(v == 'f' || v == '0' || v == 'false' || v == 'no' || v == 'n' || v == '[]');
1157 * @returns {string} Returns the string representation of the element.
1159 function startingTag(element) {
1160 element = jqLite(element).clone();
1162 // turns out IE does not let you set .html() on elements which
1163 // are not allowed to have children. So we just ignore it.
1166 // As Per DOM Standards
1168 var elemHtml = jqLite('<div>').append(element).html();
1170 return element[0].nodeType === TEXT_NODE ? lowercase(elemHtml) :
1172 match(/^(<[^>]+>)/)[1].
1173 replace(/^<([\w\-]+)/, function(match, nodeName) { return '<' + lowercase(nodeName); });
1175 return lowercase(elemHtml);
1181 /////////////////////////////////////////////////
1184 * Tries to decode the URI component without throwing an exception.
1187 * @param str value potential URI component to check.
1188 * @returns {boolean} True if `value` can be decoded
1189 * with the decodeURIComponent function.
1191 function tryDecodeURIComponent(value) {
1193 return decodeURIComponent(value);
1195 // Ignore any invalid uri component
1201 * Parses an escaped url query string into key-value pairs.
1202 * @returns {Object.<string,boolean|Array>}
1204 function parseKeyValue(/**string*/keyValue) {
1205 var obj = {}, key_value, key;
1206 forEach((keyValue || "").split('&'), function(keyValue) {
1208 key_value = keyValue.replace(/\+/g,'%20').split('=');
1209 key = tryDecodeURIComponent(key_value[0]);
1210 if ( isDefined(key) ) {
1211 var val = isDefined(key_value[1]) ? tryDecodeURIComponent(key_value[1]) : true;
1212 if (!hasOwnProperty.call(obj, key)) {
1214 } else if(isArray(obj[key])) {
1217 obj[key] = [obj[key],val];
1225 function toKeyValue(obj) {
1227 forEach(obj, function(value, key) {
1228 if (isArray(value)) {
1229 forEach(value, function(arrayValue) {
1230 parts.push(encodeUriQuery(key, true) +
1231 (arrayValue === true ? '' : '=' + encodeUriQuery(arrayValue, true)));
1234 parts.push(encodeUriQuery(key, true) +
1235 (value === true ? '' : '=' + encodeUriQuery(value, true)));
1238 return parts.length ? parts.join('&') : '';
1243 * We need our custom method because encodeURIComponent is too aggressive and doesn't follow
1244 * http://www.ietf.org/rfc/rfc3986.txt with regards to the character set (pchar) allowed in path
1247 * pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
1248 * pct-encoded = "%" HEXDIG HEXDIG
1249 * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
1250 * sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
1251 * / "*" / "+" / "," / ";" / "="
1253 function encodeUriSegment(val) {
1254 return encodeUriQuery(val, true).
1255 replace(/%26/gi, '&').
1256 replace(/%3D/gi, '=').
1257 replace(/%2B/gi, '+');
1262 * This method is intended for encoding *key* or *value* parts of query component. We need a custom
1263 * method because encodeURIComponent is too aggressive and encodes stuff that doesn't have to be
1264 * encoded per http://tools.ietf.org/html/rfc3986:
1265 * query = *( pchar / "/" / "?" )
1266 * pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
1267 * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
1268 * pct-encoded = "%" HEXDIG HEXDIG
1269 * sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
1270 * / "*" / "+" / "," / ";" / "="
1272 function encodeUriQuery(val, pctEncodeSpaces) {
1273 return encodeURIComponent(val).
1274 replace(/%40/gi, '@').
1275 replace(/%3A/gi, ':').
1276 replace(/%24/g, '$').
1277 replace(/%2C/gi, ',').
1278 replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
1288 * @param {angular.Module} ngApp an optional application
1289 * {@link angular.module module} name to load.
1293 * Use this directive to **auto-bootstrap** an AngularJS application. The `ngApp` directive
1294 * designates the **root element** of the application and is typically placed near the root element
1295 * of the page - e.g. on the `<body>` or `<html>` tags.
1297 * Only one AngularJS application can be auto-bootstrapped per HTML document. The first `ngApp`
1298 * found in the document will be used to define the root element to auto-bootstrap as an
1299 * application. To run multiple applications in an HTML document you must manually bootstrap them using
1300 * {@link angular.bootstrap} instead. AngularJS applications cannot be nested within each other.
1302 * You can specify an **AngularJS module** to be used as the root module for the application. This
1303 * module will be loaded into the {@link auto.$injector} when the application is bootstrapped and
1304 * should contain the application code needed or have dependencies on other modules that will
1305 * contain the code. See {@link angular.module} for more information.
1307 * In the example below if the `ngApp` directive were not placed on the `html` element then the
1308 * document would not be compiled, the `AppController` would not be instantiated and the `{{ a+b }}`
1309 * would not be resolved to `3`.
1311 * `ngApp` is the easiest, and most common, way to bootstrap an application.
1313 <example module="ngAppDemo">
1314 <file name="index.html">
1315 <div ng-controller="ngAppDemoController">
1316 I can add: {{a}} + {{b}} = {{ a+b }}
1319 <file name="script.js">
1320 angular.module('ngAppDemo', []).controller('ngAppDemoController', function($scope) {
1328 function angularInit(element, bootstrap) {
1329 var elements = [element],
1332 names = ['ng:app', 'ng-app', 'x-ng-app', 'data-ng-app'],
1333 NG_APP_CLASS_REGEXP = /\sng[:\-]app(:\s*([\w\d_]+);?)?\s/;
1335 function append(element) {
1336 element && elements.push(element);
1339 forEach(names, function(name) {
1341 append(document.getElementById(name));
1342 name = name.replace(':', '\\:');
1343 if (element.querySelectorAll) {
1344 forEach(element.querySelectorAll('.' + name), append);
1345 forEach(element.querySelectorAll('.' + name + '\\:'), append);
1346 forEach(element.querySelectorAll('[' + name + ']'), append);
1350 forEach(elements, function(element) {
1352 var className = ' ' + element.className + ' ';
1353 var match = NG_APP_CLASS_REGEXP.exec(className);
1355 appElement = element;
1356 module = (match[2] || '').replace(/\s+/g, ',');
1358 forEach(element.attributes, function(attr) {
1359 if (!appElement && names[attr.name]) {
1360 appElement = element;
1361 module = attr.value;
1368 bootstrap(appElement, module ? [module] : []);
1374 * @name angular.bootstrap
1377 * Use this function to manually start up angular application.
1379 * See: {@link guide/bootstrap Bootstrap}
1381 * Note that ngScenario-based end-to-end tests cannot use this function to bootstrap manually.
1382 * They must use {@link ng.directive:ngApp ngApp}.
1384 * Angular will detect if it has been loaded into the browser more than once and only allow the
1385 * first loaded script to be bootstrapped and will report a warning to the browser console for
1386 * each of the subsequent scripts. This prevents strange results in applications, where otherwise
1387 * multiple instances of Angular try to work on the DOM.
1389 * <example name="multi-bootstrap" module="multi-bootstrap">
1390 * <file name="index.html">
1391 * <script src="../../../angular.js"></script>
1392 * <div ng-controller="BrokenTable">
1395 * <th ng-repeat="heading in headings">{{heading}}</th>
1397 * <tr ng-repeat="filling in fillings">
1398 * <td ng-repeat="fill in filling">{{fill}}</td>
1403 * <file name="controller.js">
1404 * var app = angular.module('multi-bootstrap', [])
1406 * .controller('BrokenTable', function($scope) {
1407 * $scope.headings = ['One', 'Two', 'Three'];
1408 * $scope.fillings = [[1, 2, 3], ['A', 'B', 'C'], [7, 8, 9]];
1411 * <file name="protractor.js" type="protractor">
1412 * it('should only insert one table cell for each item in $scope.fillings', function() {
1413 * expect(element.all(by.css('td')).count())
1419 * @param {DOMElement} element DOM element which is the root of angular application.
1420 * @param {Array<String|Function|Array>=} modules an array of modules to load into the application.
1421 * Each item in the array should be the name of a predefined module or a (DI annotated)
1422 * function that will be invoked by the injector as a run block.
1423 * See: {@link angular.module modules}
1424 * @returns {auto.$injector} Returns the newly created injector for this app.
1426 function bootstrap(element, modules) {
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);
1443 modules.unshift('ng');
1444 var injector = createInjector(modules);
1445 injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector', '$animate',
1446 function(scope, element, compile, injector, animate) {
1447 scope.$apply(function() {
1448 element.data('$injector', injector);
1449 compile(element)(scope);
1456 var NG_DEFER_BOOTSTRAP = /^NG_DEFER_BOOTSTRAP!/;
1458 if (window && !NG_DEFER_BOOTSTRAP.test(window.name)) {
1459 return doBootstrap();
1462 window.name = window.name.replace(NG_DEFER_BOOTSTRAP, '');
1463 angular.resumeBootstrap = function(extraModules) {
1464 forEach(extraModules, function(module) {
1465 modules.push(module);
1471 var SNAKE_CASE_REGEXP = /[A-Z]/g;
1472 function snake_case(name, separator) {
1473 separator = separator || '_';
1474 return name.replace(SNAKE_CASE_REGEXP, function(letter, pos) {
1475 return (pos ? separator : '') + letter.toLowerCase();
1479 function bindJQuery() {
1480 // bind to jQuery if present;
1481 jQuery = window.jQuery;
1482 // Use jQuery if it exists with proper functionality, otherwise default to us.
1483 // Angular 1.2+ requires jQuery 1.7.1+ for on()/off() support.
1484 if (jQuery && jQuery.fn.on) {
1487 scope: JQLitePrototype.scope,
1488 isolateScope: JQLitePrototype.isolateScope,
1489 controller: JQLitePrototype.controller,
1490 injector: JQLitePrototype.injector,
1491 inheritedData: JQLitePrototype.inheritedData
1493 // Method signature:
1494 // jqLitePatchJQueryRemove(name, dispatchThis, filterElems, getterIfNoArguments)
1495 jqLitePatchJQueryRemove('remove', true, true, false);
1496 jqLitePatchJQueryRemove('empty', false, false, false);
1497 jqLitePatchJQueryRemove('html', false, false, true);
1501 angular.element = jqLite;
1505 * throw error if the argument is falsy.
1507 function assertArg(arg, name, reason) {
1509 throw ngMinErr('areq', "Argument '{0}' is {1}", (name || '?'), (reason || "required"));
1514 function assertArgFn(arg, name, acceptArrayAnnotation) {
1515 if (acceptArrayAnnotation && isArray(arg)) {
1516 arg = arg[arg.length - 1];
1519 assertArg(isFunction(arg), name, 'not a function, got ' +
1520 (arg && typeof arg === 'object' ? arg.constructor.name || 'Object' : typeof arg));
1525 * throw error if the name given is hasOwnProperty
1526 * @param {String} name the name to test
1527 * @param {String} context the context in which the name is used, such as module or directive
1529 function assertNotHasOwnProperty(name, context) {
1530 if (name === 'hasOwnProperty') {
1531 throw ngMinErr('badname', "hasOwnProperty is not a valid {0} name", context);
1536 * Return the value accessible from the object by path. Any undefined traversals are ignored
1537 * @param {Object} obj starting object
1538 * @param {String} path path to traverse
1539 * @param {boolean} [bindFnToScope=true]
1540 * @returns {Object} value as accessible by path
1542 //TODO(misko): this function needs to be removed
1543 function getter(obj, path, bindFnToScope) {
1544 if (!path) return obj;
1545 var keys = path.split('.');
1547 var lastInstance = obj;
1548 var len = keys.length;
1550 for (var i = 0; i < len; i++) {
1553 obj = (lastInstance = obj)[key];
1556 if (!bindFnToScope && isFunction(obj)) {
1557 return bind(lastInstance, obj);
1563 * Return the DOM siblings between the first and last node in the given array.
1564 * @param {Array} array like object
1565 * @returns {DOMElement} object containing the elements
1567 function getBlockElements(nodes) {
1568 var startNode = nodes[0],
1569 endNode = nodes[nodes.length - 1];
1570 if (startNode === endNode) {
1571 return jqLite(startNode);
1574 var element = startNode;
1575 var elements = [element];
1578 element = element.nextSibling;
1579 if (!element) break;
1580 elements.push(element);
1581 } while (element !== endNode);
1583 return jqLite(elements);
1588 * @name angular.Module
1592 * Interface for configuring angular {@link angular.module modules}.
1595 function setupModuleLoader(window) {
1597 var $injectorMinErr = minErr('$injector');
1598 var ngMinErr = minErr('ng');
1600 function ensure(obj, name, factory) {
1601 return obj[name] || (obj[name] = factory());
1604 var angular = ensure(window, 'angular', Object);
1606 // We need to expose `angular.$$minErr` to modules such as `ngResource` that reference it during bootstrap
1607 angular.$$minErr = angular.$$minErr || minErr;
1609 return ensure(angular, 'module', function() {
1610 /** @type {Object.<string, angular.Module>} */
1615 * @name angular.module
1619 * The `angular.module` is a global place for creating, registering and retrieving Angular
1621 * All modules (angular core or 3rd party) that should be available to an application must be
1622 * registered using this mechanism.
1624 * When passed two or more arguments, a new module is created. If passed only one argument, an
1625 * existing module (the name passed as the first argument to `module`) is retrieved.
1630 * A module is a collection of services, directives, controllers, filters, and configuration information.
1631 * `angular.module` is used to configure the {@link auto.$injector $injector}.
1634 * // Create a new module
1635 * var myModule = angular.module('myModule', []);
1637 * // register a new service
1638 * myModule.value('appName', 'MyCoolApp');
1640 * // configure existing services inside initialization blocks.
1641 * myModule.config(['$locationProvider', function($locationProvider) {
1642 * // Configure existing providers
1643 * $locationProvider.hashPrefix('!');
1647 * Then you can create an injector and load your modules like this:
1650 * var injector = angular.injector(['ng', 'myModule'])
1653 * However it's more likely that you'll just use
1654 * {@link ng.directive:ngApp ngApp} or
1655 * {@link angular.bootstrap} to simplify this process for you.
1657 * @param {!string} name The name of the module to create or retrieve.
1658 * @param {!Array.<string>=} requires If specified then new module is being created. If
1659 * unspecified then the module is being retrieved for further configuration.
1660 * @param {Function=} configFn Optional configuration function for the module. Same as
1661 * {@link angular.Module#config Module#config()}.
1662 * @returns {module} new module with the {@link angular.Module} api.
1664 return function module(name, requires, configFn) {
1665 var assertNotHasOwnProperty = function(name, context) {
1666 if (name === 'hasOwnProperty') {
1667 throw ngMinErr('badname', 'hasOwnProperty is not a valid {0} name', context);
1671 assertNotHasOwnProperty(name, 'module');
1672 if (requires && modules.hasOwnProperty(name)) {
1673 modules[name] = null;
1675 return ensure(modules, name, function() {
1677 throw $injectorMinErr('nomod', "Module '{0}' is not available! You either misspelled " +
1678 "the module name or forgot to load it. If registering a module ensure that you " +
1679 "specify the dependencies as the second argument.", name);
1682 /** @type {!Array.<Array.<*>>} */
1683 var invokeQueue = [];
1685 /** @type {!Array.<Function>} */
1688 var config = invokeLater('$injector', 'invoke');
1690 /** @type {angular.Module} */
1691 var moduleInstance = {
1693 _invokeQueue: invokeQueue,
1694 _runBlocks: runBlocks,
1698 * @name angular.Module#requires
1702 * Holds the list of modules which the injector will load before the current module is
1709 * @name angular.Module#name
1713 * Name of the module.
1720 * @name angular.Module#provider
1722 * @param {string} name service name
1723 * @param {Function} providerType Construction function for creating new instance of the
1726 * See {@link auto.$provide#provider $provide.provider()}.
1728 provider: invokeLater('$provide', 'provider'),
1732 * @name angular.Module#factory
1734 * @param {string} name service name
1735 * @param {Function} providerFunction Function for creating new instance of the service.
1737 * See {@link auto.$provide#factory $provide.factory()}.
1739 factory: invokeLater('$provide', 'factory'),
1743 * @name angular.Module#service
1745 * @param {string} name service name
1746 * @param {Function} constructor A constructor function that will be instantiated.
1748 * See {@link auto.$provide#service $provide.service()}.
1750 service: invokeLater('$provide', 'service'),
1754 * @name angular.Module#value
1756 * @param {string} name service name
1757 * @param {*} object Service instance object.
1759 * See {@link auto.$provide#value $provide.value()}.
1761 value: invokeLater('$provide', 'value'),
1765 * @name angular.Module#constant
1767 * @param {string} name constant name
1768 * @param {*} object Constant value.
1770 * Because the constant are fixed, they get applied before other provide methods.
1771 * See {@link auto.$provide#constant $provide.constant()}.
1773 constant: invokeLater('$provide', 'constant', 'unshift'),
1777 * @name angular.Module#animation
1779 * @param {string} name animation name
1780 * @param {Function} animationFactory Factory function for creating new instance of an
1784 * **NOTE**: animations take effect only if the **ngAnimate** module is loaded.
1787 * Defines an animation hook that can be later used with
1788 * {@link ngAnimate.$animate $animate} service and directives that use this service.
1791 * module.animation('.animation-name', function($inject1, $inject2) {
1793 * eventName : function(element, done) {
1794 * //code to run the animation
1795 * //once complete, then run done()
1796 * return function cancellationFunction(element) {
1797 * //code to cancel the animation
1804 * See {@link ngAnimate.$animateProvider#register $animateProvider.register()} and
1805 * {@link ngAnimate ngAnimate module} for more information.
1807 animation: invokeLater('$animateProvider', 'register'),
1811 * @name angular.Module#filter
1813 * @param {string} name Filter name.
1814 * @param {Function} filterFactory Factory function for creating new instance of filter.
1816 * See {@link ng.$filterProvider#register $filterProvider.register()}.
1818 filter: invokeLater('$filterProvider', 'register'),
1822 * @name angular.Module#controller
1824 * @param {string|Object} name Controller name, or an object map of controllers where the
1825 * keys are the names and the values are the constructors.
1826 * @param {Function} constructor Controller constructor function.
1828 * See {@link ng.$controllerProvider#register $controllerProvider.register()}.
1830 controller: invokeLater('$controllerProvider', 'register'),
1834 * @name angular.Module#directive
1836 * @param {string|Object} name Directive name, or an object map of directives where the
1837 * keys are the names and the values are the factories.
1838 * @param {Function} directiveFactory Factory function for creating new instance of
1841 * See {@link ng.$compileProvider#directive $compileProvider.directive()}.
1843 directive: invokeLater('$compileProvider', 'directive'),
1847 * @name angular.Module#config
1849 * @param {Function} configFn Execute this function on module load. Useful for service
1852 * Use this method to register work which needs to be performed on module loading.
1853 * For more about how to configure services, see
1854 * {@link providers#providers_provider-recipe Provider Recipe}.
1860 * @name angular.Module#run
1862 * @param {Function} initializationFn Execute this function after injector creation.
1863 * Useful for application initialization.
1865 * Use this method to register work which should be performed when the injector is done
1866 * loading all modules.
1868 run: function(block) {
1869 runBlocks.push(block);
1878 return moduleInstance;
1881 * @param {string} provider
1882 * @param {string} method
1883 * @param {String=} insertMethod
1884 * @returns {angular.Module}
1886 function invokeLater(provider, method, insertMethod) {
1888 invokeQueue[insertMethod || 'push']([provider, method, arguments]);
1889 return moduleInstance;
1898 /* global angularModule: true,
1904 htmlAnchorDirective,
1913 ngBindHtmlDirective,
1914 ngBindTemplateDirective,
1916 ngClassEvenDirective,
1917 ngClassOddDirective,
1920 ngControllerDirective,
1925 ngIncludeFillContentDirective,
1927 ngNonBindableDirective,
1928 ngPluralizeDirective,
1933 ngSwitchWhenDirective,
1934 ngSwitchDefaultDirective,
1936 ngTranscludeDirective,
1943 ngAttributeAliasDirectives,
1946 $AnchorScrollProvider,
1949 $CacheFactoryProvider,
1950 $ControllerProvider,
1952 $ExceptionHandlerProvider,
1954 $InterpolateProvider,
1957 $HttpBackendProvider,
1963 $$SanitizeUriProvider,
1965 $SceDelegateProvider,
1967 $TemplateCacheProvider,
1970 $$AsyncCallbackProvider,
1977 * @name angular.version
1980 * An object that contains information about the current AngularJS version. This object has the
1981 * following properties:
1983 * - `full` – `{string}` – Full version string, such as "0.9.18".
1984 * - `major` – `{number}` – Major version number, such as "0".
1985 * - `minor` – `{number}` – Minor version number, such as "9".
1986 * - `dot` – `{number}` – Dot version number, such as "18".
1987 * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat".
1990 full: '1.2.25', // all of these placeholder strings will be replaced by grunt's
1991 major: 1, // package task
1994 codeName: 'hypnotic-gesticulation'
1998 function publishExternalAPI(angular){
2000 'bootstrap': bootstrap,
2006 'injector': createInjector,
2010 'fromJson': fromJson,
2011 'identity': identity,
2012 'isUndefined': isUndefined,
2013 'isDefined': isDefined,
2014 'isString': isString,
2015 'isFunction': isFunction,
2016 'isObject': isObject,
2017 'isNumber': isNumber,
2018 'isElement': isElement,
2022 'lowercase': lowercase,
2023 'uppercase': uppercase,
2024 'callbacks': {counter: 0},
2029 angularModule = setupModuleLoader(window);
2031 angularModule('ngLocale');
2033 angularModule('ngLocale', []).provider('$locale', $LocaleProvider);
2036 angularModule('ng', ['ngLocale'], ['$provide',
2037 function ngModule($provide) {
2038 // $$sanitizeUriProvider needs to be before $compileProvider as it is used by it.
2040 $$sanitizeUri: $$SanitizeUriProvider
2042 $provide.provider('$compile', $CompileProvider).
2044 a: htmlAnchorDirective,
2045 input: inputDirective,
2046 textarea: inputDirective,
2047 form: formDirective,
2048 script: scriptDirective,
2049 select: selectDirective,
2050 style: styleDirective,
2051 option: optionDirective,
2052 ngBind: ngBindDirective,
2053 ngBindHtml: ngBindHtmlDirective,
2054 ngBindTemplate: ngBindTemplateDirective,
2055 ngClass: ngClassDirective,
2056 ngClassEven: ngClassEvenDirective,
2057 ngClassOdd: ngClassOddDirective,
2058 ngCloak: ngCloakDirective,
2059 ngController: ngControllerDirective,
2060 ngForm: ngFormDirective,
2061 ngHide: ngHideDirective,
2062 ngIf: ngIfDirective,
2063 ngInclude: ngIncludeDirective,
2064 ngInit: ngInitDirective,
2065 ngNonBindable: ngNonBindableDirective,
2066 ngPluralize: ngPluralizeDirective,
2067 ngRepeat: ngRepeatDirective,
2068 ngShow: ngShowDirective,
2069 ngStyle: ngStyleDirective,
2070 ngSwitch: ngSwitchDirective,
2071 ngSwitchWhen: ngSwitchWhenDirective,
2072 ngSwitchDefault: ngSwitchDefaultDirective,
2073 ngOptions: ngOptionsDirective,
2074 ngTransclude: ngTranscludeDirective,
2075 ngModel: ngModelDirective,
2076 ngList: ngListDirective,
2077 ngChange: ngChangeDirective,
2078 required: requiredDirective,
2079 ngRequired: requiredDirective,
2080 ngValue: ngValueDirective
2083 ngInclude: ngIncludeFillContentDirective
2085 directive(ngAttributeAliasDirectives).
2086 directive(ngEventDirectives);
2088 $anchorScroll: $AnchorScrollProvider,
2089 $animate: $AnimateProvider,
2090 $browser: $BrowserProvider,
2091 $cacheFactory: $CacheFactoryProvider,
2092 $controller: $ControllerProvider,
2093 $document: $DocumentProvider,
2094 $exceptionHandler: $ExceptionHandlerProvider,
2095 $filter: $FilterProvider,
2096 $interpolate: $InterpolateProvider,
2097 $interval: $IntervalProvider,
2098 $http: $HttpProvider,
2099 $httpBackend: $HttpBackendProvider,
2100 $location: $LocationProvider,
2102 $parse: $ParseProvider,
2103 $rootScope: $RootScopeProvider,
2106 $sceDelegate: $SceDelegateProvider,
2107 $sniffer: $SnifferProvider,
2108 $templateCache: $TemplateCacheProvider,
2109 $timeout: $TimeoutProvider,
2110 $window: $WindowProvider,
2111 $$rAF: $$RAFProvider,
2112 $$asyncCallback : $$AsyncCallbackProvider
2118 /* global JQLitePrototype: true,
2119 addEventListenerFn: true,
2120 removeEventListenerFn: true,
2124 //////////////////////////////////
2126 //////////////////////////////////
2130 * @name angular.element
2135 * Wraps a raw DOM element or HTML string as a [jQuery](http://jquery.com) element.
2137 * If jQuery is available, `angular.element` is an alias for the
2138 * [jQuery](http://api.jquery.com/jQuery/) function. If jQuery is not available, `angular.element`
2139 * delegates to Angular's built-in subset of jQuery, called "jQuery lite" or "jqLite."
2141 * <div class="alert alert-success">jqLite is a tiny, API-compatible subset of jQuery that allows
2142 * Angular to manipulate the DOM in a cross-browser compatible way. **jqLite** implements only the most
2143 * commonly needed functionality with the goal of having a very small footprint.</div>
2145 * To use jQuery, simply load it before `DOMContentLoaded` event fired.
2147 * <div class="alert">**Note:** all element references in Angular are always wrapped with jQuery or
2148 * jqLite; they are never raw DOM references.</div>
2150 * ## Angular's jqLite
2151 * jqLite provides only the following jQuery methods:
2153 * - [`addClass()`](http://api.jquery.com/addClass/)
2154 * - [`after()`](http://api.jquery.com/after/)
2155 * - [`append()`](http://api.jquery.com/append/)
2156 * - [`attr()`](http://api.jquery.com/attr/)
2157 * - [`bind()`](http://api.jquery.com/bind/) - Does not support namespaces, selectors or eventData
2158 * - [`children()`](http://api.jquery.com/children/) - Does not support selectors
2159 * - [`clone()`](http://api.jquery.com/clone/)
2160 * - [`contents()`](http://api.jquery.com/contents/)
2161 * - [`css()`](http://api.jquery.com/css/)
2162 * - [`data()`](http://api.jquery.com/data/)
2163 * - [`empty()`](http://api.jquery.com/empty/)
2164 * - [`eq()`](http://api.jquery.com/eq/)
2165 * - [`find()`](http://api.jquery.com/find/) - Limited to lookups by tag name
2166 * - [`hasClass()`](http://api.jquery.com/hasClass/)
2167 * - [`html()`](http://api.jquery.com/html/)
2168 * - [`next()`](http://api.jquery.com/next/) - Does not support selectors
2169 * - [`on()`](http://api.jquery.com/on/) - Does not support namespaces, selectors or eventData
2170 * - [`off()`](http://api.jquery.com/off/) - Does not support namespaces or selectors
2171 * - [`one()`](http://api.jquery.com/one/) - Does not support namespaces or selectors
2172 * - [`parent()`](http://api.jquery.com/parent/) - Does not support selectors
2173 * - [`prepend()`](http://api.jquery.com/prepend/)
2174 * - [`prop()`](http://api.jquery.com/prop/)
2175 * - [`ready()`](http://api.jquery.com/ready/)
2176 * - [`remove()`](http://api.jquery.com/remove/)
2177 * - [`removeAttr()`](http://api.jquery.com/removeAttr/)
2178 * - [`removeClass()`](http://api.jquery.com/removeClass/)
2179 * - [`removeData()`](http://api.jquery.com/removeData/)
2180 * - [`replaceWith()`](http://api.jquery.com/replaceWith/)
2181 * - [`text()`](http://api.jquery.com/text/)
2182 * - [`toggleClass()`](http://api.jquery.com/toggleClass/)
2183 * - [`triggerHandler()`](http://api.jquery.com/triggerHandler/) - Passes a dummy event object to handlers.
2184 * - [`unbind()`](http://api.jquery.com/unbind/) - Does not support namespaces
2185 * - [`val()`](http://api.jquery.com/val/)
2186 * - [`wrap()`](http://api.jquery.com/wrap/)
2188 * ## jQuery/jqLite Extras
2189 * Angular also provides the following additional methods and events to both jQuery and jqLite:
2192 * - `$destroy` - AngularJS intercepts all jqLite/jQuery's DOM destruction apis and fires this event
2193 * on all DOM nodes being removed. This can be used to clean up any 3rd party bindings to the DOM
2194 * element before it is removed.
2197 * - `controller(name)` - retrieves the controller of the current element or its parent. By default
2198 * retrieves controller associated with the `ngController` directive. If `name` is provided as
2199 * camelCase directive name, then the controller for this directive will be retrieved (e.g.
2201 * - `injector()` - retrieves the injector of the current element or its parent.
2202 * - `scope()` - retrieves the {@link ng.$rootScope.Scope scope} of the current
2203 * element or its parent.
2204 * - `isolateScope()` - retrieves an isolate {@link ng.$rootScope.Scope scope} if one is attached directly to the
2205 * current element. This getter should be used only on elements that contain a directive which starts a new isolate
2206 * scope. Calling `scope()` on this element always returns the original non-isolate scope.
2207 * - `inheritedData()` - same as `data()`, but walks up the DOM until a value is found or the top
2208 * parent element is reached.
2210 * @param {string|DOMElement} element HTML string or DOMElement to be wrapped into jQuery.
2211 * @returns {Object} jQuery object.
2214 JQLite.expando = 'ng339';
2216 var jqCache = JQLite.cache = {},
2218 addEventListenerFn = (window.document.addEventListener
2219 ? function(element, type, fn) {element.addEventListener(type, fn, false);}
2220 : function(element, type, fn) {element.attachEvent('on' + type, fn);}),
2221 removeEventListenerFn = (window.document.removeEventListener
2222 ? function(element, type, fn) {element.removeEventListener(type, fn, false); }
2223 : function(element, type, fn) {element.detachEvent('on' + type, fn); });
2226 * !!! This is an undocumented "private" function !!!
2228 var jqData = JQLite._data = function(node) {
2229 //jQuery always returns an object on cache miss
2230 return this.cache[node[this.expando]] || {};
2233 function jqNextId() { return ++jqId; }
2236 var SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g;
2237 var MOZ_HACK_REGEXP = /^moz([A-Z])/;
2238 var jqLiteMinErr = minErr('jqLite');
2241 * Converts snake_case to camelCase.
2242 * Also there is special case for Moz prefix starting with upper case letter.
2243 * @param name Name to normalize
2245 function camelCase(name) {
2247 replace(SPECIAL_CHARS_REGEXP, function(_, separator, letter, offset) {
2248 return offset ? letter.toUpperCase() : letter;
2250 replace(MOZ_HACK_REGEXP, 'Moz$1');
2253 /////////////////////////////////////////////
2254 // jQuery mutation patch
2256 // In conjunction with bindJQuery intercepts all jQuery's DOM destruction apis and fires a
2257 // $destroy event on all DOM nodes being removed.
2259 /////////////////////////////////////////////
2261 function jqLitePatchJQueryRemove(name, dispatchThis, filterElems, getterIfNoArguments) {
2262 var originalJqFn = jQuery.fn[name];
2263 originalJqFn = originalJqFn.$original || originalJqFn;
2264 removePatch.$original = originalJqFn;
2265 jQuery.fn[name] = removePatch;
2267 function removePatch(param) {
2269 var list = filterElems && param ? [this.filter(param)] : [this],
2270 fireEvent = dispatchThis,
2271 set, setIndex, setLength,
2272 element, childIndex, childLength, children;
2274 if (!getterIfNoArguments || param != null) {
2275 while(list.length) {
2277 for(setIndex = 0, setLength = set.length; setIndex < setLength; setIndex++) {
2278 element = jqLite(set[setIndex]);
2280 element.triggerHandler('$destroy');
2282 fireEvent = !fireEvent;
2284 for(childIndex = 0, childLength = (children = element.children()).length;
2285 childIndex < childLength;
2287 list.push(jQuery(children[childIndex]));
2292 return originalJqFn.apply(this, arguments);
2296 var SINGLE_TAG_REGEXP = /^<(\w+)\s*\/?>(?:<\/\1>|)$/;
2297 var HTML_REGEXP = /<|&#?\w+;/;
2298 var TAG_NAME_REGEXP = /<([\w:]+)/;
2299 var XHTML_TAG_REGEXP = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi;
2302 'option': [1, '<select multiple="multiple">', '</select>'],
2304 'thead': [1, '<table>', '</table>'],
2305 'col': [2, '<table><colgroup>', '</colgroup></table>'],
2306 'tr': [2, '<table><tbody>', '</tbody></table>'],
2307 'td': [3, '<table><tbody><tr>', '</tr></tbody></table>'],
2308 '_default': [0, "", ""]
2311 wrapMap.optgroup = wrapMap.option;
2312 wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
2313 wrapMap.th = wrapMap.td;
2315 function jqLiteIsTextNode(html) {
2316 return !HTML_REGEXP.test(html);
2319 function jqLiteBuildFragment(html, context) {
2320 var elem, tmp, tag, wrap,
2321 fragment = context.createDocumentFragment(),
2322 nodes = [], i, j, jj;
2324 if (jqLiteIsTextNode(html)) {
2325 // Convert non-html into a text node
2326 nodes.push(context.createTextNode(html));
2328 tmp = fragment.appendChild(context.createElement('div'));
2329 // Convert html into DOM nodes
2330 tag = (TAG_NAME_REGEXP.exec(html) || ["", ""])[1].toLowerCase();
2331 wrap = wrapMap[tag] || wrapMap._default;
2332 tmp.innerHTML = '<div> </div>' +
2333 wrap[1] + html.replace(XHTML_TAG_REGEXP, "<$1></$2>") + wrap[2];
2334 tmp.removeChild(tmp.firstChild);
2336 // Descend through wrappers to the right content
2339 tmp = tmp.lastChild;
2342 for (j=0, jj=tmp.childNodes.length; j<jj; ++j) nodes.push(tmp.childNodes[j]);
2344 tmp = fragment.firstChild;
2345 tmp.textContent = "";
2348 // Remove wrapper from fragment
2349 fragment.textContent = "";
2350 fragment.innerHTML = ""; // Clear inner HTML
2354 function jqLiteParseHTML(html, context) {
2355 context = context || document;
2358 if ((parsed = SINGLE_TAG_REGEXP.exec(html))) {
2359 return [context.createElement(parsed[1])];
2362 return jqLiteBuildFragment(html, context);
2365 /////////////////////////////////////////////
2366 function JQLite(element) {
2367 if (element instanceof JQLite) {
2370 if (isString(element)) {
2371 element = trim(element);
2373 if (!(this instanceof JQLite)) {
2374 if (isString(element) && element.charAt(0) != '<') {
2375 throw jqLiteMinErr('nosel', 'Looking up elements via selectors is not supported by jqLite! See: http://docs.angularjs.org/api/angular.element');
2377 return new JQLite(element);
2380 if (isString(element)) {
2381 jqLiteAddNodes(this, jqLiteParseHTML(element));
2382 var fragment = jqLite(document.createDocumentFragment());
2383 fragment.append(this);
2385 jqLiteAddNodes(this, element);
2389 function jqLiteClone(element) {
2390 return element.cloneNode(true);
2393 function jqLiteDealoc(element){
2394 jqLiteRemoveData(element);
2395 for ( var i = 0, children = element.childNodes || []; i < children.length; i++) {
2396 jqLiteDealoc(children[i]);
2400 function jqLiteOff(element, type, fn, unsupported) {
2401 if (isDefined(unsupported)) throw jqLiteMinErr('offargs', 'jqLite#off() does not support the `selector` argument');
2403 var events = jqLiteExpandoStore(element, 'events'),
2404 handle = jqLiteExpandoStore(element, 'handle');
2406 if (!handle) return; //no listeners registered
2408 if (isUndefined(type)) {
2409 forEach(events, function(eventHandler, type) {
2410 removeEventListenerFn(element, type, eventHandler);
2411 delete events[type];
2414 forEach(type.split(' '), function(type) {
2415 if (isUndefined(fn)) {
2416 removeEventListenerFn(element, type, events[type]);
2417 delete events[type];
2419 arrayRemove(events[type] || [], fn);
2425 function jqLiteRemoveData(element, name) {
2426 var expandoId = element.ng339,
2427 expandoStore = jqCache[expandoId];
2431 delete jqCache[expandoId].data[name];
2435 if (expandoStore.handle) {
2436 expandoStore.events.$destroy && expandoStore.handle({}, '$destroy');
2439 delete jqCache[expandoId];
2440 element.ng339 = undefined; // don't delete DOM expandos. IE and Chrome don't like it
2444 function jqLiteExpandoStore(element, key, value) {
2445 var expandoId = element.ng339,
2446 expandoStore = jqCache[expandoId || -1];
2448 if (isDefined(value)) {
2449 if (!expandoStore) {
2450 element.ng339 = expandoId = jqNextId();
2451 expandoStore = jqCache[expandoId] = {};
2453 expandoStore[key] = value;
2455 return expandoStore && expandoStore[key];
2459 function jqLiteData(element, key, value) {
2460 var data = jqLiteExpandoStore(element, 'data'),
2461 isSetter = isDefined(value),
2462 keyDefined = !isSetter && isDefined(key),
2463 isSimpleGetter = keyDefined && !isObject(key);
2465 if (!data && !isSimpleGetter) {
2466 jqLiteExpandoStore(element, 'data', data = {});
2473 if (isSimpleGetter) {
2474 // don't create data in this case.
2475 return data && data[key];
2485 function jqLiteHasClass(element, selector) {
2486 if (!element.getAttribute) return false;
2487 return ((" " + (element.getAttribute('class') || '') + " ").replace(/[\n\t]/g, " ").
2488 indexOf( " " + selector + " " ) > -1);
2491 function jqLiteRemoveClass(element, cssClasses) {
2492 if (cssClasses && element.setAttribute) {
2493 forEach(cssClasses.split(' '), function(cssClass) {
2494 element.setAttribute('class', trim(
2495 (" " + (element.getAttribute('class') || '') + " ")
2496 .replace(/[\n\t]/g, " ")
2497 .replace(" " + trim(cssClass) + " ", " "))
2503 function jqLiteAddClass(element, cssClasses) {
2504 if (cssClasses && element.setAttribute) {
2505 var existingClasses = (' ' + (element.getAttribute('class') || '') + ' ')
2506 .replace(/[\n\t]/g, " ");
2508 forEach(cssClasses.split(' '), function(cssClass) {
2509 cssClass = trim(cssClass);
2510 if (existingClasses.indexOf(' ' + cssClass + ' ') === -1) {
2511 existingClasses += cssClass + ' ';
2515 element.setAttribute('class', trim(existingClasses));
2519 function jqLiteAddNodes(root, elements) {
2521 elements = (!elements.nodeName && isDefined(elements.length) && !isWindow(elements))
2524 for(var i=0; i < elements.length; i++) {
2525 root.push(elements[i]);
2530 function jqLiteController(element, name) {
2531 return jqLiteInheritedData(element, '$' + (name || 'ngController' ) + 'Controller');
2534 function jqLiteInheritedData(element, name, value) {
2535 // if element is the document object work with the html element instead
2536 // this makes $(document).scope() possible
2537 if(element.nodeType == 9) {
2538 element = element.documentElement;
2540 var names = isArray(name) ? name : [name];
2543 for (var i = 0, ii = names.length; i < ii; i++) {
2544 if ((value = jqLite.data(element, names[i])) !== undefined) return value;
2547 // If dealing with a document fragment node with a host element, and no parent, use the host
2548 // element as the parent. This enables directives within a Shadow DOM or polyfilled Shadow DOM
2549 // to lookup parent controllers.
2550 element = element.parentNode || (element.nodeType === 11 && element.host);
2554 function jqLiteEmpty(element) {
2555 for (var i = 0, childNodes = element.childNodes; i < childNodes.length; i++) {
2556 jqLiteDealoc(childNodes[i]);
2558 while (element.firstChild) {
2559 element.removeChild(element.firstChild);
2563 //////////////////////////////////////////
2564 // Functions which are declared directly.
2565 //////////////////////////////////////////
2566 var JQLitePrototype = JQLite.prototype = {
2567 ready: function(fn) {
2570 function trigger() {
2576 // check if document already is loaded
2577 if (document.readyState === 'complete'){
2578 setTimeout(trigger);
2580 this.on('DOMContentLoaded', trigger); // works for modern browsers and IE9
2581 // we can not use jqLite since we are not done loading and jQuery could be loaded later.
2583 JQLite(window).on('load', trigger); // fallback to window.onload for others
2587 toString: function() {
2589 forEach(this, function(e){ value.push('' + e);});
2590 return '[' + value.join(', ') + ']';
2593 eq: function(index) {
2594 return (index >= 0) ? jqLite(this[index]) : jqLite(this[this.length + index]);
2603 //////////////////////////////////////////
2604 // Functions iterating getter/setters.
2605 // these functions return self on setter and
2607 //////////////////////////////////////////
2608 var BOOLEAN_ATTR = {};
2609 forEach('multiple,selected,checked,disabled,readOnly,required,open'.split(','), function(value) {
2610 BOOLEAN_ATTR[lowercase(value)] = value;
2612 var BOOLEAN_ELEMENTS = {};
2613 forEach('input,select,option,textarea,button,form,details'.split(','), function(value) {
2614 BOOLEAN_ELEMENTS[uppercase(value)] = true;
2617 function getBooleanAttrName(element, name) {
2618 // check dom last since we will most likely fail on name
2619 var booleanAttr = BOOLEAN_ATTR[name.toLowerCase()];
2621 // booleanAttr is here twice to minimize DOM access
2622 return booleanAttr && BOOLEAN_ELEMENTS[element.nodeName] && booleanAttr;
2627 removeData: jqLiteRemoveData
2628 }, function(fn, name) {
2634 inheritedData: jqLiteInheritedData,
2636 scope: function(element) {
2637 // Can't use jqLiteData here directly so we stay compatible with jQuery!
2638 return jqLite.data(element, '$scope') || jqLiteInheritedData(element.parentNode || element, ['$isolateScope', '$scope']);
2641 isolateScope: function(element) {
2642 // Can't use jqLiteData here directly so we stay compatible with jQuery!
2643 return jqLite.data(element, '$isolateScope') || jqLite.data(element, '$isolateScopeNoTemplate');
2646 controller: jqLiteController,
2648 injector: function(element) {
2649 return jqLiteInheritedData(element, '$injector');
2652 removeAttr: function(element,name) {
2653 element.removeAttribute(name);
2656 hasClass: jqLiteHasClass,
2658 css: function(element, name, value) {
2659 name = camelCase(name);
2661 if (isDefined(value)) {
2662 element.style[name] = value;
2667 // this is some IE specific weirdness that jQuery 1.6.4 does not sure why
2668 val = element.currentStyle && element.currentStyle[name];
2669 if (val === '') val = 'auto';
2672 val = val || element.style[name];
2675 // jquery weirdness :-/
2676 val = (val === '') ? undefined : val;
2683 attr: function(element, name, value){
2684 var lowercasedName = lowercase(name);
2685 if (BOOLEAN_ATTR[lowercasedName]) {
2686 if (isDefined(value)) {
2688 element[name] = true;
2689 element.setAttribute(name, lowercasedName);
2691 element[name] = false;
2692 element.removeAttribute(lowercasedName);
2695 return (element[name] ||
2696 (element.attributes.getNamedItem(name)|| noop).specified)
2700 } else if (isDefined(value)) {
2701 element.setAttribute(name, value);
2702 } else if (element.getAttribute) {
2703 // the extra argument "2" is to get the right thing for a.href in IE, see jQuery code
2704 // some elements (e.g. Document) don't have get attribute, so return undefined
2705 var ret = element.getAttribute(name, 2);
2706 // normalize non-existing attributes to undefined (as jQuery)
2707 return ret === null ? undefined : ret;
2711 prop: function(element, name, value) {
2712 if (isDefined(value)) {
2713 element[name] = value;
2715 return element[name];
2720 var NODE_TYPE_TEXT_PROPERTY = [];
2722 NODE_TYPE_TEXT_PROPERTY[1] = 'innerText'; /** Element **/
2723 NODE_TYPE_TEXT_PROPERTY[3] = 'nodeValue'; /** Text **/
2725 NODE_TYPE_TEXT_PROPERTY[1] = /** Element **/
2726 NODE_TYPE_TEXT_PROPERTY[3] = 'textContent'; /** Text **/
2731 function getText(element, value) {
2732 var textProp = NODE_TYPE_TEXT_PROPERTY[element.nodeType];
2733 if (isUndefined(value)) {
2734 return textProp ? element[textProp] : '';
2736 element[textProp] = value;
2740 val: function(element, value) {
2741 if (isUndefined(value)) {
2742 if (nodeName_(element) === 'SELECT' && element.multiple) {
2744 forEach(element.options, function (option) {
2745 if (option.selected) {
2746 result.push(option.value || option.text);
2749 return result.length === 0 ? null : result;
2751 return element.value;
2753 element.value = value;
2756 html: function(element, value) {
2757 if (isUndefined(value)) {
2758 return element.innerHTML;
2760 for (var i = 0, childNodes = element.childNodes; i < childNodes.length; i++) {
2761 jqLiteDealoc(childNodes[i]);
2763 element.innerHTML = value;
2767 }, function(fn, name){
2769 * Properties: writes return selection, reads return first value
2771 JQLite.prototype[name] = function(arg1, arg2) {
2773 var nodeCount = this.length;
2775 // jqLiteHasClass has only two arguments, but is a getter-only fn, so we need to special-case it
2776 // in a way that survives minification.
2777 // jqLiteEmpty takes no arguments but is a setter.
2778 if (fn !== jqLiteEmpty &&
2779 (((fn.length == 2 && (fn !== jqLiteHasClass && fn !== jqLiteController)) ? arg1 : arg2) === undefined)) {
2780 if (isObject(arg1)) {
2782 // we are a write, but the object properties are the key/values
2783 for (i = 0; i < nodeCount; i++) {
2784 if (fn === jqLiteData) {
2785 // data() takes the whole object in jQuery
2789 fn(this[i], key, arg1[key]);
2793 // return self for chaining
2796 // we are a read, so read the first child.
2797 // TODO: do we still need this?
2799 // Only if we have $dv do we iterate over all, otherwise it is just the first element.
2800 var jj = (value === undefined) ? Math.min(nodeCount, 1) : nodeCount;
2801 for (var j = 0; j < jj; j++) {
2802 var nodeValue = fn(this[j], arg1, arg2);
2803 value = value ? value + nodeValue : nodeValue;
2808 // we are a write, so apply to all children
2809 for (i = 0; i < nodeCount; i++) {
2810 fn(this[i], arg1, arg2);
2812 // return self for chaining
2818 function createEventHandler(element, events) {
2819 var eventHandler = function (event, type) {
2820 if (!event.preventDefault) {
2821 event.preventDefault = function() {
2822 event.returnValue = false; //ie
2826 if (!event.stopPropagation) {
2827 event.stopPropagation = function() {
2828 event.cancelBubble = true; //ie
2832 if (!event.target) {
2833 event.target = event.srcElement || document;
2836 if (isUndefined(event.defaultPrevented)) {
2837 var prevent = event.preventDefault;
2838 event.preventDefault = function() {
2839 event.defaultPrevented = true;
2840 prevent.call(event);
2842 event.defaultPrevented = false;
2845 event.isDefaultPrevented = function() {
2846 return event.defaultPrevented || event.returnValue === false;
2849 // Copy event handlers in case event handlers array is modified during execution.
2850 var eventHandlersCopy = shallowCopy(events[type || event.type] || []);
2852 forEach(eventHandlersCopy, function(fn) {
2853 fn.call(element, event);
2856 // Remove monkey-patched methods (IE),
2857 // as they would cause memory leaks in IE8.
2859 // IE7/8 does not allow to delete property on native object
2860 event.preventDefault = null;
2861 event.stopPropagation = null;
2862 event.isDefaultPrevented = null;
2864 // It shouldn't affect normal browsers (native methods are defined on prototype).
2865 delete event.preventDefault;
2866 delete event.stopPropagation;
2867 delete event.isDefaultPrevented;
2870 eventHandler.elem = element;
2871 return eventHandler;
2874 //////////////////////////////////////////
2875 // Functions iterating traversal.
2876 // These functions chain results into a single
2878 //////////////////////////////////////////
2880 removeData: jqLiteRemoveData,
2882 dealoc: jqLiteDealoc,
2884 on: function onFn(element, type, fn, unsupported){
2885 if (isDefined(unsupported)) throw jqLiteMinErr('onargs', 'jqLite#on() does not support the `selector` or `eventData` parameters');
2887 var events = jqLiteExpandoStore(element, 'events'),
2888 handle = jqLiteExpandoStore(element, 'handle');
2890 if (!events) jqLiteExpandoStore(element, 'events', events = {});
2891 if (!handle) jqLiteExpandoStore(element, 'handle', handle = createEventHandler(element, events));
2893 forEach(type.split(' '), function(type){
2894 var eventFns = events[type];
2897 if (type == 'mouseenter' || type == 'mouseleave') {
2898 var contains = document.body.contains || document.body.compareDocumentPosition ?
2900 // jshint bitwise: false
2901 var adown = a.nodeType === 9 ? a.documentElement : a,
2902 bup = b && b.parentNode;
2903 return a === bup || !!( bup && bup.nodeType === 1 && (
2905 adown.contains( bup ) :
2906 a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16
2911 while ( (b = b.parentNode) ) {
2922 // Refer to jQuery's implementation of mouseenter & mouseleave
2923 // Read about mouseenter and mouseleave:
2924 // http://www.quirksmode.org/js/events_mouse.html#link8
2925 var eventmap = { mouseleave : "mouseout", mouseenter : "mouseover"};
2927 onFn(element, eventmap[type], function(event) {
2928 var target = this, related = event.relatedTarget;
2929 // For mousenter/leave call the handler if related is outside the target.
2930 // NB: No relatedTarget if the mouse left/entered the browser window
2931 if ( !related || (related !== target && !contains(target, related)) ){
2932 handle(event, type);
2937 addEventListenerFn(element, type, handle);
2940 eventFns = events[type];
2948 one: function(element, type, fn) {
2949 element = jqLite(element);
2951 //add the listener twice so that when it is called
2952 //you can remove the original function and still be
2953 //able to call element.off(ev, fn) normally
2954 element.on(type, function onFn() {
2955 element.off(type, fn);
2956 element.off(type, onFn);
2958 element.on(type, fn);
2961 replaceWith: function(element, replaceNode) {
2962 var index, parent = element.parentNode;
2963 jqLiteDealoc(element);
2964 forEach(new JQLite(replaceNode), function(node){
2966 parent.insertBefore(node, index.nextSibling);
2968 parent.replaceChild(node, element);
2974 children: function(element) {
2976 forEach(element.childNodes, function(element){
2977 if (element.nodeType === 1)
2978 children.push(element);
2983 contents: function(element) {
2984 return element.contentDocument || element.childNodes || [];
2987 append: function(element, node) {
2988 forEach(new JQLite(node), function(child){
2989 if (element.nodeType === 1 || element.nodeType === 11) {
2990 element.appendChild(child);
2995 prepend: function(element, node) {
2996 if (element.nodeType === 1) {
2997 var index = element.firstChild;
2998 forEach(new JQLite(node), function(child){
2999 element.insertBefore(child, index);
3004 wrap: function(element, wrapNode) {
3005 wrapNode = jqLite(wrapNode)[0];
3006 var parent = element.parentNode;
3008 parent.replaceChild(wrapNode, element);
3010 wrapNode.appendChild(element);
3013 remove: function(element) {
3014 jqLiteDealoc(element);
3015 var parent = element.parentNode;
3016 if (parent) parent.removeChild(element);
3019 after: function(element, newElement) {
3020 var index = element, parent = element.parentNode;
3021 forEach(new JQLite(newElement), function(node){
3022 parent.insertBefore(node, index.nextSibling);
3027 addClass: jqLiteAddClass,
3028 removeClass: jqLiteRemoveClass,
3030 toggleClass: function(element, selector, condition) {
3032 forEach(selector.split(' '), function(className){
3033 var classCondition = condition;
3034 if (isUndefined(classCondition)) {
3035 classCondition = !jqLiteHasClass(element, className);
3037 (classCondition ? jqLiteAddClass : jqLiteRemoveClass)(element, className);
3042 parent: function(element) {
3043 var parent = element.parentNode;
3044 return parent && parent.nodeType !== 11 ? parent : null;
3047 next: function(element) {
3048 if (element.nextElementSibling) {
3049 return element.nextElementSibling;
3052 // IE8 doesn't have nextElementSibling
3053 var elm = element.nextSibling;
3054 while (elm != null && elm.nodeType !== 1) {
3055 elm = elm.nextSibling;
3060 find: function(element, selector) {
3061 if (element.getElementsByTagName) {
3062 return element.getElementsByTagName(selector);
3070 triggerHandler: function(element, event, extraParameters) {
3072 var dummyEvent, eventFnsCopy, handlerArgs;
3073 var eventName = event.type || event;
3074 var eventFns = (jqLiteExpandoStore(element, 'events') || {})[eventName];
3078 // Create a dummy event to pass to the handlers
3080 preventDefault: function() { this.defaultPrevented = true; },
3081 isDefaultPrevented: function() { return this.defaultPrevented === true; },
3082 stopPropagation: noop,
3087 // If a custom event was provided then extend our dummy event with it
3089 dummyEvent = extend(dummyEvent, event);
3092 // Copy event handlers in case event handlers array is modified during execution.
3093 eventFnsCopy = shallowCopy(eventFns);
3094 handlerArgs = extraParameters ? [dummyEvent].concat(extraParameters) : [dummyEvent];
3096 forEach(eventFnsCopy, function(fn) {
3097 fn.apply(element, handlerArgs);
3102 }, function(fn, name){
3104 * chaining functions
3106 JQLite.prototype[name] = function(arg1, arg2, arg3) {
3108 for(var i=0; i < this.length; i++) {
3109 if (isUndefined(value)) {
3110 value = fn(this[i], arg1, arg2, arg3);
3111 if (isDefined(value)) {
3112 // any function which returns a value needs to be wrapped
3113 value = jqLite(value);
3116 jqLiteAddNodes(value, fn(this[i], arg1, arg2, arg3));
3119 return isDefined(value) ? value : this;
3122 // bind legacy bind/unbind to on/off
3123 JQLite.prototype.bind = JQLite.prototype.on;
3124 JQLite.prototype.unbind = JQLite.prototype.off;
3128 * Computes a hash of an 'obj'.
3131 * number is number as string
3132 * object is either result of calling $$hashKey function on the object or uniquely generated id,
3133 * that is also assigned to the $$hashKey property of the object.
3136 * @returns {string} hash string such that the same input will have the same hash string.
3137 * The resulting string key is in 'type:hashKey' format.
3139 function hashKey(obj, nextUidFn) {
3140 var objType = typeof obj,
3143 if (objType == 'function' || (objType == 'object' && obj !== null)) {
3144 if (typeof (key = obj.$$hashKey) == 'function') {
3145 // must invoke on object to keep the right this
3146 key = obj.$$hashKey();
3147 } else if (key === undefined) {
3148 key = obj.$$hashKey = (nextUidFn || nextUid)();
3154 return objType + ':' + key;
3158 * HashMap which can use objects as keys
3160 function HashMap(array, isolatedUid) {
3163 this.nextUid = function() {
3167 forEach(array, this.put, this);
3169 HashMap.prototype = {
3171 * Store key value pair
3172 * @param key key to store can be any type
3173 * @param value value to store can be any type
3175 put: function(key, value) {
3176 this[hashKey(key, this.nextUid)] = value;
3181 * @returns {Object} the value for the key
3183 get: function(key) {
3184 return this[hashKey(key, this.nextUid)];
3188 * Remove the key/value pair
3191 remove: function(key) {
3192 var value = this[key = hashKey(key, this.nextUid)];
3201 * @name angular.injector
3205 * Creates an injector function that can be used for retrieving services as well as for
3206 * dependency injection (see {@link guide/di dependency injection}).
3209 * @param {Array.<string|Function>} modules A list of module functions or their aliases. See
3210 * {@link angular.module}. The `ng` module must be explicitly added.
3211 * @returns {function()} Injector function. See {@link auto.$injector $injector}.
3216 * // create an injector
3217 * var $injector = angular.injector(['ng']);
3219 * // use the injector to kick off your application
3220 * // use the type inference to auto inject arguments, or use implicit injection
3221 * $injector.invoke(function($rootScope, $compile, $document){
3222 * $compile($document)($rootScope);
3223 * $rootScope.$digest();
3227 * Sometimes you want to get access to the injector of a currently running Angular app
3228 * from outside Angular. Perhaps, you want to inject and compile some markup after the
3229 * application has been bootstrapped. You can do this using the extra `injector()` added
3230 * to JQuery/jqLite elements. See {@link angular.element}.
3232 * *This is fairly rare but could be the case if a third party library is injecting the
3235 * In the following example a new block of HTML containing a `ng-controller`
3236 * directive is added to the end of the document body by JQuery. We then compile and link
3237 * it into the current AngularJS scope.
3240 * var $div = $('<div ng-controller="MyCtrl">{{content.label}}</div>');
3241 * $(document.body).append($div);
3243 * angular.element(document).injector().invoke(function($compile) {
3244 * var scope = angular.element($div).scope();
3245 * $compile($div)(scope);
3256 * Implicit module which gets automatically added to each {@link auto.$injector $injector}.
3259 var FN_ARGS = /^function\s*[^\(]*\(\s*([^\)]*)\)/m;
3260 var FN_ARG_SPLIT = /,/;
3261 var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/;
3262 var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
3263 var $injectorMinErr = minErr('$injector');
3264 function annotate(fn) {
3270 if (typeof fn === 'function') {
3271 if (!($inject = fn.$inject)) {
3274 fnText = fn.toString().replace(STRIP_COMMENTS, '');
3275 argDecl = fnText.match(FN_ARGS);
3276 forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg){
3277 arg.replace(FN_ARG, function(all, underscore, name){
3282 fn.$inject = $inject;
3284 } else if (isArray(fn)) {
3285 last = fn.length - 1;
3286 assertArgFn(fn[last], 'fn');
3287 $inject = fn.slice(0, last);
3289 assertArgFn(fn, 'fn', true);
3294 ///////////////////////////////////////
3303 * `$injector` is used to retrieve object instances as defined by
3304 * {@link auto.$provide provider}, instantiate types, invoke methods,
3307 * The following always holds true:
3310 * var $injector = angular.injector();
3311 * expect($injector.get('$injector')).toBe($injector);
3312 * expect($injector.invoke(function($injector){
3314 * }).toBe($injector);
3317 * # Injection Function Annotation
3319 * JavaScript does not have annotations, and annotations are needed for dependency injection. The
3320 * following are all valid ways of annotating function with injection arguments and are equivalent.
3323 * // inferred (only works if code not minified/obfuscated)
3324 * $injector.invoke(function(serviceA){});
3327 * function explicit(serviceA) {};
3328 * explicit.$inject = ['serviceA'];
3329 * $injector.invoke(explicit);
3332 * $injector.invoke(['serviceA', function(serviceA){}]);
3337 * In JavaScript calling `toString()` on a function returns the function definition. The definition
3338 * can then be parsed and the function arguments can be extracted. *NOTE:* This does not work with
3339 * minification, and obfuscation tools since these tools change the argument names.
3341 * ## `$inject` Annotation
3342 * By adding an `$inject` property onto a function the injection parameters can be specified.
3345 * As an array of injection names, where the last item in the array is the function to call.
3350 * @name $injector#get
3353 * Return an instance of the service.
3355 * @param {string} name The name of the instance to retrieve.
3356 * @return {*} The instance.
3361 * @name $injector#invoke
3364 * Invoke the method and supply the method arguments from the `$injector`.
3366 * @param {!Function} fn The function to invoke. Function parameters are injected according to the
3367 * {@link guide/di $inject Annotation} rules.
3368 * @param {Object=} self The `this` for the invoked method.
3369 * @param {Object=} locals Optional object. If preset then any argument names are read from this
3370 * object first, before the `$injector` is consulted.
3371 * @returns {*} the value returned by the invoked `fn` function.
3376 * @name $injector#has
3379 * Allows the user to query if the particular service exists.
3381 * @param {string} Name of the service to query.
3382 * @returns {boolean} returns true if injector has given service.
3387 * @name $injector#instantiate
3389 * Create a new instance of JS type. The method takes a constructor function, invokes the new
3390 * operator, and supplies all of the arguments to the constructor function as specified by the
3391 * constructor annotation.
3393 * @param {Function} Type Annotated constructor function.
3394 * @param {Object=} locals Optional object. If preset then any argument names are read from this
3395 * object first, before the `$injector` is consulted.
3396 * @returns {Object} new instance of `Type`.
3401 * @name $injector#annotate
3404 * Returns an array of service names which the function is requesting for injection. This API is
3405 * used by the injector to determine which services need to be injected into the function when the
3406 * function is invoked. There are three ways in which the function can be annotated with the needed
3411 * The simplest form is to extract the dependencies from the arguments of the function. This is done
3412 * by converting the function into a string using `toString()` method and extracting the argument
3416 * function MyController($scope, $route) {
3421 * expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
3424 * This method does not work with code minification / obfuscation. For this reason the following
3425 * annotation strategies are supported.
3427 * # The `$inject` property
3429 * If a function has an `$inject` property and its value is an array of strings, then the strings
3430 * represent names of services to be injected into the function.
3433 * var MyController = function(obfuscatedScope, obfuscatedRoute) {
3436 * // Define function dependencies
3437 * MyController['$inject'] = ['$scope', '$route'];
3440 * expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
3443 * # The array notation
3445 * It is often desirable to inline Injected functions and that's when setting the `$inject` property
3446 * is very inconvenient. In these situations using the array notation to specify the dependencies in
3447 * a way that survives minification is a better choice:
3450 * // We wish to write this (not minification / obfuscation safe)
3451 * injector.invoke(function($compile, $rootScope) {
3455 * // We are forced to write break inlining
3456 * var tmpFn = function(obfuscatedCompile, obfuscatedRootScope) {
3459 * tmpFn.$inject = ['$compile', '$rootScope'];
3460 * injector.invoke(tmpFn);
3462 * // To better support inline function the inline annotation is supported
3463 * injector.invoke(['$compile', '$rootScope', function(obfCompile, obfRootScope) {
3468 * expect(injector.annotate(
3469 * ['$compile', '$rootScope', function(obfus_$compile, obfus_$rootScope) {}])
3470 * ).toEqual(['$compile', '$rootScope']);
3473 * @param {Function|Array.<string|Function>} fn Function for which dependent service names need to
3474 * be retrieved as described above.
3476 * @returns {Array.<string>} The names of the services which the function requires.
3488 * The {@link auto.$provide $provide} service has a number of methods for registering components
3489 * with the {@link auto.$injector $injector}. Many of these functions are also exposed on
3490 * {@link angular.Module}.
3492 * An Angular **service** is a singleton object created by a **service factory**. These **service
3493 * factories** are functions which, in turn, are created by a **service provider**.
3494 * The **service providers** are constructor functions. When instantiated they must contain a
3495 * property called `$get`, which holds the **service factory** function.
3497 * When you request a service, the {@link auto.$injector $injector} is responsible for finding the
3498 * correct **service provider**, instantiating it and then calling its `$get` **service factory**
3499 * function to get the instance of the **service**.
3501 * Often services have no configuration options and there is no need to add methods to the service
3502 * provider. The provider will be no more than a constructor function with a `$get` property. For
3503 * these cases the {@link auto.$provide $provide} service has additional helper methods to register
3504 * services without specifying a provider.
3506 * * {@link auto.$provide#provider provider(provider)} - registers a **service provider** with the
3507 * {@link auto.$injector $injector}
3508 * * {@link auto.$provide#constant constant(obj)} - registers a value/object that can be accessed by
3509 * providers and services.
3510 * * {@link auto.$provide#value value(obj)} - registers a value/object that can only be accessed by
3511 * services, not providers.
3512 * * {@link auto.$provide#factory factory(fn)} - registers a service **factory function**, `fn`,
3513 * that will be wrapped in a **service provider** object, whose `$get` property will contain the
3514 * given factory function.
3515 * * {@link auto.$provide#service service(class)} - registers a **constructor function**, `class`
3516 * that will be wrapped in a **service provider** object, whose `$get` property will instantiate
3517 * a new object using the given constructor function.
3519 * See the individual methods for more information and examples.
3524 * @name $provide#provider
3527 * Register a **provider function** with the {@link auto.$injector $injector}. Provider functions
3528 * are constructor functions, whose instances are responsible for "providing" a factory for a
3531 * Service provider names start with the name of the service they provide followed by `Provider`.
3532 * For example, the {@link ng.$log $log} service has a provider called
3533 * {@link ng.$logProvider $logProvider}.
3535 * Service provider objects can have additional methods which allow configuration of the provider
3536 * and its service. Importantly, you can configure what kind of service is created by the `$get`
3537 * method, or how that service will act. For example, the {@link ng.$logProvider $logProvider} has a
3538 * method {@link ng.$logProvider#debugEnabled debugEnabled}
3539 * which lets you specify whether the {@link ng.$log $log} service will log debug messages to the
3542 * @param {string} name The name of the instance. NOTE: the provider will be available under `name +
3544 * @param {(Object|function())} provider If the provider is:
3546 * - `Object`: then it should have a `$get` method. The `$get` method will be invoked using
3547 * {@link auto.$injector#invoke $injector.invoke()} when an instance needs to be created.
3548 * - `Constructor`: a new instance of the provider will be created using
3549 * {@link auto.$injector#instantiate $injector.instantiate()}, then treated as `object`.
3551 * @returns {Object} registered provider instance
3555 * The following example shows how to create a simple event tracking service and register it using
3556 * {@link auto.$provide#provider $provide.provider()}.
3559 * // Define the eventTracker provider
3560 * function EventTrackerProvider() {
3561 * var trackingUrl = '/track';
3563 * // A provider method for configuring where the tracked events should been saved
3564 * this.setTrackingUrl = function(url) {
3565 * trackingUrl = url;
3568 * // The service factory function
3569 * this.$get = ['$http', function($http) {
3570 * var trackedEvents = {};
3572 * // Call this to track an event
3573 * event: function(event) {
3574 * var count = trackedEvents[event] || 0;
3576 * trackedEvents[event] = count;
3579 * // Call this to save the tracked events to the trackingUrl
3580 * save: function() {
3581 * $http.post(trackingUrl, trackedEvents);
3587 * describe('eventTracker', function() {
3590 * beforeEach(module(function($provide) {
3591 * // Register the eventTracker provider
3592 * $provide.provider('eventTracker', EventTrackerProvider);
3595 * beforeEach(module(function(eventTrackerProvider) {
3596 * // Configure eventTracker provider
3597 * eventTrackerProvider.setTrackingUrl('/custom-track');
3600 * it('tracks events', inject(function(eventTracker) {
3601 * expect(eventTracker.event('login')).toEqual(1);
3602 * expect(eventTracker.event('login')).toEqual(2);
3605 * it('saves to the tracking url', inject(function(eventTracker, $http) {
3606 * postSpy = spyOn($http, 'post');
3607 * eventTracker.event('login');
3608 * eventTracker.save();
3609 * expect(postSpy).toHaveBeenCalled();
3610 * expect(postSpy.mostRecentCall.args[0]).not.toEqual('/track');
3611 * expect(postSpy.mostRecentCall.args[0]).toEqual('/custom-track');
3612 * expect(postSpy.mostRecentCall.args[1]).toEqual({ 'login': 1 });
3620 * @name $provide#factory
3623 * Register a **service factory**, which will be called to return the service instance.
3624 * This is short for registering a service where its provider consists of only a `$get` property,
3625 * which is the given service factory function.
3626 * You should use {@link auto.$provide#factory $provide.factory(getFn)} if you do not need to
3627 * configure your service in a provider.
3629 * @param {string} name The name of the instance.
3630 * @param {function()} $getFn The $getFn for the instance creation. Internally this is a short hand
3631 * for `$provide.provider(name, {$get: $getFn})`.
3632 * @returns {Object} registered provider instance
3635 * Here is an example of registering a service
3637 * $provide.factory('ping', ['$http', function($http) {
3638 * return function ping() {
3639 * return $http.send('/ping');
3643 * You would then inject and use this service like this:
3645 * someModule.controller('Ctrl', ['ping', function(ping) {
3654 * @name $provide#service
3657 * Register a **service constructor**, which will be invoked with `new` to create the service
3659 * This is short for registering a service where its provider's `$get` property is the service
3660 * constructor function that will be used to instantiate the service instance.
3662 * You should use {@link auto.$provide#service $provide.service(class)} if you define your service
3665 * @param {string} name The name of the instance.
3666 * @param {Function} constructor A class (constructor function) that will be instantiated.
3667 * @returns {Object} registered provider instance
3670 * Here is an example of registering a service using
3671 * {@link auto.$provide#service $provide.service(class)}.
3673 * var Ping = function($http) {
3674 * this.$http = $http;
3677 * Ping.$inject = ['$http'];
3679 * Ping.prototype.send = function() {
3680 * return this.$http.get('/ping');
3682 * $provide.service('ping', Ping);
3684 * You would then inject and use this service like this:
3686 * someModule.controller('Ctrl', ['ping', function(ping) {
3695 * @name $provide#value
3698 * Register a **value service** with the {@link auto.$injector $injector}, such as a string, a
3699 * number, an array, an object or a function. This is short for registering a service where its
3700 * provider's `$get` property is a factory function that takes no arguments and returns the **value
3703 * Value services are similar to constant services, except that they cannot be injected into a
3704 * module configuration function (see {@link angular.Module#config}) but they can be overridden by
3706 * {@link auto.$provide#decorator decorator}.
3708 * @param {string} name The name of the instance.
3709 * @param {*} value The value.
3710 * @returns {Object} registered provider instance
3713 * Here are some examples of creating value services.
3715 * $provide.value('ADMIN_USER', 'admin');
3717 * $provide.value('RoleLookup', { admin: 0, writer: 1, reader: 2 });
3719 * $provide.value('halfOf', function(value) {
3728 * @name $provide#constant
3731 * Register a **constant service**, such as a string, a number, an array, an object or a function,
3732 * with the {@link auto.$injector $injector}. Unlike {@link auto.$provide#value value} it can be
3733 * injected into a module configuration function (see {@link angular.Module#config}) and it cannot
3734 * be overridden by an Angular {@link auto.$provide#decorator decorator}.
3736 * @param {string} name The name of the constant.
3737 * @param {*} value The constant value.
3738 * @returns {Object} registered instance
3741 * Here a some examples of creating constants:
3743 * $provide.constant('SHARD_HEIGHT', 306);
3745 * $provide.constant('MY_COLOURS', ['red', 'blue', 'grey']);
3747 * $provide.constant('double', function(value) {
3756 * @name $provide#decorator
3759 * Register a **service decorator** with the {@link auto.$injector $injector}. A service decorator
3760 * intercepts the creation of a service, allowing it to override or modify the behaviour of the
3761 * service. The object returned by the decorator may be the original service, or a new service
3762 * object which replaces or wraps and delegates to the original service.
3764 * @param {string} name The name of the service to decorate.
3765 * @param {function()} decorator This function will be invoked when the service needs to be
3766 * instantiated and should return the decorated service instance. The function is called using
3767 * the {@link auto.$injector#invoke injector.invoke} method and is therefore fully injectable.
3768 * Local injection arguments:
3770 * * `$delegate` - The original service instance, which can be monkey patched, configured,
3771 * decorated or delegated to.
3774 * Here we decorate the {@link ng.$log $log} service to convert warnings to errors by intercepting
3775 * calls to {@link ng.$log#error $log.warn()}.
3777 * $provide.decorator('$log', ['$delegate', function($delegate) {
3778 * $delegate.warn = $delegate.error;
3785 function createInjector(modulesToLoad) {
3786 var INSTANTIATING = {},
3787 providerSuffix = 'Provider',
3789 loadedModules = new HashMap([], true),
3792 provider: supportObject(provider),
3793 factory: supportObject(factory),
3794 service: supportObject(service),
3795 value: supportObject(value),
3796 constant: supportObject(constant),
3797 decorator: decorator
3800 providerInjector = (providerCache.$injector =
3801 createInternalInjector(providerCache, function() {
3802 throw $injectorMinErr('unpr', "Unknown provider: {0}", path.join(' <- '));
3805 instanceInjector = (instanceCache.$injector =
3806 createInternalInjector(instanceCache, function(servicename) {
3807 var provider = providerInjector.get(servicename + providerSuffix);
3808 return instanceInjector.invoke(provider.$get, provider);
3812 forEach(loadModules(modulesToLoad), function(fn) { instanceInjector.invoke(fn || noop); });
3814 return instanceInjector;
3816 ////////////////////////////////////
3818 ////////////////////////////////////
3820 function supportObject(delegate) {
3821 return function(key, value) {
3822 if (isObject(key)) {
3823 forEach(key, reverseParams(delegate));
3825 return delegate(key, value);
3830 function provider(name, provider_) {
3831 assertNotHasOwnProperty(name, 'service');
3832 if (isFunction(provider_) || isArray(provider_)) {
3833 provider_ = providerInjector.instantiate(provider_);
3835 if (!provider_.$get) {
3836 throw $injectorMinErr('pget', "Provider '{0}' must define $get factory method.", name);
3838 return providerCache[name + providerSuffix] = provider_;
3841 function factory(name, factoryFn) { return provider(name, { $get: factoryFn }); }
3843 function service(name, constructor) {
3844 return factory(name, ['$injector', function($injector) {
3845 return $injector.instantiate(constructor);
3849 function value(name, val) { return factory(name, valueFn(val)); }
3851 function constant(name, value) {
3852 assertNotHasOwnProperty(name, 'constant');
3853 providerCache[name] = value;
3854 instanceCache[name] = value;
3857 function decorator(serviceName, decorFn) {
3858 var origProvider = providerInjector.get(serviceName + providerSuffix),
3859 orig$get = origProvider.$get;
3861 origProvider.$get = function() {
3862 var origInstance = instanceInjector.invoke(orig$get, origProvider);
3863 return instanceInjector.invoke(decorFn, null, {$delegate: origInstance});
3867 ////////////////////////////////////
3869 ////////////////////////////////////
3870 function loadModules(modulesToLoad){
3871 var runBlocks = [], moduleFn, invokeQueue, i, ii;
3872 forEach(modulesToLoad, function(module) {
3873 if (loadedModules.get(module)) return;
3874 loadedModules.put(module, true);
3877 if (isString(module)) {
3878 moduleFn = angularModule(module);
3879 runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks);
3881 for(invokeQueue = moduleFn._invokeQueue, i = 0, ii = invokeQueue.length; i < ii; i++) {
3882 var invokeArgs = invokeQueue[i],
3883 provider = providerInjector.get(invokeArgs[0]);
3885 provider[invokeArgs[1]].apply(provider, invokeArgs[2]);
3887 } else if (isFunction(module)) {
3888 runBlocks.push(providerInjector.invoke(module));
3889 } else if (isArray(module)) {
3890 runBlocks.push(providerInjector.invoke(module));
3892 assertArgFn(module, 'module');
3895 if (isArray(module)) {
3896 module = module[module.length - 1];
3898 if (e.message && e.stack && e.stack.indexOf(e.message) == -1) {
3899 // Safari & FF's stack traces don't contain error.message content
3900 // unlike those of Chrome and IE
3901 // So if stack doesn't contain message, we create a new string that contains both.
3902 // Since error.stack is read-only in Safari, I'm overriding e and not e.stack here.
3904 e = e.message + '\n' + e.stack;
3906 throw $injectorMinErr('modulerr', "Failed to instantiate module {0} due to:\n{1}",
3907 module, e.stack || e.message || e);
3913 ////////////////////////////////////
3914 // internal Injector
3915 ////////////////////////////////////
3917 function createInternalInjector(cache, factory) {
3919 function getService(serviceName) {
3920 if (cache.hasOwnProperty(serviceName)) {
3921 if (cache[serviceName] === INSTANTIATING) {
3922 throw $injectorMinErr('cdep', 'Circular dependency found: {0}',
3923 serviceName + ' <- ' + path.join(' <- '));
3925 return cache[serviceName];
3928 path.unshift(serviceName);
3929 cache[serviceName] = INSTANTIATING;
3930 return cache[serviceName] = factory(serviceName);
3932 if (cache[serviceName] === INSTANTIATING) {
3933 delete cache[serviceName];
3942 function invoke(fn, self, locals){
3944 $inject = annotate(fn),
3948 for(i = 0, length = $inject.length; i < length; i++) {
3950 if (typeof key !== 'string') {
3951 throw $injectorMinErr('itkn',
3952 'Incorrect injection token! Expected service name as string, got {0}', key);
3955 locals && locals.hasOwnProperty(key)
3964 // http://jsperf.com/angularjs-invoke-apply-vs-switch
3966 return fn.apply(self, args);
3969 function instantiate(Type, locals) {
3970 var Constructor = function() {},
3971 instance, returnedValue;
3973 // Check if Type is annotated and use just the given function at n-1 as parameter
3974 // e.g. someModule.factory('greeter', ['$window', function(renamed$window) {}]);
3975 Constructor.prototype = (isArray(Type) ? Type[Type.length - 1] : Type).prototype;
3976 instance = new Constructor();
3977 returnedValue = invoke(Type, instance, locals);
3979 return isObject(returnedValue) || isFunction(returnedValue) ? returnedValue : instance;
3984 instantiate: instantiate,
3987 has: function(name) {
3988 return providerCache.hasOwnProperty(name + providerSuffix) || cache.hasOwnProperty(name);
3996 * @name $anchorScroll
3999 * @requires $location
4000 * @requires $rootScope
4003 * When called, it checks current value of `$location.hash()` and scrolls to the related element,
4004 * according to rules specified in
4005 * [Html5 spec](http://dev.w3.org/html5/spec/Overview.html#the-indicated-part-of-the-document).
4007 * It also watches the `$location.hash()` and scrolls whenever it changes to match any anchor.
4008 * This can be disabled by calling `$anchorScrollProvider.disableAutoScrolling()`.
4012 <file name="index.html">
4013 <div id="scrollArea" ng-controller="ScrollCtrl">
4014 <a ng-click="gotoBottom()">Go to bottom</a>
4015 <a id="bottom"></a> You're at the bottom!
4018 <file name="script.js">
4019 function ScrollCtrl($scope, $location, $anchorScroll) {
4020 $scope.gotoBottom = function (){
4021 // set the location.hash to the id of
4022 // the element you wish to scroll to.
4023 $location.hash('bottom');
4025 // call $anchorScroll()
4030 <file name="style.css">
4043 function $AnchorScrollProvider() {
4045 var autoScrollingEnabled = true;
4047 this.disableAutoScrolling = function() {
4048 autoScrollingEnabled = false;
4051 this.$get = ['$window', '$location', '$rootScope', function($window, $location, $rootScope) {
4052 var document = $window.document;
4054 // helper function to get first anchor from a NodeList
4055 // can't use filter.filter, as it accepts only instances of Array
4056 // and IE can't convert NodeList to an array using [].slice
4057 // TODO(vojta): use filter if we change it to accept lists as well
4058 function getFirstAnchor(list) {
4060 forEach(list, function(element) {
4061 if (!result && lowercase(element.nodeName) === 'a') result = element;
4067 var hash = $location.hash(), elm;
4069 // empty hash, scroll to the top of the page
4070 if (!hash) $window.scrollTo(0, 0);
4072 // element with given id
4073 else if ((elm = document.getElementById(hash))) elm.scrollIntoView();
4075 // first anchor with given name :-D
4076 else if ((elm = getFirstAnchor(document.getElementsByName(hash)))) elm.scrollIntoView();
4078 // no element and hash == 'top', scroll to the top of the page
4079 else if (hash === 'top') $window.scrollTo(0, 0);
4082 // does not scroll when user clicks on anchor link that is currently on
4083 // (no url change, no $location.hash() change), browser native does scroll
4084 if (autoScrollingEnabled) {
4085 $rootScope.$watch(function autoScrollWatch() {return $location.hash();},
4086 function autoScrollWatchAction() {
4087 $rootScope.$evalAsync(scroll);
4095 var $animateMinErr = minErr('$animate');
4099 * @name $animateProvider
4102 * Default implementation of $animate that doesn't perform any animations, instead just
4103 * synchronously performs DOM
4104 * updates and calls done() callbacks.
4106 * In order to enable animations the ngAnimate module has to be loaded.
4108 * To see the functional implementation check out src/ngAnimate/animate.js
4110 var $AnimateProvider = ['$provide', function($provide) {
4113 this.$$selectors = {};
4118 * @name $animateProvider#register
4121 * Registers a new injectable animation factory function. The factory function produces the
4122 * animation object which contains callback functions for each event that is expected to be
4125 * * `eventFn`: `function(Element, doneFunction)` The element to animate, the `doneFunction`
4126 * must be called once the element animation is complete. If a function is returned then the
4127 * animation service will use this function to cancel the animation whenever a cancel event is
4133 * eventFn : function(element, done) {
4134 * //code to run the animation
4135 * //once complete, then run done()
4136 * return function cancellationFunction() {
4137 * //code to cancel the animation
4143 * @param {string} name The name of the animation.
4144 * @param {Function} factory The factory function that will be executed to return the animation
4147 this.register = function(name, factory) {
4148 var key = name + '-animation';
4149 if (name && name.charAt(0) != '.') throw $animateMinErr('notcsel',
4150 "Expecting class selector starting with '.' got '{0}'.", name);
4151 this.$$selectors[name.substr(1)] = key;
4152 $provide.factory(key, factory);
4157 * @name $animateProvider#classNameFilter
4160 * Sets and/or returns the CSS class regular expression that is checked when performing
4161 * an animation. Upon bootstrap the classNameFilter value is not set at all and will
4162 * therefore enable $animate to attempt to perform an animation on any element.
4163 * When setting the classNameFilter value, animations will only be performed on elements
4164 * that successfully match the filter expression. This in turn can boost performance
4165 * for low-powered devices as well as applications containing a lot of structural operations.
4166 * @param {RegExp=} expression The className expression which will be checked against all animations
4167 * @return {RegExp} The current CSS className expression value. If null then there is no expression value
4169 this.classNameFilter = function(expression) {
4170 if(arguments.length === 1) {
4171 this.$$classNameFilter = (expression instanceof RegExp) ? expression : null;
4173 return this.$$classNameFilter;
4176 this.$get = ['$timeout', '$$asyncCallback', function($timeout, $$asyncCallback) {
4178 function async(fn) {
4179 fn && $$asyncCallback(fn);
4186 * @description The $animate service provides rudimentary DOM manipulation functions to
4187 * insert, remove and move elements within the DOM, as well as adding and removing classes.
4188 * This service is the core service used by the ngAnimate $animator service which provides
4189 * high-level animation hooks for CSS and JavaScript.
4191 * $animate is available in the AngularJS core, however, the ngAnimate module must be included
4192 * to enable full out animation support. Otherwise, $animate will only perform simple DOM
4193 * manipulation operations.
4195 * To learn more about enabling animation support, click here to visit the {@link ngAnimate
4196 * ngAnimate module page} as well as the {@link ngAnimate.$animate ngAnimate $animate service
4204 * @name $animate#enter
4206 * @description Inserts the element into the DOM either after the `after` element or within
4207 * the `parent` element. Once complete, the done() callback will be fired (if provided).
4208 * @param {DOMElement} element the element which will be inserted into the DOM
4209 * @param {DOMElement} parent the parent element which will append the element as
4210 * a child (if the after element is not present)
4211 * @param {DOMElement} after the sibling element which will append the element
4213 * @param {Function=} done callback function that will be called after the element has been
4214 * inserted into the DOM
4216 enter : function(element, parent, after, done) {
4218 after.after(element);
4220 if (!parent || !parent[0]) {
4221 parent = after.parent();
4223 parent.append(element);
4231 * @name $animate#leave
4233 * @description Removes the element from the DOM. Once complete, the done() callback will be
4234 * fired (if provided).
4235 * @param {DOMElement} element the element which will be removed from the DOM
4236 * @param {Function=} done callback function that will be called after the element has been
4237 * removed from the DOM
4239 leave : function(element, done) {
4247 * @name $animate#move
4249 * @description Moves the position of the provided element within the DOM to be placed
4250 * either after the `after` element or inside of the `parent` element. Once complete, the
4251 * done() callback will be fired (if provided).
4253 * @param {DOMElement} element the element which will be moved around within the
4255 * @param {DOMElement} parent the parent element where the element will be
4256 * inserted into (if the after element is not present)
4257 * @param {DOMElement} after the sibling element where the element will be
4258 * positioned next to
4259 * @param {Function=} done the callback function (if provided) that will be fired after the
4260 * element has been moved to its new position
4262 move : function(element, parent, after, done) {
4263 // Do not remove element before insert. Removing will cause data associated with the
4264 // element to be dropped. Insert will implicitly do the remove.
4265 this.enter(element, parent, after, done);
4271 * @name $animate#addClass
4273 * @description Adds the provided className CSS class value to the provided element. Once
4274 * complete, the done() callback will be fired (if provided).
4275 * @param {DOMElement} element the element which will have the className value
4277 * @param {string} className the CSS class which will be added to the element
4278 * @param {Function=} done the callback function (if provided) that will be fired after the
4279 * className value has been added to the element
4281 addClass : function(element, className, done) {
4282 className = isString(className) ?
4284 isArray(className) ? className.join(' ') : '';
4285 forEach(element, function (element) {
4286 jqLiteAddClass(element, className);
4294 * @name $animate#removeClass
4296 * @description Removes the provided className CSS class value from the provided element.
4297 * Once complete, the done() callback will be fired (if provided).
4298 * @param {DOMElement} element the element which will have the className value
4300 * @param {string} className the CSS class which will be removed from the element
4301 * @param {Function=} done the callback function (if provided) that will be fired after the
4302 * className value has been removed from the element
4304 removeClass : function(element, className, done) {
4305 className = isString(className) ?
4307 isArray(className) ? className.join(' ') : '';
4308 forEach(element, function (element) {
4309 jqLiteRemoveClass(element, className);
4317 * @name $animate#setClass
4319 * @description Adds and/or removes the given CSS classes to and from the element.
4320 * Once complete, the done() callback will be fired (if provided).
4321 * @param {DOMElement} element the element which will have its CSS classes changed
4323 * @param {string} add the CSS classes which will be added to the element
4324 * @param {string} remove the CSS class which will be removed from the element
4325 * @param {Function=} done the callback function (if provided) that will be fired after the
4326 * CSS classes have been set on the element
4328 setClass : function(element, add, remove, done) {
4329 forEach(element, function (element) {
4330 jqLiteAddClass(element, add);
4331 jqLiteRemoveClass(element, remove);
4341 function $$AsyncCallbackProvider(){
4342 this.$get = ['$$rAF', '$timeout', function($$rAF, $timeout) {
4343 return $$rAF.supported
4344 ? function(fn) { return $$rAF(fn); }
4346 return $timeout(fn, 0, false);
4352 * ! This is a private undocumented service !
4357 * This object has two goals:
4359 * - hide all the global state in the browser caused by the window object
4360 * - abstract away all the browser specific features and inconsistencies
4362 * For tests we provide {@link ngMock.$browser mock implementation} of the `$browser`
4363 * service, which can be used for convenient testing of the application without the interaction with
4364 * the real browser apis.
4367 * @param {object} window The global window object.
4368 * @param {object} document jQuery wrapped document.
4369 * @param {function()} XHR XMLHttpRequest constructor.
4370 * @param {object} $log console.log or an object with the same interface.
4371 * @param {object} $sniffer $sniffer service
4373 function Browser(window, document, $log, $sniffer) {
4375 rawDocument = document[0],
4376 location = window.location,
4377 history = window.history,
4378 setTimeout = window.setTimeout,
4379 clearTimeout = window.clearTimeout,
4380 pendingDeferIds = {};
4382 self.isMock = false;
4384 var outstandingRequestCount = 0;
4385 var outstandingRequestCallbacks = [];
4387 // TODO(vojta): remove this temporary api
4388 self.$$completeOutstandingRequest = completeOutstandingRequest;
4389 self.$$incOutstandingRequestCount = function() { outstandingRequestCount++; };
4392 * Executes the `fn` function(supports currying) and decrements the `outstandingRequestCallbacks`
4393 * counter. If the counter reaches 0, all the `outstandingRequestCallbacks` are executed.
4395 function completeOutstandingRequest(fn) {
4397 fn.apply(null, sliceArgs(arguments, 1));
4399 outstandingRequestCount--;
4400 if (outstandingRequestCount === 0) {
4401 while(outstandingRequestCallbacks.length) {
4403 outstandingRequestCallbacks.pop()();
4414 * Note: this method is used only by scenario runner
4415 * TODO(vojta): prefix this method with $$ ?
4416 * @param {function()} callback Function that will be called when no outstanding request
4418 self.notifyWhenNoOutstandingRequests = function(callback) {
4419 // force browser to execute all pollFns - this is needed so that cookies and other pollers fire
4420 // at some deterministic time in respect to the test runner's actions. Leaving things up to the
4421 // regular poller would result in flaky tests.
4422 forEach(pollFns, function(pollFn){ pollFn(); });
4424 if (outstandingRequestCount === 0) {
4427 outstandingRequestCallbacks.push(callback);
4431 //////////////////////////////////////////////////////////////
4433 //////////////////////////////////////////////////////////////
4438 * @name $browser#addPollFn
4440 * @param {function()} fn Poll function to add
4443 * Adds a function to the list of functions that poller periodically executes,
4444 * and starts polling if not started yet.
4446 * @returns {function()} the added function
4448 self.addPollFn = function(fn) {
4449 if (isUndefined(pollTimeout)) startPoller(100, setTimeout);
4455 * @param {number} interval How often should browser call poll functions (ms)
4456 * @param {function()} setTimeout Reference to a real or fake `setTimeout` function.
4459 * Configures the poller to run in the specified intervals, using the specified
4460 * setTimeout fn and kicks it off.
4462 function startPoller(interval, setTimeout) {
4464 forEach(pollFns, function(pollFn){ pollFn(); });
4465 pollTimeout = setTimeout(check, interval);
4469 //////////////////////////////////////////////////////////////
4471 //////////////////////////////////////////////////////////////
4473 var lastBrowserUrl = location.href,
4474 baseElement = document.find('base'),
4478 * @name $browser#url
4482 * Without any argument, this method just returns current value of location.href.
4485 * With at least one argument, this method sets url to new value.
4486 * If html5 history api supported, pushState/replaceState is used, otherwise
4487 * location.href/location.replace is used.
4488 * Returns its own instance to allow chaining
4490 * NOTE: this api is intended for use only by the $location service. Please use the
4491 * {@link ng.$location $location service} to change url.
4493 * @param {string} url New url (when used as setter)
4494 * @param {boolean=} replace Should new url replace current history record ?
4496 self.url = function(url, replace) {
4497 // Android Browser BFCache causes location, history reference to become stale.
4498 if (location !== window.location) location = window.location;
4499 if (history !== window.history) history = window.history;
4503 if (lastBrowserUrl == url) return;
4504 lastBrowserUrl = url;
4505 if ($sniffer.history) {
4506 if (replace) history.replaceState(null, '', url);
4508 history.pushState(null, '', url);
4509 // Crazy Opera Bug: http://my.opera.com/community/forums/topic.dml?id=1185462
4510 baseElement.attr('href', baseElement.attr('href'));
4515 location.replace(url);
4517 location.href = url;
4523 // - newLocation is a workaround for an IE7-9 issue with location.replace and location.href
4524 // methods not updating location.href synchronously.
4525 // - the replacement is a workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=407172
4526 return newLocation || location.href.replace(/%27/g,"'");
4530 var urlChangeListeners = [],
4531 urlChangeInit = false;
4533 function fireUrlChange() {
4535 if (lastBrowserUrl == self.url()) return;
4537 lastBrowserUrl = self.url();
4538 forEach(urlChangeListeners, function(listener) {
4539 listener(self.url());
4544 * @name $browser#onUrlChange
4547 * Register callback function that will be called, when url changes.
4549 * It's only called when the url is changed from outside of angular:
4550 * - user types different url into address bar
4551 * - user clicks on history (forward/back) button
4552 * - user clicks on a link
4554 * It's not called when url is changed by $browser.url() method
4556 * The listener gets called with new url as parameter.
4558 * NOTE: this api is intended for use only by the $location service. Please use the
4559 * {@link ng.$location $location service} to monitor url changes in angular apps.
4561 * @param {function(string)} listener Listener function to be called when url changes.
4562 * @return {function(string)} Returns the registered listener fn - handy if the fn is anonymous.
4564 self.onUrlChange = function(callback) {
4565 // TODO(vojta): refactor to use node's syntax for events
4566 if (!urlChangeInit) {
4567 // We listen on both (hashchange/popstate) when available, as some browsers (e.g. Opera)
4568 // don't fire popstate when user change the address bar and don't fire hashchange when url
4569 // changed by push/replaceState
4571 // html5 history api - popstate event
4572 if ($sniffer.history) jqLite(window).on('popstate', fireUrlChange);
4574 if ($sniffer.hashchange) jqLite(window).on('hashchange', fireUrlChange);
4576 else self.addPollFn(fireUrlChange);
4578 urlChangeInit = true;
4581 urlChangeListeners.push(callback);
4586 * Checks whether the url has changed outside of Angular.
4587 * Needs to be exported to be able to check for changes that have been done in sync,
4588 * as hashchange/popstate events fire in async.
4590 self.$$checkUrlChange = fireUrlChange;
4592 //////////////////////////////////////////////////////////////
4594 //////////////////////////////////////////////////////////////
4597 * @name $browser#baseHref
4600 * Returns current <base href>
4601 * (always relative - without domain)
4603 * @returns {string} The current base href
4605 self.baseHref = function() {
4606 var href = baseElement.attr('href');
4607 return href ? href.replace(/^(https?\:)?\/\/[^\/]*/, '') : '';
4610 //////////////////////////////////////////////////////////////
4612 //////////////////////////////////////////////////////////////
4613 var lastCookies = {};
4614 var lastCookieString = '';
4615 var cookiePath = self.baseHref();
4618 * @name $browser#cookies
4620 * @param {string=} name Cookie name
4621 * @param {string=} value Cookie value
4624 * The cookies method provides a 'private' low level access to browser cookies.
4625 * It is not meant to be used directly, use the $cookie service instead.
4627 * The return values vary depending on the arguments that the method was called with as follows:
4629 * - cookies() -> hash of all cookies, this is NOT a copy of the internal state, so do not modify
4631 * - cookies(name, value) -> set name to value, if value is undefined delete the cookie
4632 * - cookies(name) -> the same as (name, undefined) == DELETES (no one calls it right now that
4635 * @returns {Object} Hash of all cookies (if called without any parameter)
4637 self.cookies = function(name, value) {
4638 /* global escape: false, unescape: false */
4639 var cookieLength, cookieArray, cookie, i, index;
4642 if (value === undefined) {
4643 rawDocument.cookie = escape(name) + "=;path=" + cookiePath +
4644 ";expires=Thu, 01 Jan 1970 00:00:00 GMT";
4646 if (isString(value)) {
4647 cookieLength = (rawDocument.cookie = escape(name) + '=' + escape(value) +
4648 ';path=' + cookiePath).length + 1;
4650 // per http://www.ietf.org/rfc/rfc2109.txt browser must allow at minimum:
4652 // - 20 cookies per unique domain
4653 // - 4096 bytes per cookie
4654 if (cookieLength > 4096) {
4655 $log.warn("Cookie '"+ name +
4656 "' possibly not set or overflowed because it was too large ("+
4657 cookieLength + " > 4096 bytes)!");
4662 if (rawDocument.cookie !== lastCookieString) {
4663 lastCookieString = rawDocument.cookie;
4664 cookieArray = lastCookieString.split("; ");
4667 for (i = 0; i < cookieArray.length; i++) {
4668 cookie = cookieArray[i];
4669 index = cookie.indexOf('=');
4670 if (index > 0) { //ignore nameless cookies
4671 name = unescape(cookie.substring(0, index));
4672 // the first value that is seen for a cookie is the most
4673 // specific one. values for the same cookie name that
4674 // follow are for less specific paths.
4675 if (lastCookies[name] === undefined) {
4676 lastCookies[name] = unescape(cookie.substring(index + 1));
4687 * @name $browser#defer
4688 * @param {function()} fn A function, who's execution should be deferred.
4689 * @param {number=} [delay=0] of milliseconds to defer the function execution.
4690 * @returns {*} DeferId that can be used to cancel the task via `$browser.defer.cancel()`.
4693 * Executes a fn asynchronously via `setTimeout(fn, delay)`.
4695 * Unlike when calling `setTimeout` directly, in test this function is mocked and instead of using
4696 * `setTimeout` in tests, the fns are queued in an array, which can be programmatically flushed
4697 * via `$browser.defer.flush()`.
4700 self.defer = function(fn, delay) {
4702 outstandingRequestCount++;
4703 timeoutId = setTimeout(function() {
4704 delete pendingDeferIds[timeoutId];
4705 completeOutstandingRequest(fn);
4707 pendingDeferIds[timeoutId] = true;
4713 * @name $browser#defer.cancel
4716 * Cancels a deferred task identified with `deferId`.
4718 * @param {*} deferId Token returned by the `$browser.defer` function.
4719 * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully
4722 self.defer.cancel = function(deferId) {
4723 if (pendingDeferIds[deferId]) {
4724 delete pendingDeferIds[deferId];
4725 clearTimeout(deferId);
4726 completeOutstandingRequest(noop);
4734 function $BrowserProvider(){
4735 this.$get = ['$window', '$log', '$sniffer', '$document',
4736 function( $window, $log, $sniffer, $document){
4737 return new Browser($window, $document, $log, $sniffer);
4743 * @name $cacheFactory
4746 * Factory that constructs {@link $cacheFactory.Cache Cache} objects and gives access to
4751 * var cache = $cacheFactory('cacheId');
4752 * expect($cacheFactory.get('cacheId')).toBe(cache);
4753 * expect($cacheFactory.get('noSuchCacheId')).not.toBeDefined();
4755 * cache.put("key", "value");
4756 * cache.put("another key", "another value");
4758 * // We've specified no options on creation
4759 * expect(cache.info()).toEqual({id: 'cacheId', size: 2});
4764 * @param {string} cacheId Name or id of the newly created cache.
4765 * @param {object=} options Options object that specifies the cache behavior. Properties:
4767 * - `{number=}` `capacity` — turns the cache into LRU cache.
4769 * @returns {object} Newly created cache object with the following set of methods:
4771 * - `{object}` `info()` — Returns id, size, and options of cache.
4772 * - `{{*}}` `put({string} key, {*} value)` — Puts a new key-value pair into the cache and returns
4774 * - `{{*}}` `get({string} key)` — Returns cached value for `key` or undefined for cache miss.
4775 * - `{void}` `remove({string} key)` — Removes a key-value pair from the cache.
4776 * - `{void}` `removeAll()` — Removes all cached values.
4777 * - `{void}` `destroy()` — Removes references to this cache from $cacheFactory.
4780 <example module="cacheExampleApp">
4781 <file name="index.html">
4782 <div ng-controller="CacheController">
4783 <input ng-model="newCacheKey" placeholder="Key">
4784 <input ng-model="newCacheValue" placeholder="Value">
4785 <button ng-click="put(newCacheKey, newCacheValue)">Cache</button>
4787 <p ng-if="keys.length">Cached Values</p>
4788 <div ng-repeat="key in keys">
4789 <span ng-bind="key"></span>
4791 <b ng-bind="cache.get(key)"></b>
4795 <div ng-repeat="(key, value) in cache.info()">
4796 <span ng-bind="key"></span>
4798 <b ng-bind="value"></b>
4802 <file name="script.js">
4803 angular.module('cacheExampleApp', []).
4804 controller('CacheController', ['$scope', '$cacheFactory', function($scope, $cacheFactory) {
4806 $scope.cache = $cacheFactory('cacheId');
4807 $scope.put = function(key, value) {
4808 if ($scope.cache.get(key) === undefined) {
4809 $scope.keys.push(key);
4811 $scope.cache.put(key, value === undefined ? null : value);
4815 <file name="style.css">
4822 function $CacheFactoryProvider() {
4824 this.$get = function() {
4827 function cacheFactory(cacheId, options) {
4828 if (cacheId in caches) {
4829 throw minErr('$cacheFactory')('iid', "CacheId '{0}' is already taken!", cacheId);
4833 stats = extend({}, options, {id: cacheId}),
4835 capacity = (options && options.capacity) || Number.MAX_VALUE,
4842 * @name $cacheFactory.Cache
4845 * A cache object used to store and retrieve data, primarily used by
4846 * {@link $http $http} and the {@link ng.directive:script script} directive to cache
4847 * templates and other data.
4850 * angular.module('superCache')
4851 * .factory('superCache', ['$cacheFactory', function($cacheFactory) {
4852 * return $cacheFactory('super-cache');
4859 * it('should behave like a cache', inject(function(superCache) {
4860 * superCache.put('key', 'value');
4861 * superCache.put('another key', 'another value');
4863 * expect(superCache.info()).toEqual({
4864 * id: 'super-cache',
4868 * superCache.remove('another key');
4869 * expect(superCache.get('another key')).toBeUndefined();
4871 * superCache.removeAll();
4872 * expect(superCache.info()).toEqual({
4873 * id: 'super-cache',
4879 return caches[cacheId] = {
4883 * @name $cacheFactory.Cache#put
4887 * Inserts a named entry into the {@link $cacheFactory.Cache Cache} object to be
4888 * retrieved later, and incrementing the size of the cache if the key was not already
4889 * present in the cache. If behaving like an LRU cache, it will also remove stale
4890 * entries from the set.
4892 * It will not insert undefined values into the cache.
4894 * @param {string} key the key under which the cached data is stored.
4895 * @param {*} value the value to store alongside the key. If it is undefined, the key
4896 * will not be stored.
4897 * @returns {*} the value stored.
4899 put: function(key, value) {
4900 if (capacity < Number.MAX_VALUE) {
4901 var lruEntry = lruHash[key] || (lruHash[key] = {key: key});
4906 if (isUndefined(value)) return;
4907 if (!(key in data)) size++;
4910 if (size > capacity) {
4911 this.remove(staleEnd.key);
4919 * @name $cacheFactory.Cache#get
4923 * Retrieves named data stored in the {@link $cacheFactory.Cache Cache} object.
4925 * @param {string} key the key of the data to be retrieved
4926 * @returns {*} the value stored.
4928 get: function(key) {
4929 if (capacity < Number.MAX_VALUE) {
4930 var lruEntry = lruHash[key];
4932 if (!lruEntry) return;
4943 * @name $cacheFactory.Cache#remove
4947 * Removes an entry from the {@link $cacheFactory.Cache Cache} object.
4949 * @param {string} key the key of the entry to be removed
4951 remove: function(key) {
4952 if (capacity < Number.MAX_VALUE) {
4953 var lruEntry = lruHash[key];
4955 if (!lruEntry) return;
4957 if (lruEntry == freshEnd) freshEnd = lruEntry.p;
4958 if (lruEntry == staleEnd) staleEnd = lruEntry.n;
4959 link(lruEntry.n,lruEntry.p);
4961 delete lruHash[key];
4971 * @name $cacheFactory.Cache#removeAll
4975 * Clears the cache object of any entries.
4977 removeAll: function() {
4981 freshEnd = staleEnd = null;
4987 * @name $cacheFactory.Cache#destroy
4991 * Destroys the {@link $cacheFactory.Cache Cache} object entirely,
4992 * removing it from the {@link $cacheFactory $cacheFactory} set.
4994 destroy: function() {
4998 delete caches[cacheId];
5004 * @name $cacheFactory.Cache#info
5008 * Retrieve information regarding a particular {@link $cacheFactory.Cache Cache}.
5010 * @returns {object} an object with the following properties:
5012 * <li>**id**: the id of the cache instance</li>
5013 * <li>**size**: the number of entries kept in the cache instance</li>
5014 * <li>**...**: any additional properties from the options object when creating the
5019 return extend({}, stats, {size: size});
5025 * makes the `entry` the freshEnd of the LRU linked list
5027 function refresh(entry) {
5028 if (entry != freshEnd) {
5031 } else if (staleEnd == entry) {
5035 link(entry.n, entry.p);
5036 link(entry, freshEnd);
5044 * bidirectionally links two entries of the LRU linked list
5046 function link(nextEntry, prevEntry) {
5047 if (nextEntry != prevEntry) {
5048 if (nextEntry) nextEntry.p = prevEntry; //p stands for previous, 'prev' didn't minify
5049 if (prevEntry) prevEntry.n = nextEntry; //n stands for next, 'next' didn't minify
5057 * @name $cacheFactory#info
5060 * Get information about all the caches that have been created
5062 * @returns {Object} - key-value map of `cacheId` to the result of calling `cache#info`
5064 cacheFactory.info = function() {
5066 forEach(caches, function(cache, cacheId) {
5067 info[cacheId] = cache.info();
5075 * @name $cacheFactory#get
5078 * Get access to a cache object by the `cacheId` used when it was created.
5080 * @param {string} cacheId Name or id of a cache to access.
5081 * @returns {object} Cache object identified by the cacheId or undefined if no such cache.
5083 cacheFactory.get = function(cacheId) {
5084 return caches[cacheId];
5088 return cacheFactory;
5094 * @name $templateCache
5097 * The first time a template is used, it is loaded in the template cache for quick retrieval. You
5098 * can load templates directly into the cache in a `script` tag, or by consuming the
5099 * `$templateCache` service directly.
5101 * Adding via the `script` tag:
5104 * <script type="text/ng-template" id="templateId.html">
5105 * <p>This is the content of the template</p>
5109 * **Note:** the `script` tag containing the template does not need to be included in the `head` of
5110 * the document, but it must be below the `ng-app` definition.
5112 * Adding via the $templateCache service:
5115 * var myApp = angular.module('myApp', []);
5116 * myApp.run(function($templateCache) {
5117 * $templateCache.put('templateId.html', 'This is the content of the template');
5121 * To retrieve the template later, simply use it in your HTML:
5123 * <div ng-include=" 'templateId.html' "></div>
5126 * or get it via Javascript:
5128 * $templateCache.get('templateId.html')
5131 * See {@link ng.$cacheFactory $cacheFactory}.
5134 function $TemplateCacheProvider() {
5135 this.$get = ['$cacheFactory', function($cacheFactory) {
5136 return $cacheFactory('templates');
5140 /* ! VARIABLE/FUNCTION NAMING CONVENTIONS THAT APPLY TO THIS FILE!
5142 * DOM-related variables:
5144 * - "node" - DOM Node
5145 * - "element" - DOM Element or Node
5146 * - "$node" or "$element" - jqLite-wrapped node or element
5149 * Compiler related stuff:
5151 * - "linkFn" - linking fn of a single directive
5152 * - "nodeLinkFn" - function that aggregates all linking fns for a particular node
5153 * - "childLinkFn" - function that aggregates all linking fns for child nodes of a particular node
5154 * - "compositeLinkFn" - function that aggregates all linking fns for a compilation root (nodeList)
5164 * Compiles an HTML string or DOM into a template and produces a template function, which
5165 * can then be used to link {@link ng.$rootScope.Scope `scope`} and the template together.
5167 * The compilation is a process of walking the DOM tree and matching DOM elements to
5168 * {@link ng.$compileProvider#directive directives}.
5170 * <div class="alert alert-warning">
5171 * **Note:** This document is an in-depth reference of all directive options.
5172 * For a gentle introduction to directives with examples of common use cases,
5173 * see the {@link guide/directive directive guide}.
5176 * ## Comprehensive Directive API
5178 * There are many different options for a directive.
5180 * The difference resides in the return value of the factory function.
5181 * You can either return a "Directive Definition Object" (see below) that defines the directive properties,
5182 * or just the `postLink` function (all other properties will have the default values).
5184 * <div class="alert alert-success">
5185 * **Best Practice:** It's recommended to use the "directive definition object" form.
5188 * Here's an example directive declared with a Directive Definition Object:
5191 * var myModule = angular.module(...);
5193 * myModule.directive('directiveName', function factory(injectables) {
5194 * var directiveDefinitionObject = {
5196 * template: '<div></div>', // or // function(tElement, tAttrs) { ... },
5198 * // templateUrl: 'directive.html', // or // function(tElement, tAttrs) { ... },
5199 * transclude: false,
5202 * controller: function($scope, $element, $attrs, $transclude, otherInjectables) { ... },
5203 * controllerAs: 'stringAlias',
5204 * require: 'siblingDirectiveName', // or // ['^parentDirectiveName', '?optionalDirectiveName', '?^optionalParent'],
5205 * compile: function compile(tElement, tAttrs, transclude) {
5207 * pre: function preLink(scope, iElement, iAttrs, controller) { ... },
5208 * post: function postLink(scope, iElement, iAttrs, controller) { ... }
5211 * // return function postLink( ... ) { ... }
5215 * // pre: function preLink(scope, iElement, iAttrs, controller) { ... },
5216 * // post: function postLink(scope, iElement, iAttrs, controller) { ... }
5219 * // link: function postLink( ... ) { ... }
5221 * return directiveDefinitionObject;
5225 * <div class="alert alert-warning">
5226 * **Note:** Any unspecified options will use the default value. You can see the default values below.
5229 * Therefore the above can be simplified as:
5232 * var myModule = angular.module(...);
5234 * myModule.directive('directiveName', function factory(injectables) {
5235 * var directiveDefinitionObject = {
5236 * link: function postLink(scope, iElement, iAttrs) { ... }
5238 * return directiveDefinitionObject;
5240 * // return function postLink(scope, iElement, iAttrs) { ... }
5246 * ### Directive Definition Object
5248 * The directive definition object provides instructions to the {@link ng.$compile
5249 * compiler}. The attributes are:
5252 * When there are multiple directives defined on a single DOM element, sometimes it
5253 * is necessary to specify the order in which the directives are applied. The `priority` is used
5254 * to sort the directives before their `compile` functions get called. Priority is defined as a
5255 * number. Directives with greater numerical `priority` are compiled first. Pre-link functions
5256 * are also run in priority order, but post-link functions are run in reverse order. The order
5257 * of directives with the same priority is undefined. The default priority is `0`.
5260 * If set to true then the current `priority` will be the last set of directives
5261 * which will execute (any directives at the current priority will still execute
5262 * as the order of execution on same `priority` is undefined).
5265 * **If set to `true`,** then a new scope will be created for this directive. If multiple directives on the
5266 * same element request a new scope, only one new scope is created. The new scope rule does not
5267 * apply for the root of the template since the root of the template always gets a new scope.
5269 * **If set to `{}` (object hash),** then a new "isolate" scope is created. The 'isolate' scope differs from
5270 * normal scope in that it does not prototypically inherit from the parent scope. This is useful
5271 * when creating reusable components, which should not accidentally read or modify data in the
5274 * The 'isolate' scope takes an object hash which defines a set of local scope properties
5275 * derived from the parent scope. These local properties are useful for aliasing values for
5276 * templates. Locals definition is a hash of local scope property to its source:
5278 * * `@` or `@attr` - bind a local scope property to the value of DOM attribute. The result is
5279 * always a string since DOM attributes are strings. If no `attr` name is specified then the
5280 * attribute name is assumed to be the same as the local name.
5281 * Given `<widget my-attr="hello {{name}}">` and widget definition
5282 * of `scope: { localName:'@myAttr' }`, then widget scope property `localName` will reflect
5283 * the interpolated value of `hello {{name}}`. As the `name` attribute changes so will the
5284 * `localName` property on the widget scope. The `name` is read from the parent scope (not
5287 * * `=` or `=attr` - set up bi-directional binding between a local scope property and the
5288 * parent scope property of name defined via the value of the `attr` attribute. If no `attr`
5289 * name is specified then the attribute name is assumed to be the same as the local name.
5290 * Given `<widget my-attr="parentModel">` and widget definition of
5291 * `scope: { localModel:'=myAttr' }`, then widget scope property `localModel` will reflect the
5292 * value of `parentModel` on the parent scope. Any changes to `parentModel` will be reflected
5293 * in `localModel` and any changes in `localModel` will reflect in `parentModel`. If the parent
5294 * scope property doesn't exist, it will throw a NON_ASSIGNABLE_MODEL_EXPRESSION exception. You
5295 * can avoid this behavior using `=?` or `=?attr` in order to flag the property as optional.
5297 * * `&` or `&attr` - provides a way to execute an expression in the context of the parent scope.
5298 * If no `attr` name is specified then the attribute name is assumed to be the same as the
5299 * local name. Given `<widget my-attr="count = count + value">` and widget definition of
5300 * `scope: { localFn:'&myAttr' }`, then isolate scope property `localFn` will point to
5301 * a function wrapper for the `count = count + value` expression. Often it's desirable to
5302 * pass data from the isolated scope via an expression to the parent scope, this can be
5303 * done by passing a map of local variable names and values into the expression wrapper fn.
5304 * For example, if the expression is `increment(amount)` then we can specify the amount value
5305 * by calling the `localFn` as `localFn({amount: 22})`.
5310 * Controller constructor function. The controller is instantiated before the
5311 * pre-linking phase and it is shared with other directives (see
5312 * `require` attribute). This allows the directives to communicate with each other and augment
5313 * each other's behavior. The controller is injectable (and supports bracket notation) with the following locals:
5315 * * `$scope` - Current scope associated with the element
5316 * * `$element` - Current element
5317 * * `$attrs` - Current attributes object for the element
5318 * * `$transclude` - A transclude linking function pre-bound to the correct transclusion scope.
5319 * The scope can be overridden by an optional first argument.
5320 * `function([scope], cloneLinkingFn)`.
5324 * Require another directive and inject its controller as the fourth argument to the linking function. The
5325 * `require` takes a string name (or array of strings) of the directive(s) to pass in. If an array is used, the
5326 * injected argument will be an array in corresponding order. If no such directive can be
5327 * found, or if the directive does not have a controller, then an error is raised. The name can be prefixed with:
5329 * * (no prefix) - Locate the required controller on the current element. Throw an error if not found.
5330 * * `?` - Attempt to locate the required controller or pass `null` to the `link` fn if not found.
5331 * * `^` - Locate the required controller by searching the element and its parents. Throw an error if not found.
5332 * * `?^` - Attempt to locate the required controller by searching the element and its parents or pass
5333 * `null` to the `link` fn if not found.
5336 * #### `controllerAs`
5337 * Controller alias at the directive scope. An alias for the controller so it
5338 * can be referenced at the directive template. The directive needs to define a scope for this
5339 * configuration to be used. Useful in the case when directive is used as component.
5343 * String of subset of `EACM` which restricts the directive to a specific directive
5344 * declaration style. If omitted, the default (attributes only) is used.
5346 * * `E` - Element name: `<my-directive></my-directive>`
5347 * * `A` - Attribute (default): `<div my-directive="exp"></div>`
5348 * * `C` - Class: `<div class="my-directive: exp;"></div>`
5349 * * `M` - Comment: `<!-- directive: my-directive exp -->`
5353 * HTML markup that may:
5354 * * Replace the contents of the directive's element (default).
5355 * * Replace the directive's element itself (if `replace` is true - DEPRECATED).
5356 * * Wrap the contents of the directive's element (if `transclude` is true).
5360 * * A string. For example `<div red-on-hover>{{delete_str}}</div>`.
5361 * * A function which takes two arguments `tElement` and `tAttrs` (described in the `compile`
5362 * function api below) and returns a string value.
5365 * #### `templateUrl`
5366 * Same as `template` but the template is loaded from the specified URL. Because
5367 * the template loading is asynchronous the compilation/linking is suspended until the template
5370 * You can specify `templateUrl` as a string representing the URL or as a function which takes two
5371 * arguments `tElement` and `tAttrs` (described in the `compile` function api below) and returns
5372 * a string value representing the url. In either case, the template URL is passed through {@link
5373 * api/ng.$sce#getTrustedResourceUrl $sce.getTrustedResourceUrl}.
5376 * #### `replace` ([*DEPRECATED*!], will be removed in next major release)
5377 * specify what the template should replace. Defaults to `false`.
5379 * * `true` - the template will replace the directive's element.
5380 * * `false` - the template will replace the contents of the directive's element.
5382 * The replacement process migrates all of the attributes / classes from the old element to the new
5383 * one. See the {@link guide/directive#creating-custom-directives_creating-directives_template-expanding-directive
5384 * Directives Guide} for an example.
5387 * compile the content of the element and make it available to the directive.
5388 * Typically used with {@link ng.directive:ngTransclude
5389 * ngTransclude}. The advantage of transclusion is that the linking function receives a
5390 * transclusion function which is pre-bound to the correct scope. In a typical setup the widget
5391 * creates an `isolate` scope, but the transclusion is not a child, but a sibling of the `isolate`
5392 * scope. This makes it possible for the widget to have private state, and the transclusion to
5393 * be bound to the parent (pre-`isolate`) scope.
5395 * * `true` - transclude the content of the directive.
5396 * * `'element'` - transclude the whole element including any directives defined at lower priority.
5398 * <div class="alert alert-warning">
5399 * **Note:** When testing an element transclude directive you must not place the directive at the root of the
5400 * DOM fragment that is being compiled. See {@link guide/unit-testing#testing-transclusion-directives
5401 * Testing Transclusion Directives}.
5407 * function compile(tElement, tAttrs, transclude) { ... }
5410 * The compile function deals with transforming the template DOM. Since most directives do not do
5411 * template transformation, it is not used often. The compile function takes the following arguments:
5413 * * `tElement` - template element - The element where the directive has been declared. It is
5414 * safe to do template transformation on the element and child elements only.
5416 * * `tAttrs` - template attributes - Normalized list of attributes declared on this element shared
5417 * between all directive compile functions.
5419 * * `transclude` - [*DEPRECATED*!] A transclude linking function: `function(scope, cloneLinkingFn)`
5421 * <div class="alert alert-warning">
5422 * **Note:** The template instance and the link instance may be different objects if the template has
5423 * been cloned. For this reason it is **not** safe to do anything other than DOM transformations that
5424 * apply to all cloned DOM nodes within the compile function. Specifically, DOM listener registration
5425 * should be done in a linking function rather than in a compile function.
5428 * <div class="alert alert-warning">
5429 * **Note:** The compile function cannot handle directives that recursively use themselves in their
5430 * own templates or compile functions. Compiling these directives results in an infinite loop and a
5431 * stack overflow errors.
5433 * This can be avoided by manually using $compile in the postLink function to imperatively compile
5434 * a directive's template instead of relying on automatic template compilation via `template` or
5435 * `templateUrl` declaration or manual compilation inside the compile function.
5438 * <div class="alert alert-error">
5439 * **Note:** The `transclude` function that is passed to the compile function is deprecated, as it
5440 * e.g. does not know about the right outer scope. Please use the transclude function that is passed
5441 * to the link function instead.
5444 * A compile function can have a return value which can be either a function or an object.
5446 * * returning a (post-link) function - is equivalent to registering the linking function via the
5447 * `link` property of the config object when the compile function is empty.
5449 * * returning an object with function(s) registered via `pre` and `post` properties - allows you to
5450 * control when a linking function should be called during the linking phase. See info about
5451 * pre-linking and post-linking functions below.
5455 * This property is used only if the `compile` property is not defined.
5458 * function link(scope, iElement, iAttrs, controller, transcludeFn) { ... }
5461 * The link function is responsible for registering DOM listeners as well as updating the DOM. It is
5462 * executed after the template has been cloned. This is where most of the directive logic will be
5465 * * `scope` - {@link ng.$rootScope.Scope Scope} - The scope to be used by the
5466 * directive for registering {@link ng.$rootScope.Scope#$watch watches}.
5468 * * `iElement` - instance element - The element where the directive is to be used. It is safe to
5469 * manipulate the children of the element only in `postLink` function since the children have
5470 * already been linked.
5472 * * `iAttrs` - instance attributes - Normalized list of attributes declared on this element shared
5473 * between all directive linking functions.
5475 * * `controller` - a controller instance - A controller instance if at least one directive on the
5476 * element defines a controller. The controller is shared among all the directives, which allows
5477 * the directives to use the controllers as a communication channel.
5479 * * `transcludeFn` - A transclude linking function pre-bound to the correct transclusion scope.
5480 * The scope can be overridden by an optional first argument. This is the same as the `$transclude`
5481 * parameter of directive controllers.
5482 * `function([scope], cloneLinkingFn)`.
5485 * #### Pre-linking function
5487 * Executed before the child elements are linked. Not safe to do DOM transformation since the
5488 * compiler linking function will fail to locate the correct elements for linking.
5490 * #### Post-linking function
5492 * Executed after the child elements are linked. It is safe to do DOM transformation in the post-linking function.
5494 * <a name="Attributes"></a>
5497 * The {@link ng.$compile.directive.Attributes Attributes} object - passed as a parameter in the
5498 * `link()` or `compile()` functions. It has a variety of uses.
5500 * accessing *Normalized attribute names:*
5501 * Directives like 'ngBind' can be expressed in many ways: 'ng:bind', `data-ng-bind`, or 'x-ng-bind'.
5502 * the attributes object allows for normalized access to
5505 * * *Directive inter-communication:* All directives share the same instance of the attributes
5506 * object which allows the directives to use the attributes object as inter directive
5509 * * *Supports interpolation:* Interpolation attributes are assigned to the attribute object
5510 * allowing other directives to read the interpolated value.
5512 * * *Observing interpolated attributes:* Use `$observe` to observe the value changes of attributes
5513 * that contain interpolation (e.g. `src="{{bar}}"`). Not only is this very efficient but it's also
5514 * the only way to easily get the actual value because during the linking phase the interpolation
5515 * hasn't been evaluated yet and so the value is at this time set to `undefined`.
5518 * function linkingFn(scope, elm, attrs, ctrl) {
5519 * // get the attribute value
5520 * console.log(attrs.ngModel);
5522 * // change the attribute
5523 * attrs.$set('ngModel', 'new value');
5525 * // observe changes to interpolated attribute
5526 * attrs.$observe('ngModel', function(value) {
5527 * console.log('ngModel has changed value to ' + value);
5532 * Below is an example using `$compileProvider`.
5534 * <div class="alert alert-warning">
5535 * **Note**: Typically directives are registered with `module.directive`. The example below is
5536 * to illustrate how `$compile` works.
5539 <example module="compileExample">
5540 <file name="index.html">
5542 angular.module('compileExample', [], function($compileProvider) {
5543 // configure new 'compile' directive by passing a directive
5544 // factory function. The factory function injects the '$compile'
5545 $compileProvider.directive('compile', function($compile) {
5546 // directive factory creates a link function
5547 return function(scope, element, attrs) {
5550 // watch the 'compile' expression for changes
5551 return scope.$eval(attrs.compile);
5554 // when the 'compile' expression changes
5555 // assign it into the current DOM
5556 element.html(value);
5558 // compile the new DOM and link it to the current
5560 // NOTE: we only compile .childNodes so that
5561 // we don't get into infinite loop compiling ourselves
5562 $compile(element.contents())(scope);
5568 .controller('GreeterController', ['$scope', function($scope) {
5569 $scope.name = 'Angular';
5570 $scope.html = 'Hello {{name}}';
5573 <div ng-controller="GreeterController">
5574 <input ng-model="name"> <br>
5575 <textarea ng-model="html"></textarea> <br>
5576 <div compile="html"></div>
5579 <file name="protractor.js" type="protractor">
5580 it('should auto compile', function() {
5581 var textarea = $('textarea');
5582 var output = $('div[compile]');
5583 // The initial state reads 'Hello Angular'.
5584 expect(output.getText()).toBe('Hello Angular');
5586 textarea.sendKeys('{{name}}!');
5587 expect(output.getText()).toBe('Angular!');
5594 * @param {string|DOMElement} element Element or HTML string to compile into a template function.
5595 * @param {function(angular.Scope, cloneAttachFn=)} transclude function available to directives.
5596 * @param {number} maxPriority only apply directives lower than given priority (Only effects the
5597 * root element(s), not their children)
5598 * @returns {function(scope, cloneAttachFn=)} a link function which is used to bind template
5599 * (a DOM element/tree) to a scope. Where:
5601 * * `scope` - A {@link ng.$rootScope.Scope Scope} to bind to.
5602 * * `cloneAttachFn` - If `cloneAttachFn` is provided, then the link function will clone the
5603 * `template` and call the `cloneAttachFn` function allowing the caller to attach the
5604 * cloned elements to the DOM document at the appropriate place. The `cloneAttachFn` is
5605 * called as: <br> `cloneAttachFn(clonedElement, scope)` where:
5607 * * `clonedElement` - is a clone of the original `element` passed into the compiler.
5608 * * `scope` - is the current scope with which the linking function is working with.
5610 * Calling the linking function returns the element of the template. It is either the original
5611 * element passed in, or the clone of the element if the `cloneAttachFn` is provided.
5613 * After linking the view is not updated until after a call to $digest which typically is done by
5614 * Angular automatically.
5616 * If you need access to the bound view, there are two ways to do it:
5618 * - If you are not asking the linking function to clone the template, create the DOM element(s)
5619 * before you send them to the compiler and keep this reference around.
5621 * var element = $compile('<p>{{total}}</p>')(scope);
5624 * - if on the other hand, you need the element to be cloned, the view reference from the original
5625 * example would not point to the clone, but rather to the original template that was cloned. In
5626 * this case, you can access the clone via the cloneAttachFn:
5628 * var templateElement = angular.element('<p>{{total}}</p>'),
5631 * var clonedElement = $compile(templateElement)(scope, function(clonedElement, scope) {
5632 * //attach the clone to DOM document at the right place
5635 * //now we have reference to the cloned DOM via `clonedElement`
5639 * For information on how the compiler works, see the
5640 * {@link guide/compiler Angular HTML Compiler} section of the Developer Guide.
5643 var $compileMinErr = minErr('$compile');
5647 * @name $compileProvider
5652 $CompileProvider.$inject = ['$provide', '$$sanitizeUriProvider'];
5653 function $CompileProvider($provide, $$sanitizeUriProvider) {
5654 var hasDirectives = {},
5655 Suffix = 'Directive',
5656 COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\d\w_\-]+)\s+(.*)$/,
5657 CLASS_DIRECTIVE_REGEXP = /(([\d\w_\-]+)(?:\:([^;]+))?;?)/;
5659 // Ref: http://developers.whatwg.org/webappapis.html#event-handler-idl-attributes
5660 // The assumption is that future DOM event attribute names will begin with
5661 // 'on' and be composed of only English letters.
5662 var EVENT_HANDLER_ATTR_REGEXP = /^(on[a-z]+|formaction)$/;
5666 * @name $compileProvider#directive
5670 * Register a new directive with the compiler.
5672 * @param {string|Object} name Name of the directive in camel-case (i.e. <code>ngBind</code> which
5673 * will match as <code>ng-bind</code>), or an object map of directives where the keys are the
5674 * names and the values are the factories.
5675 * @param {Function|Array} directiveFactory An injectable directive factory function. See
5676 * {@link guide/directive} for more info.
5677 * @returns {ng.$compileProvider} Self for chaining.
5679 this.directive = function registerDirective(name, directiveFactory) {
5680 assertNotHasOwnProperty(name, 'directive');
5681 if (isString(name)) {
5682 assertArg(directiveFactory, 'directiveFactory');
5683 if (!hasDirectives.hasOwnProperty(name)) {
5684 hasDirectives[name] = [];
5685 $provide.factory(name + Suffix, ['$injector', '$exceptionHandler',
5686 function($injector, $exceptionHandler) {
5687 var directives = [];
5688 forEach(hasDirectives[name], function(directiveFactory, index) {
5690 var directive = $injector.invoke(directiveFactory);
5691 if (isFunction(directive)) {
5692 directive = { compile: valueFn(directive) };
5693 } else if (!directive.compile && directive.link) {
5694 directive.compile = valueFn(directive.link);
5696 directive.priority = directive.priority || 0;
5697 directive.index = index;
5698 directive.name = directive.name || name;
5699 directive.require = directive.require || (directive.controller && directive.name);
5700 directive.restrict = directive.restrict || 'A';
5701 directives.push(directive);
5703 $exceptionHandler(e);
5709 hasDirectives[name].push(directiveFactory);
5711 forEach(name, reverseParams(registerDirective));
5719 * @name $compileProvider#aHrefSanitizationWhitelist
5723 * Retrieves or overrides the default regular expression that is used for whitelisting of safe
5724 * urls during a[href] sanitization.
5726 * The sanitization is a security measure aimed at prevent XSS attacks via html links.
5728 * Any url about to be assigned to a[href] via data-binding is first normalized and turned into
5729 * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist`
5730 * regular expression. If a match is found, the original url is written into the dom. Otherwise,
5731 * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
5733 * @param {RegExp=} regexp New regexp to whitelist urls with.
5734 * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
5735 * chaining otherwise.
5737 this.aHrefSanitizationWhitelist = function(regexp) {
5738 if (isDefined(regexp)) {
5739 $$sanitizeUriProvider.aHrefSanitizationWhitelist(regexp);
5742 return $$sanitizeUriProvider.aHrefSanitizationWhitelist();
5749 * @name $compileProvider#imgSrcSanitizationWhitelist
5753 * Retrieves or overrides the default regular expression that is used for whitelisting of safe
5754 * urls during img[src] sanitization.
5756 * The sanitization is a security measure aimed at prevent XSS attacks via html links.
5758 * Any url about to be assigned to img[src] via data-binding is first normalized and turned into
5759 * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist`
5760 * regular expression. If a match is found, the original url is written into the dom. Otherwise,
5761 * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
5763 * @param {RegExp=} regexp New regexp to whitelist urls with.
5764 * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
5765 * chaining otherwise.
5767 this.imgSrcSanitizationWhitelist = function(regexp) {
5768 if (isDefined(regexp)) {
5769 $$sanitizeUriProvider.imgSrcSanitizationWhitelist(regexp);
5772 return $$sanitizeUriProvider.imgSrcSanitizationWhitelist();
5777 '$injector', '$interpolate', '$exceptionHandler', '$http', '$templateCache', '$parse',
5778 '$controller', '$rootScope', '$document', '$sce', '$animate', '$$sanitizeUri',
5779 function($injector, $interpolate, $exceptionHandler, $http, $templateCache, $parse,
5780 $controller, $rootScope, $document, $sce, $animate, $$sanitizeUri) {
5782 var Attributes = function(element, attr) {
5783 this.$$element = element;
5784 this.$attr = attr || {};
5787 Attributes.prototype = {
5788 $normalize: directiveNormalize,
5793 * @name $compile.directive.Attributes#$addClass
5797 * Adds the CSS class value specified by the classVal parameter to the element. If animations
5798 * are enabled then an animation will be triggered for the class addition.
5800 * @param {string} classVal The className value that will be added to the element
5802 $addClass : function(classVal) {
5803 if(classVal && classVal.length > 0) {
5804 $animate.addClass(this.$$element, classVal);
5810 * @name $compile.directive.Attributes#$removeClass
5814 * Removes the CSS class value specified by the classVal parameter from the element. If
5815 * animations are enabled then an animation will be triggered for the class removal.
5817 * @param {string} classVal The className value that will be removed from the element
5819 $removeClass : function(classVal) {
5820 if(classVal && classVal.length > 0) {
5821 $animate.removeClass(this.$$element, classVal);
5827 * @name $compile.directive.Attributes#$updateClass
5831 * Adds and removes the appropriate CSS class values to the element based on the difference
5832 * between the new and old CSS class values (specified as newClasses and oldClasses).
5834 * @param {string} newClasses The current CSS className value
5835 * @param {string} oldClasses The former CSS className value
5837 $updateClass : function(newClasses, oldClasses) {
5838 var toAdd = tokenDifference(newClasses, oldClasses);
5839 var toRemove = tokenDifference(oldClasses, newClasses);
5841 if(toAdd.length === 0) {
5842 $animate.removeClass(this.$$element, toRemove);
5843 } else if(toRemove.length === 0) {
5844 $animate.addClass(this.$$element, toAdd);
5846 $animate.setClass(this.$$element, toAdd, toRemove);
5851 * Set a normalized attribute on the element in a way such that all directives
5852 * can share the attribute. This function properly handles boolean attributes.
5853 * @param {string} key Normalized key. (ie ngAttribute)
5854 * @param {string|boolean} value The value to set. If `null` attribute will be deleted.
5855 * @param {boolean=} writeAttr If false, does not write the value to DOM element attribute.
5857 * @param {string=} attrName Optional none normalized name. Defaults to key.
5859 $set: function(key, value, writeAttr, attrName) {
5860 // TODO: decide whether or not to throw an error if "class"
5861 //is set through this function since it may cause $updateClass to
5864 var booleanKey = getBooleanAttrName(this.$$element[0], key),
5869 this.$$element.prop(key, value);
5870 attrName = booleanKey;
5875 // translate normalized key to actual key
5877 this.$attr[key] = attrName;
5879 attrName = this.$attr[key];
5881 this.$attr[key] = attrName = snake_case(key, '-');
5885 nodeName = nodeName_(this.$$element);
5887 // sanitize a[href] and img[src] values
5888 if ((nodeName === 'A' && key === 'href') ||
5889 (nodeName === 'IMG' && key === 'src')) {
5890 this[key] = value = $$sanitizeUri(value, key === 'src');
5893 if (writeAttr !== false) {
5894 if (value === null || value === undefined) {
5895 this.$$element.removeAttr(attrName);
5897 this.$$element.attr(attrName, value);
5902 var $$observers = this.$$observers;
5903 $$observers && forEach($$observers[key], function(fn) {
5907 $exceptionHandler(e);
5915 * @name $compile.directive.Attributes#$observe
5919 * Observes an interpolated attribute.
5921 * The observer function will be invoked once during the next `$digest` following
5922 * compilation. The observer is then invoked whenever the interpolated value
5925 * @param {string} key Normalized key. (ie ngAttribute) .
5926 * @param {function(interpolatedValue)} fn Function that will be called whenever
5927 the interpolated value of the attribute changes.
5928 * See the {@link guide/directive#Attributes Directives} guide for more info.
5929 * @returns {function()} the `fn` parameter.
5931 $observe: function(key, fn) {
5933 $$observers = (attrs.$$observers || (attrs.$$observers = {})),
5934 listeners = ($$observers[key] || ($$observers[key] = []));
5937 $rootScope.$evalAsync(function() {
5938 if (!listeners.$$inter) {
5939 // no one registered attribute interpolation function, so lets call it manually
5947 var startSymbol = $interpolate.startSymbol(),
5948 endSymbol = $interpolate.endSymbol(),
5949 denormalizeTemplate = (startSymbol == '{{' || endSymbol == '}}')
5951 : function denormalizeTemplate(template) {
5952 return template.replace(/\{\{/g, startSymbol).replace(/}}/g, endSymbol);
5954 NG_ATTR_BINDING = /^ngAttr[A-Z]/;
5959 //================================
5961 function compile($compileNodes, transcludeFn, maxPriority, ignoreDirective,
5962 previousCompileContext) {
5963 if (!($compileNodes instanceof jqLite)) {
5964 // jquery always rewraps, whereas we need to preserve the original selector so that we can
5966 $compileNodes = jqLite($compileNodes);
5968 // We can not compile top level text elements since text nodes can be merged and we will
5969 // not be able to attach scope data to them, so we will wrap them in <span>
5970 forEach($compileNodes, function(node, index){
5971 if (node.nodeType == 3 /* text node */ && node.nodeValue.match(/\S+/) /* non-empty */ ) {
5972 $compileNodes[index] = node = jqLite(node).wrap('<span></span>').parent()[0];
5975 var compositeLinkFn =
5976 compileNodes($compileNodes, transcludeFn, $compileNodes,
5977 maxPriority, ignoreDirective, previousCompileContext);
5978 safeAddClass($compileNodes, 'ng-scope');
5979 return function publicLinkFn(scope, cloneConnectFn, transcludeControllers, parentBoundTranscludeFn){
5980 assertArg(scope, 'scope');
5981 // important!!: we must call our jqLite.clone() since the jQuery one is trying to be smart
5982 // and sometimes changes the structure of the DOM.
5983 var $linkNode = cloneConnectFn
5984 ? JQLitePrototype.clone.call($compileNodes) // IMPORTANT!!!
5987 forEach(transcludeControllers, function(instance, name) {
5988 $linkNode.data('$' + name + 'Controller', instance);
5991 // Attach scope only to non-text nodes.
5992 for(var i = 0, ii = $linkNode.length; i<ii; i++) {
5993 var node = $linkNode[i],
5994 nodeType = node.nodeType;
5995 if (nodeType === 1 /* element */ || nodeType === 9 /* document */) {
5996 $linkNode.eq(i).data('$scope', scope);
6000 if (cloneConnectFn) cloneConnectFn($linkNode, scope);
6001 if (compositeLinkFn) compositeLinkFn(scope, $linkNode, $linkNode, parentBoundTranscludeFn);
6006 function safeAddClass($element, className) {
6008 $element.addClass(className);
6010 // ignore, since it means that we are trying to set class on
6011 // SVG element, where class name is read-only.
6016 * Compile function matches each node in nodeList against the directives. Once all directives
6017 * for a particular node are collected their compile functions are executed. The compile
6018 * functions return values - the linking functions - are combined into a composite linking
6019 * function, which is the a linking function for the node.
6021 * @param {NodeList} nodeList an array of nodes or NodeList to compile
6022 * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the
6023 * scope argument is auto-generated to the new child of the transcluded parent scope.
6024 * @param {DOMElement=} $rootElement If the nodeList is the root of the compilation tree then
6025 * the rootElement must be set the jqLite collection of the compile root. This is
6026 * needed so that the jqLite collection items can be replaced with widgets.
6027 * @param {number=} maxPriority Max directive priority.
6028 * @returns {Function} A composite linking function of all of the matched directives or null.
6030 function compileNodes(nodeList, transcludeFn, $rootElement, maxPriority, ignoreDirective,
6031 previousCompileContext) {
6033 attrs, directives, nodeLinkFn, childNodes, childLinkFn, linkFnFound;
6035 for (var i = 0; i < nodeList.length; i++) {
6036 attrs = new Attributes();
6038 // we must always refer to nodeList[i] since the nodes can be replaced underneath us.
6039 directives = collectDirectives(nodeList[i], [], attrs, i === 0 ? maxPriority : undefined,
6042 nodeLinkFn = (directives.length)
6043 ? applyDirectivesToNode(directives, nodeList[i], attrs, transcludeFn, $rootElement,
6044 null, [], [], previousCompileContext)
6047 if (nodeLinkFn && nodeLinkFn.scope) {
6048 safeAddClass(attrs.$$element, 'ng-scope');
6051 childLinkFn = (nodeLinkFn && nodeLinkFn.terminal ||
6052 !(childNodes = nodeList[i].childNodes) ||
6055 : compileNodes(childNodes,
6057 (nodeLinkFn.transcludeOnThisElement || !nodeLinkFn.templateOnThisElement)
6058 && nodeLinkFn.transclude) : transcludeFn);
6060 linkFns.push(nodeLinkFn, childLinkFn);
6061 linkFnFound = linkFnFound || nodeLinkFn || childLinkFn;
6062 //use the previous context only for the first element in the virtual group
6063 previousCompileContext = null;
6066 // return a linking function if we have found anything, null otherwise
6067 return linkFnFound ? compositeLinkFn : null;
6069 function compositeLinkFn(scope, nodeList, $rootElement, parentBoundTranscludeFn) {
6070 var nodeLinkFn, childLinkFn, node, childScope, i, ii, n, childBoundTranscludeFn;
6072 // copy nodeList so that linking doesn't break due to live list updates.
6073 var nodeListLength = nodeList.length,
6074 stableNodeList = new Array(nodeListLength);
6075 for (i = 0; i < nodeListLength; i++) {
6076 stableNodeList[i] = nodeList[i];
6079 for(i = 0, n = 0, ii = linkFns.length; i < ii; n++) {
6080 node = stableNodeList[n];
6081 nodeLinkFn = linkFns[i++];
6082 childLinkFn = linkFns[i++];
6085 if (nodeLinkFn.scope) {
6086 childScope = scope.$new();
6087 jqLite.data(node, '$scope', childScope);
6092 if ( nodeLinkFn.transcludeOnThisElement ) {
6093 childBoundTranscludeFn = createBoundTranscludeFn(scope, nodeLinkFn.transclude, parentBoundTranscludeFn);
6095 } else if (!nodeLinkFn.templateOnThisElement && parentBoundTranscludeFn) {
6096 childBoundTranscludeFn = parentBoundTranscludeFn;
6098 } else if (!parentBoundTranscludeFn && transcludeFn) {
6099 childBoundTranscludeFn = createBoundTranscludeFn(scope, transcludeFn);
6102 childBoundTranscludeFn = null;
6105 nodeLinkFn(childLinkFn, childScope, node, $rootElement, childBoundTranscludeFn);
6107 } else if (childLinkFn) {
6108 childLinkFn(scope, node.childNodes, undefined, parentBoundTranscludeFn);
6114 function createBoundTranscludeFn(scope, transcludeFn, previousBoundTranscludeFn) {
6116 var boundTranscludeFn = function(transcludedScope, cloneFn, controllers) {
6117 var scopeCreated = false;
6119 if (!transcludedScope) {
6120 transcludedScope = scope.$new();
6121 transcludedScope.$$transcluded = true;
6122 scopeCreated = true;
6125 var clone = transcludeFn(transcludedScope, cloneFn, controllers, previousBoundTranscludeFn);
6127 clone.on('$destroy', function() { transcludedScope.$destroy(); });
6132 return boundTranscludeFn;
6136 * Looks for directives on the given node and adds them to the directive collection which is
6139 * @param node Node to search.
6140 * @param directives An array to which the directives are added to. This array is sorted before
6141 * the function returns.
6142 * @param attrs The shared attrs object which is used to populate the normalized attributes.
6143 * @param {number=} maxPriority Max directive priority.
6145 function collectDirectives(node, directives, attrs, maxPriority, ignoreDirective) {
6146 var nodeType = node.nodeType,
6147 attrsMap = attrs.$attr,
6152 case 1: /* Element */
6153 // use the node name: <directive>
6154 addDirective(directives,
6155 directiveNormalize(nodeName_(node).toLowerCase()), 'E', maxPriority, ignoreDirective);
6157 // iterate over the attributes
6158 for (var attr, name, nName, ngAttrName, value, isNgAttr, nAttrs = node.attributes,
6159 j = 0, jj = nAttrs && nAttrs.length; j < jj; j++) {
6160 var attrStartName = false;
6161 var attrEndName = false;
6164 if (!msie || msie >= 8 || attr.specified) {
6166 value = trim(attr.value);
6168 // support ngAttr attribute binding
6169 ngAttrName = directiveNormalize(name);
6170 if (isNgAttr = NG_ATTR_BINDING.test(ngAttrName)) {
6171 name = snake_case(ngAttrName.substr(6), '-');
6174 var directiveNName = ngAttrName.replace(/(Start|End)$/, '');
6175 if (ngAttrName === directiveNName + 'Start') {
6176 attrStartName = name;
6177 attrEndName = name.substr(0, name.length - 5) + 'end';
6178 name = name.substr(0, name.length - 6);
6181 nName = directiveNormalize(name.toLowerCase());
6182 attrsMap[nName] = name;
6183 if (isNgAttr || !attrs.hasOwnProperty(nName)) {
6184 attrs[nName] = value;
6185 if (getBooleanAttrName(node, nName)) {
6186 attrs[nName] = true; // presence means true
6189 addAttrInterpolateDirective(node, directives, value, nName);
6190 addDirective(directives, nName, 'A', maxPriority, ignoreDirective, attrStartName,
6195 // use class as directive
6196 className = node.className;
6197 if (isString(className) && className !== '') {
6198 while (match = CLASS_DIRECTIVE_REGEXP.exec(className)) {
6199 nName = directiveNormalize(match[2]);
6200 if (addDirective(directives, nName, 'C', maxPriority, ignoreDirective)) {
6201 attrs[nName] = trim(match[3]);
6203 className = className.substr(match.index + match[0].length);
6207 case 3: /* Text Node */
6208 addTextInterpolateDirective(directives, node.nodeValue);
6210 case 8: /* Comment */
6212 match = COMMENT_DIRECTIVE_REGEXP.exec(node.nodeValue);
6214 nName = directiveNormalize(match[1]);
6215 if (addDirective(directives, nName, 'M', maxPriority, ignoreDirective)) {
6216 attrs[nName] = trim(match[2]);
6220 // turns out that under some circumstances IE9 throws errors when one attempts to read
6221 // comment's node value.
6222 // Just ignore it and continue. (Can't seem to reproduce in test case.)
6227 directives.sort(byPriority);
6232 * Given a node with an directive-start it collects all of the siblings until it finds
6239 function groupScan(node, attrStart, attrEnd) {
6242 if (attrStart && node.hasAttribute && node.hasAttribute(attrStart)) {
6243 var startNode = node;
6246 throw $compileMinErr('uterdir',
6247 "Unterminated attribute, found '{0}' but no matching '{1}' found.",
6248 attrStart, attrEnd);
6250 if (node.nodeType == 1 /** Element **/) {
6251 if (node.hasAttribute(attrStart)) depth++;
6252 if (node.hasAttribute(attrEnd)) depth--;
6255 node = node.nextSibling;
6256 } while (depth > 0);
6261 return jqLite(nodes);
6265 * Wrapper for linking function which converts normal linking function into a grouped
6270 * @returns {Function}
6272 function groupElementsLinkFnWrapper(linkFn, attrStart, attrEnd) {
6273 return function(scope, element, attrs, controllers, transcludeFn) {
6274 element = groupScan(element[0], attrStart, attrEnd);
6275 return linkFn(scope, element, attrs, controllers, transcludeFn);
6280 * Once the directives have been collected, their compile functions are executed. This method
6281 * is responsible for inlining directive templates as well as terminating the application
6282 * of the directives if the terminal directive has been reached.
6284 * @param {Array} directives Array of collected directives to execute their compile function.
6285 * this needs to be pre-sorted by priority order.
6286 * @param {Node} compileNode The raw DOM node to apply the compile functions to
6287 * @param {Object} templateAttrs The shared attribute function
6288 * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the
6289 * scope argument is auto-generated to the new
6290 * child of the transcluded parent scope.
6291 * @param {JQLite} jqCollection If we are working on the root of the compile tree then this
6292 * argument has the root jqLite array so that we can replace nodes
6294 * @param {Object=} originalReplaceDirective An optional directive that will be ignored when
6295 * compiling the transclusion.
6296 * @param {Array.<Function>} preLinkFns
6297 * @param {Array.<Function>} postLinkFns
6298 * @param {Object} previousCompileContext Context used for previous compilation of the current
6300 * @returns {Function} linkFn
6302 function applyDirectivesToNode(directives, compileNode, templateAttrs, transcludeFn,
6303 jqCollection, originalReplaceDirective, preLinkFns, postLinkFns,
6304 previousCompileContext) {
6305 previousCompileContext = previousCompileContext || {};
6307 var terminalPriority = -Number.MAX_VALUE,
6309 controllerDirectives = previousCompileContext.controllerDirectives,
6310 newIsolateScopeDirective = previousCompileContext.newIsolateScopeDirective,
6311 templateDirective = previousCompileContext.templateDirective,
6312 nonTlbTranscludeDirective = previousCompileContext.nonTlbTranscludeDirective,
6313 hasTranscludeDirective = false,
6314 hasTemplate = false,
6315 hasElementTranscludeDirective = previousCompileContext.hasElementTranscludeDirective,
6316 $compileNode = templateAttrs.$$element = jqLite(compileNode),
6320 replaceDirective = originalReplaceDirective,
6321 childTranscludeFn = transcludeFn,
6325 // executes all directives on the current element
6326 for(var i = 0, ii = directives.length; i < ii; i++) {
6327 directive = directives[i];
6328 var attrStart = directive.$$start;
6329 var attrEnd = directive.$$end;
6331 // collect multiblock sections
6333 $compileNode = groupScan(compileNode, attrStart, attrEnd);
6335 $template = undefined;
6337 if (terminalPriority > directive.priority) {
6338 break; // prevent further processing of directives
6341 if (directiveValue = directive.scope) {
6342 newScopeDirective = newScopeDirective || directive;
6344 // skip the check for directives with async templates, we'll check the derived sync
6345 // directive when the template arrives
6346 if (!directive.templateUrl) {
6347 assertNoDuplicate('new/isolated scope', newIsolateScopeDirective, directive,
6349 if (isObject(directiveValue)) {
6350 newIsolateScopeDirective = directive;
6355 directiveName = directive.name;
6357 if (!directive.templateUrl && directive.controller) {
6358 directiveValue = directive.controller;
6359 controllerDirectives = controllerDirectives || {};
6360 assertNoDuplicate("'" + directiveName + "' controller",
6361 controllerDirectives[directiveName], directive, $compileNode);
6362 controllerDirectives[directiveName] = directive;
6365 if (directiveValue = directive.transclude) {
6366 hasTranscludeDirective = true;
6368 // Special case ngIf and ngRepeat so that we don't complain about duplicate transclusion.
6369 // This option should only be used by directives that know how to safely handle element transclusion,
6370 // where the transcluded nodes are added or replaced after linking.
6371 if (!directive.$$tlb) {
6372 assertNoDuplicate('transclusion', nonTlbTranscludeDirective, directive, $compileNode);
6373 nonTlbTranscludeDirective = directive;
6376 if (directiveValue == 'element') {
6377 hasElementTranscludeDirective = true;
6378 terminalPriority = directive.priority;
6379 $template = $compileNode;
6380 $compileNode = templateAttrs.$$element =
6381 jqLite(document.createComment(' ' + directiveName + ': ' +
6382 templateAttrs[directiveName] + ' '));
6383 compileNode = $compileNode[0];
6384 replaceWith(jqCollection, sliceArgs($template), compileNode);
6386 childTranscludeFn = compile($template, transcludeFn, terminalPriority,
6387 replaceDirective && replaceDirective.name, {
6389 // - controllerDirectives - otherwise we'll create duplicates controllers
6390 // - newIsolateScopeDirective or templateDirective - combining templates with
6391 // element transclusion doesn't make sense.
6393 // We need only nonTlbTranscludeDirective so that we prevent putting transclusion
6394 // on the same element more than once.
6395 nonTlbTranscludeDirective: nonTlbTranscludeDirective
6398 $template = jqLite(jqLiteClone(compileNode)).contents();
6399 $compileNode.empty(); // clear contents
6400 childTranscludeFn = compile($template, transcludeFn);
6404 if (directive.template) {
6406 assertNoDuplicate('template', templateDirective, directive, $compileNode);
6407 templateDirective = directive;
6409 directiveValue = (isFunction(directive.template))
6410 ? directive.template($compileNode, templateAttrs)
6411 : directive.template;
6413 directiveValue = denormalizeTemplate(directiveValue);
6415 if (directive.replace) {
6416 replaceDirective = directive;
6417 if (jqLiteIsTextNode(directiveValue)) {
6420 $template = jqLite(trim(directiveValue));
6422 compileNode = $template[0];
6424 if ($template.length != 1 || compileNode.nodeType !== 1) {
6425 throw $compileMinErr('tplrt',
6426 "Template for directive '{0}' must have exactly one root element. {1}",
6430 replaceWith(jqCollection, $compileNode, compileNode);
6432 var newTemplateAttrs = {$attr: {}};
6434 // combine directives from the original node and from the template:
6435 // - take the array of directives for this element
6436 // - split it into two parts, those that already applied (processed) and those that weren't (unprocessed)
6437 // - collect directives from the template and sort them by priority
6438 // - combine directives as: processed + template + unprocessed
6439 var templateDirectives = collectDirectives(compileNode, [], newTemplateAttrs);
6440 var unprocessedDirectives = directives.splice(i + 1, directives.length - (i + 1));
6442 if (newIsolateScopeDirective) {
6443 markDirectivesAsIsolate(templateDirectives);
6445 directives = directives.concat(templateDirectives).concat(unprocessedDirectives);
6446 mergeTemplateAttributes(templateAttrs, newTemplateAttrs);
6448 ii = directives.length;
6450 $compileNode.html(directiveValue);
6454 if (directive.templateUrl) {
6456 assertNoDuplicate('template', templateDirective, directive, $compileNode);
6457 templateDirective = directive;
6459 if (directive.replace) {
6460 replaceDirective = directive;
6463 nodeLinkFn = compileTemplateUrl(directives.splice(i, directives.length - i), $compileNode,
6464 templateAttrs, jqCollection, hasTranscludeDirective && childTranscludeFn, preLinkFns, postLinkFns, {
6465 controllerDirectives: controllerDirectives,
6466 newIsolateScopeDirective: newIsolateScopeDirective,
6467 templateDirective: templateDirective,
6468 nonTlbTranscludeDirective: nonTlbTranscludeDirective
6470 ii = directives.length;
6471 } else if (directive.compile) {
6473 linkFn = directive.compile($compileNode, templateAttrs, childTranscludeFn);
6474 if (isFunction(linkFn)) {
6475 addLinkFns(null, linkFn, attrStart, attrEnd);
6476 } else if (linkFn) {
6477 addLinkFns(linkFn.pre, linkFn.post, attrStart, attrEnd);
6480 $exceptionHandler(e, startingTag($compileNode));
6484 if (directive.terminal) {
6485 nodeLinkFn.terminal = true;
6486 terminalPriority = Math.max(terminalPriority, directive.priority);
6491 nodeLinkFn.scope = newScopeDirective && newScopeDirective.scope === true;
6492 nodeLinkFn.transcludeOnThisElement = hasTranscludeDirective;
6493 nodeLinkFn.templateOnThisElement = hasTemplate;
6494 nodeLinkFn.transclude = childTranscludeFn;
6496 previousCompileContext.hasElementTranscludeDirective = hasElementTranscludeDirective;
6498 // might be normal or delayed nodeLinkFn depending on if templateUrl is present
6501 ////////////////////
6503 function addLinkFns(pre, post, attrStart, attrEnd) {
6505 if (attrStart) pre = groupElementsLinkFnWrapper(pre, attrStart, attrEnd);
6506 pre.require = directive.require;
6507 pre.directiveName = directiveName;
6508 if (newIsolateScopeDirective === directive || directive.$$isolateScope) {
6509 pre = cloneAndAnnotateFn(pre, {isolateScope: true});
6511 preLinkFns.push(pre);
6514 if (attrStart) post = groupElementsLinkFnWrapper(post, attrStart, attrEnd);
6515 post.require = directive.require;
6516 post.directiveName = directiveName;
6517 if (newIsolateScopeDirective === directive || directive.$$isolateScope) {
6518 post = cloneAndAnnotateFn(post, {isolateScope: true});
6520 postLinkFns.push(post);
6525 function getControllers(directiveName, require, $element, elementControllers) {
6526 var value, retrievalMethod = 'data', optional = false;
6527 if (isString(require)) {
6528 while((value = require.charAt(0)) == '^' || value == '?') {
6529 require = require.substr(1);
6531 retrievalMethod = 'inheritedData';
6533 optional = optional || value == '?';
6537 if (elementControllers && retrievalMethod === 'data') {
6538 value = elementControllers[require];
6540 value = value || $element[retrievalMethod]('$' + require + 'Controller');
6542 if (!value && !optional) {
6543 throw $compileMinErr('ctreq',
6544 "Controller '{0}', required by directive '{1}', can't be found!",
6545 require, directiveName);
6548 } else if (isArray(require)) {
6550 forEach(require, function(require) {
6551 value.push(getControllers(directiveName, require, $element, elementControllers));
6558 function nodeLinkFn(childLinkFn, scope, linkNode, $rootElement, boundTranscludeFn) {
6559 var attrs, $element, i, ii, linkFn, controller, isolateScope, elementControllers = {}, transcludeFn;
6561 attrs = (compileNode === linkNode)
6563 : shallowCopy(templateAttrs, new Attributes(jqLite(linkNode), templateAttrs.$attr));
6564 $element = attrs.$$element;
6566 if (newIsolateScopeDirective) {
6567 var LOCAL_REGEXP = /^\s*([@=&])(\??)\s*(\w*)\s*$/;
6569 isolateScope = scope.$new(true);
6571 if (templateDirective && (templateDirective === newIsolateScopeDirective ||
6572 templateDirective === newIsolateScopeDirective.$$originalDirective)) {
6573 $element.data('$isolateScope', isolateScope);
6575 $element.data('$isolateScopeNoTemplate', isolateScope);
6580 safeAddClass($element, 'ng-isolate-scope');
6582 forEach(newIsolateScopeDirective.scope, function(definition, scopeName) {
6583 var match = definition.match(LOCAL_REGEXP) || [],
6584 attrName = match[3] || scopeName,
6585 optional = (match[2] == '?'),
6586 mode = match[1], // @, =, or &
6588 parentGet, parentSet, compare;
6590 isolateScope.$$isolateBindings[scopeName] = mode + attrName;
6595 attrs.$observe(attrName, function(value) {
6596 isolateScope[scopeName] = value;
6598 attrs.$$observers[attrName].$$scope = scope;
6599 if( attrs[attrName] ) {
6600 // If the attribute has been provided then we trigger an interpolation to ensure
6601 // the value is there for use in the link fn
6602 isolateScope[scopeName] = $interpolate(attrs[attrName])(scope);
6607 if (optional && !attrs[attrName]) {
6610 parentGet = $parse(attrs[attrName]);
6611 if (parentGet.literal) {
6614 compare = function(a,b) { return a === b || (a !== a && b !== b); };
6616 parentSet = parentGet.assign || function() {
6617 // reset the change, or we will throw this exception on every $digest
6618 lastValue = isolateScope[scopeName] = parentGet(scope);
6619 throw $compileMinErr('nonassign',
6620 "Expression '{0}' used with directive '{1}' is non-assignable!",
6621 attrs[attrName], newIsolateScopeDirective.name);
6623 lastValue = isolateScope[scopeName] = parentGet(scope);
6624 isolateScope.$watch(function parentValueWatch() {
6625 var parentValue = parentGet(scope);
6626 if (!compare(parentValue, isolateScope[scopeName])) {
6627 // we are out of sync and need to copy
6628 if (!compare(parentValue, lastValue)) {
6629 // parent changed and it has precedence
6630 isolateScope[scopeName] = parentValue;
6632 // if the parent can be assigned then do so
6633 parentSet(scope, parentValue = isolateScope[scopeName]);
6636 return lastValue = parentValue;
6637 }, null, parentGet.literal);
6641 parentGet = $parse(attrs[attrName]);
6642 isolateScope[scopeName] = function(locals) {
6643 return parentGet(scope, locals);
6648 throw $compileMinErr('iscp',
6649 "Invalid isolate scope definition for directive '{0}'." +
6650 " Definition: {... {1}: '{2}' ...}",
6651 newIsolateScopeDirective.name, scopeName, definition);
6655 transcludeFn = boundTranscludeFn && controllersBoundTransclude;
6656 if (controllerDirectives) {
6657 forEach(controllerDirectives, function(directive) {
6659 $scope: directive === newIsolateScopeDirective || directive.$$isolateScope ? isolateScope : scope,
6662 $transclude: transcludeFn
6663 }, controllerInstance;
6665 controller = directive.controller;
6666 if (controller == '@') {
6667 controller = attrs[directive.name];
6670 controllerInstance = $controller(controller, locals);
6671 // For directives with element transclusion the element is a comment,
6672 // but jQuery .data doesn't support attaching data to comment nodes as it's hard to
6673 // clean up (http://bugs.jquery.com/ticket/8335).
6674 // Instead, we save the controllers for the element in a local hash and attach to .data
6675 // later, once we have the actual element.
6676 elementControllers[directive.name] = controllerInstance;
6677 if (!hasElementTranscludeDirective) {
6678 $element.data('$' + directive.name + 'Controller', controllerInstance);
6681 if (directive.controllerAs) {
6682 locals.$scope[directive.controllerAs] = controllerInstance;
6688 for(i = 0, ii = preLinkFns.length; i < ii; i++) {
6690 linkFn = preLinkFns[i];
6691 linkFn(linkFn.isolateScope ? isolateScope : scope, $element, attrs,
6692 linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers), transcludeFn);
6694 $exceptionHandler(e, startingTag($element));
6699 // We only pass the isolate scope, if the isolate directive has a template,
6700 // otherwise the child elements do not belong to the isolate directive.
6701 var scopeToChild = scope;
6702 if (newIsolateScopeDirective && (newIsolateScopeDirective.template || newIsolateScopeDirective.templateUrl === null)) {
6703 scopeToChild = isolateScope;
6705 childLinkFn && childLinkFn(scopeToChild, linkNode.childNodes, undefined, boundTranscludeFn);
6708 for(i = postLinkFns.length - 1; i >= 0; i--) {
6710 linkFn = postLinkFns[i];
6711 linkFn(linkFn.isolateScope ? isolateScope : scope, $element, attrs,
6712 linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers), transcludeFn);
6714 $exceptionHandler(e, startingTag($element));
6718 // This is the function that is injected as `$transclude`.
6719 function controllersBoundTransclude(scope, cloneAttachFn) {
6720 var transcludeControllers;
6723 if (arguments.length < 2) {
6724 cloneAttachFn = scope;
6728 if (hasElementTranscludeDirective) {
6729 transcludeControllers = elementControllers;
6732 return boundTranscludeFn(scope, cloneAttachFn, transcludeControllers);
6737 function markDirectivesAsIsolate(directives) {
6738 // mark all directives as needing isolate scope.
6739 for (var j = 0, jj = directives.length; j < jj; j++) {
6740 directives[j] = inherit(directives[j], {$$isolateScope: true});
6745 * looks up the directive and decorates it with exception handling and proper parameters. We
6746 * call this the boundDirective.
6748 * @param {string} name name of the directive to look up.
6749 * @param {string} location The directive must be found in specific format.
6750 * String containing any of theses characters:
6752 * * `E`: element name
6756 * @returns {boolean} true if directive was added.
6758 function addDirective(tDirectives, name, location, maxPriority, ignoreDirective, startAttrName,
6760 if (name === ignoreDirective) return null;
6762 if (hasDirectives.hasOwnProperty(name)) {
6763 for(var directive, directives = $injector.get(name + Suffix),
6764 i = 0, ii = directives.length; i<ii; i++) {
6766 directive = directives[i];
6767 if ( (maxPriority === undefined || maxPriority > directive.priority) &&
6768 directive.restrict.indexOf(location) != -1) {
6769 if (startAttrName) {
6770 directive = inherit(directive, {$$start: startAttrName, $$end: endAttrName});
6772 tDirectives.push(directive);
6775 } catch(e) { $exceptionHandler(e); }
6783 * When the element is replaced with HTML template then the new attributes
6784 * on the template need to be merged with the existing attributes in the DOM.
6785 * The desired effect is to have both of the attributes present.
6787 * @param {object} dst destination attributes (original DOM)
6788 * @param {object} src source attributes (from the directive template)
6790 function mergeTemplateAttributes(dst, src) {
6791 var srcAttr = src.$attr,
6792 dstAttr = dst.$attr,
6793 $element = dst.$$element;
6795 // reapply the old attributes to the new element
6796 forEach(dst, function(value, key) {
6797 if (key.charAt(0) != '$') {
6798 if (src[key] && src[key] !== value) {
6799 value += (key === 'style' ? ';' : ' ') + src[key];
6801 dst.$set(key, value, true, srcAttr[key]);
6805 // copy the new attributes on the old attrs object
6806 forEach(src, function(value, key) {
6807 if (key == 'class') {
6808 safeAddClass($element, value);
6809 dst['class'] = (dst['class'] ? dst['class'] + ' ' : '') + value;
6810 } else if (key == 'style') {
6811 $element.attr('style', $element.attr('style') + ';' + value);
6812 dst['style'] = (dst['style'] ? dst['style'] + ';' : '') + value;
6813 // `dst` will never contain hasOwnProperty as DOM parser won't let it.
6814 // You will get an "InvalidCharacterError: DOM Exception 5" error if you
6815 // have an attribute like "has-own-property" or "data-has-own-property", etc.
6816 } else if (key.charAt(0) != '$' && !dst.hasOwnProperty(key)) {
6818 dstAttr[key] = srcAttr[key];
6824 function compileTemplateUrl(directives, $compileNode, tAttrs,
6825 $rootElement, childTranscludeFn, preLinkFns, postLinkFns, previousCompileContext) {
6827 afterTemplateNodeLinkFn,
6828 afterTemplateChildLinkFn,
6829 beforeTemplateCompileNode = $compileNode[0],
6830 origAsyncDirective = directives.shift(),
6831 // The fact that we have to copy and patch the directive seems wrong!
6832 derivedSyncDirective = extend({}, origAsyncDirective, {
6833 templateUrl: null, transclude: null, replace: null, $$originalDirective: origAsyncDirective
6835 templateUrl = (isFunction(origAsyncDirective.templateUrl))
6836 ? origAsyncDirective.templateUrl($compileNode, tAttrs)
6837 : origAsyncDirective.templateUrl;
6839 $compileNode.empty();
6841 $http.get($sce.getTrustedResourceUrl(templateUrl), {cache: $templateCache}).
6842 success(function(content) {
6843 var compileNode, tempTemplateAttrs, $template, childBoundTranscludeFn;
6845 content = denormalizeTemplate(content);
6847 if (origAsyncDirective.replace) {
6848 if (jqLiteIsTextNode(content)) {
6851 $template = jqLite(trim(content));
6853 compileNode = $template[0];
6855 if ($template.length != 1 || compileNode.nodeType !== 1) {
6856 throw $compileMinErr('tplrt',
6857 "Template for directive '{0}' must have exactly one root element. {1}",
6858 origAsyncDirective.name, templateUrl);
6861 tempTemplateAttrs = {$attr: {}};
6862 replaceWith($rootElement, $compileNode, compileNode);
6863 var templateDirectives = collectDirectives(compileNode, [], tempTemplateAttrs);
6865 if (isObject(origAsyncDirective.scope)) {
6866 markDirectivesAsIsolate(templateDirectives);
6868 directives = templateDirectives.concat(directives);
6869 mergeTemplateAttributes(tAttrs, tempTemplateAttrs);
6871 compileNode = beforeTemplateCompileNode;
6872 $compileNode.html(content);
6875 directives.unshift(derivedSyncDirective);
6877 afterTemplateNodeLinkFn = applyDirectivesToNode(directives, compileNode, tAttrs,
6878 childTranscludeFn, $compileNode, origAsyncDirective, preLinkFns, postLinkFns,
6879 previousCompileContext);
6880 forEach($rootElement, function(node, i) {
6881 if (node == compileNode) {
6882 $rootElement[i] = $compileNode[0];
6885 afterTemplateChildLinkFn = compileNodes($compileNode[0].childNodes, childTranscludeFn);
6887 while(linkQueue.length) {
6888 var scope = linkQueue.shift(),
6889 beforeTemplateLinkNode = linkQueue.shift(),
6890 linkRootElement = linkQueue.shift(),
6891 boundTranscludeFn = linkQueue.shift(),
6892 linkNode = $compileNode[0];
6894 if (beforeTemplateLinkNode !== beforeTemplateCompileNode) {
6895 var oldClasses = beforeTemplateLinkNode.className;
6897 if (!(previousCompileContext.hasElementTranscludeDirective &&
6898 origAsyncDirective.replace)) {
6899 // it was cloned therefore we have to clone as well.
6900 linkNode = jqLiteClone(compileNode);
6903 replaceWith(linkRootElement, jqLite(beforeTemplateLinkNode), linkNode);
6905 // Copy in CSS classes from original node
6906 safeAddClass(jqLite(linkNode), oldClasses);
6908 if (afterTemplateNodeLinkFn.transcludeOnThisElement) {
6909 childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn);
6911 childBoundTranscludeFn = boundTranscludeFn;
6913 afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, linkNode, $rootElement,
6914 childBoundTranscludeFn);
6918 error(function(response, code, headers, config) {
6919 throw $compileMinErr('tpload', 'Failed to load template: {0}', config.url);
6922 return function delayedNodeLinkFn(ignoreChildLinkFn, scope, node, rootElement, boundTranscludeFn) {
6923 var childBoundTranscludeFn = boundTranscludeFn;
6925 linkQueue.push(scope);
6926 linkQueue.push(node);
6927 linkQueue.push(rootElement);
6928 linkQueue.push(childBoundTranscludeFn);
6930 if (afterTemplateNodeLinkFn.transcludeOnThisElement) {
6931 childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn);
6933 afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, node, rootElement, childBoundTranscludeFn);
6940 * Sorting function for bound directives.
6942 function byPriority(a, b) {
6943 var diff = b.priority - a.priority;
6944 if (diff !== 0) return diff;
6945 if (a.name !== b.name) return (a.name < b.name) ? -1 : 1;
6946 return a.index - b.index;
6950 function assertNoDuplicate(what, previousDirective, directive, element) {
6951 if (previousDirective) {
6952 throw $compileMinErr('multidir', 'Multiple directives [{0}, {1}] asking for {2} on: {3}',
6953 previousDirective.name, directive.name, what, startingTag(element));
6958 function addTextInterpolateDirective(directives, text) {
6959 var interpolateFn = $interpolate(text, true);
6960 if (interpolateFn) {
6963 compile: function textInterpolateCompileFn(templateNode) {
6964 // when transcluding a template that has bindings in the root
6965 // then we don't have a parent and should do this in the linkFn
6966 var parent = templateNode.parent(), hasCompileParent = parent.length;
6967 if (hasCompileParent) safeAddClass(templateNode.parent(), 'ng-binding');
6969 return function textInterpolateLinkFn(scope, node) {
6970 var parent = node.parent(),
6971 bindings = parent.data('$binding') || [];
6972 bindings.push(interpolateFn);
6973 parent.data('$binding', bindings);
6974 if (!hasCompileParent) safeAddClass(parent, 'ng-binding');
6975 scope.$watch(interpolateFn, function interpolateFnWatchAction(value) {
6976 node[0].nodeValue = value;
6985 function getTrustedContext(node, attrNormalizedName) {
6986 if (attrNormalizedName == "srcdoc") {
6989 var tag = nodeName_(node);
6990 // maction[xlink:href] can source SVG. It's not limited to <maction>.
6991 if (attrNormalizedName == "xlinkHref" ||
6992 (tag == "FORM" && attrNormalizedName == "action") ||
6993 (tag != "IMG" && (attrNormalizedName == "src" ||
6994 attrNormalizedName == "ngSrc"))) {
6995 return $sce.RESOURCE_URL;
7000 function addAttrInterpolateDirective(node, directives, value, name) {
7001 var interpolateFn = $interpolate(value, true);
7003 // no interpolation found -> ignore
7004 if (!interpolateFn) return;
7007 if (name === "multiple" && nodeName_(node) === "SELECT") {
7008 throw $compileMinErr("selmulti",
7009 "Binding to the 'multiple' attribute is not supported. Element: {0}",
7015 compile: function() {
7017 pre: function attrInterpolatePreLinkFn(scope, element, attr) {
7018 var $$observers = (attr.$$observers || (attr.$$observers = {}));
7020 if (EVENT_HANDLER_ATTR_REGEXP.test(name)) {
7021 throw $compileMinErr('nodomevents',
7022 "Interpolations for HTML DOM event attributes are disallowed. Please use the " +
7023 "ng- versions (such as ng-click instead of onclick) instead.");
7026 // we need to interpolate again, in case the attribute value has been updated
7027 // (e.g. by another directive's compile function)
7028 interpolateFn = $interpolate(attr[name], true, getTrustedContext(node, name));
7030 // if attribute was updated so that there is no interpolation going on we don't want to
7031 // register any observers
7032 if (!interpolateFn) return;
7034 // TODO(i): this should likely be attr.$set(name, iterpolateFn(scope) so that we reset the
7035 // actual attr value
7036 attr[name] = interpolateFn(scope);
7037 ($$observers[name] || ($$observers[name] = [])).$$inter = true;
7038 (attr.$$observers && attr.$$observers[name].$$scope || scope).
7039 $watch(interpolateFn, function interpolateFnWatchAction(newValue, oldValue) {
7040 //special case for class attribute addition + removal
7041 //so that class changes can tap into the animation
7042 //hooks provided by the $animate service. Be sure to
7043 //skip animations when the first digest occurs (when
7044 //both the new and the old values are the same) since
7045 //the CSS classes are the non-interpolated values
7046 if(name === 'class' && newValue != oldValue) {
7047 attr.$updateClass(newValue, oldValue);
7049 attr.$set(name, newValue);
7060 * This is a special jqLite.replaceWith, which can replace items which
7061 * have no parents, provided that the containing jqLite collection is provided.
7063 * @param {JqLite=} $rootElement The root of the compile tree. Used so that we can replace nodes
7064 * in the root of the tree.
7065 * @param {JqLite} elementsToRemove The jqLite element which we are going to replace. We keep
7066 * the shell, but replace its DOM node reference.
7067 * @param {Node} newNode The new DOM node.
7069 function replaceWith($rootElement, elementsToRemove, newNode) {
7070 var firstElementToRemove = elementsToRemove[0],
7071 removeCount = elementsToRemove.length,
7072 parent = firstElementToRemove.parentNode,
7076 for(i = 0, ii = $rootElement.length; i < ii; i++) {
7077 if ($rootElement[i] == firstElementToRemove) {
7078 $rootElement[i++] = newNode;
7079 for (var j = i, j2 = j + removeCount - 1,
7080 jj = $rootElement.length;
7081 j < jj; j++, j2++) {
7083 $rootElement[j] = $rootElement[j2];
7085 delete $rootElement[j];
7088 $rootElement.length -= removeCount - 1;
7095 parent.replaceChild(newNode, firstElementToRemove);
7097 var fragment = document.createDocumentFragment();
7098 fragment.appendChild(firstElementToRemove);
7099 newNode[jqLite.expando] = firstElementToRemove[jqLite.expando];
7100 for (var k = 1, kk = elementsToRemove.length; k < kk; k++) {
7101 var element = elementsToRemove[k];
7102 jqLite(element).remove(); // must do this way to clean up expando
7103 fragment.appendChild(element);
7104 delete elementsToRemove[k];
7107 elementsToRemove[0] = newNode;
7108 elementsToRemove.length = 1;
7112 function cloneAndAnnotateFn(fn, annotation) {
7113 return extend(function() { return fn.apply(null, arguments); }, fn, annotation);
7118 var PREFIX_REGEXP = /^(x[\:\-_]|data[\:\-_])/i;
7120 * Converts all accepted directives format into proper directive name.
7121 * All of these will become 'myDirective':
7127 * Also there is special case for Moz prefix starting with upper case letter.
7128 * @param name Name to normalize
7130 function directiveNormalize(name) {
7131 return camelCase(name.replace(PREFIX_REGEXP, ''));
7136 * @name $compile.directive.Attributes
7139 * A shared object between directive compile / linking functions which contains normalized DOM
7140 * element attributes. The values reflect current binding state `{{ }}`. The normalization is
7141 * needed since all of these are treated as equivalent in Angular:
7144 * <span ng:bind="a" ng-bind="a" data-ng-bind="a" x-ng-bind="a">
7150 * @name $compile.directive.Attributes#$attr
7153 * A map of DOM element attribute names to the normalized name. This is
7154 * needed to do reverse lookup from normalized name back to actual name.
7160 * @name $compile.directive.Attributes#$set
7164 * Set DOM element attribute value.
7167 * @param {string} name Normalized element attribute name of the property to modify. The name is
7168 * reverse-translated using the {@link ng.$compile.directive.Attributes#$attr $attr}
7169 * property to the original name.
7170 * @param {string} value Value to set the attribute to. The value can be an interpolated string.
7176 * Closure compiler type information
7179 function nodesetLinkingFn(
7180 /* angular.Scope */ scope,
7181 /* NodeList */ nodeList,
7182 /* Element */ rootElement,
7183 /* function(Function) */ boundTranscludeFn
7186 function directiveLinkingFn(
7187 /* nodesetLinkingFn */ nodesetLinkingFn,
7188 /* angular.Scope */ scope,
7190 /* Element */ rootElement,
7191 /* function(Function) */ boundTranscludeFn
7194 function tokenDifference(str1, str2) {
7196 tokens1 = str1.split(/\s+/),
7197 tokens2 = str2.split(/\s+/);
7200 for(var i = 0; i < tokens1.length; i++) {
7201 var token = tokens1[i];
7202 for(var j = 0; j < tokens2.length; j++) {
7203 if(token == tokens2[j]) continue outer;
7205 values += (values.length > 0 ? ' ' : '') + token;
7212 * @name $controllerProvider
7214 * The {@link ng.$controller $controller service} is used by Angular to create new
7217 * This provider allows controller registration via the
7218 * {@link ng.$controllerProvider#register register} method.
7220 function $ControllerProvider() {
7221 var controllers = {},
7222 CNTRL_REG = /^(\S+)(\s+as\s+(\w+))?$/;
7227 * @name $controllerProvider#register
7228 * @param {string|Object} name Controller name, or an object map of controllers where the keys are
7229 * the names and the values are the constructors.
7230 * @param {Function|Array} constructor Controller constructor fn (optionally decorated with DI
7231 * annotations in the array notation).
7233 this.register = function(name, constructor) {
7234 assertNotHasOwnProperty(name, 'controller');
7235 if (isObject(name)) {
7236 extend(controllers, name);
7238 controllers[name] = constructor;
7243 this.$get = ['$injector', '$window', function($injector, $window) {
7248 * @requires $injector
7250 * @param {Function|string} constructor If called with a function then it's considered to be the
7251 * controller constructor function. Otherwise it's considered to be a string which is used
7252 * to retrieve the controller constructor using the following steps:
7254 * * check if a controller with given name is registered via `$controllerProvider`
7255 * * check if evaluating the string on the current scope returns a constructor
7256 * * check `window[constructor]` on the global `window` object
7258 * @param {Object} locals Injection locals for Controller.
7259 * @return {Object} Instance of given controller.
7262 * `$controller` service is responsible for instantiating controllers.
7264 * It's just a simple call to {@link auto.$injector $injector}, but extracted into
7265 * a service, so that one can override this service with [BC version](https://gist.github.com/1649788).
7267 return function(expression, locals) {
7268 var instance, match, constructor, identifier;
7270 if(isString(expression)) {
7271 match = expression.match(CNTRL_REG),
7272 constructor = match[1],
7273 identifier = match[3];
7274 expression = controllers.hasOwnProperty(constructor)
7275 ? controllers[constructor]
7276 : getter(locals.$scope, constructor, true) || getter($window, constructor, true);
7278 assertArgFn(expression, constructor, true);
7281 instance = $injector.instantiate(expression, locals);
7284 if (!(locals && typeof locals.$scope === 'object')) {
7285 throw minErr('$controller')('noscp',
7286 "Cannot export controller '{0}' as '{1}'! No $scope object provided via `locals`.",
7287 constructor || expression.name, identifier);
7290 locals.$scope[identifier] = instance;
7304 * A {@link angular.element jQuery or jqLite} wrapper for the browser's `window.document` object.
7307 <example module="documentExample">
7308 <file name="index.html">
7309 <div ng-controller="ExampleController">
7310 <p>$document title: <b ng-bind="title"></b></p>
7311 <p>window.document title: <b ng-bind="windowTitle"></b></p>
7314 <file name="script.js">
7315 angular.module('documentExample', [])
7316 .controller('ExampleController', ['$scope', '$document', function($scope, $document) {
7317 $scope.title = $document[0].title;
7318 $scope.windowTitle = angular.element(window.document)[0].title;
7323 function $DocumentProvider(){
7324 this.$get = ['$window', function(window){
7325 return jqLite(window.document);
7331 * @name $exceptionHandler
7335 * Any uncaught exception in angular expressions is delegated to this service.
7336 * The default implementation simply delegates to `$log.error` which logs it into
7337 * the browser console.
7339 * In unit tests, if `angular-mocks.js` is loaded, this service is overridden by
7340 * {@link ngMock.$exceptionHandler mock $exceptionHandler} which aids in testing.
7345 * angular.module('exceptionOverride', []).factory('$exceptionHandler', function () {
7346 * return function (exception, cause) {
7347 * exception.message += ' (caused by "' + cause + '")';
7353 * This example will override the normal action of `$exceptionHandler`, to make angular
7354 * exceptions fail hard when they happen, instead of just logging to the console.
7356 * @param {Error} exception Exception associated with the error.
7357 * @param {string=} cause optional information about the context in which
7358 * the error was thrown.
7361 function $ExceptionHandlerProvider() {
7362 this.$get = ['$log', function($log) {
7363 return function(exception, cause) {
7364 $log.error.apply($log, arguments);
7370 * Parse headers into key value object
7372 * @param {string} headers Raw headers as a string
7373 * @returns {Object} Parsed headers as key value object
7375 function parseHeaders(headers) {
7376 var parsed = {}, key, val, i;
7378 if (!headers) return parsed;
7380 forEach(headers.split('\n'), function(line) {
7381 i = line.indexOf(':');
7382 key = lowercase(trim(line.substr(0, i)));
7383 val = trim(line.substr(i + 1));
7386 parsed[key] = parsed[key] ? parsed[key] + ', ' + val : val;
7395 * Returns a function that provides access to parsed headers.
7397 * Headers are lazy parsed when first requested.
7400 * @param {(string|Object)} headers Headers to provide access to.
7401 * @returns {function(string=)} Returns a getter function which if called with:
7403 * - if called with single an argument returns a single header value or null
7404 * - if called with no arguments returns an object containing all headers.
7406 function headersGetter(headers) {
7407 var headersObj = isObject(headers) ? headers : undefined;
7409 return function(name) {
7410 if (!headersObj) headersObj = parseHeaders(headers);
7413 return headersObj[lowercase(name)] || null;
7422 * Chain all given functions
7424 * This function is used for both request and response transforming
7426 * @param {*} data Data to transform.
7427 * @param {function(string=)} headers Http headers getter fn.
7428 * @param {(Function|Array.<Function>)} fns Function or an array of functions.
7429 * @returns {*} Transformed data.
7431 function transformData(data, headers, fns) {
7432 if (isFunction(fns))
7433 return fns(data, headers);
7435 forEach(fns, function(fn) {
7436 data = fn(data, headers);
7443 function isSuccess(status) {
7444 return 200 <= status && status < 300;
7450 * @name $httpProvider
7452 * Use `$httpProvider` to change the default behavior of the {@link ng.$http $http} service.
7454 function $HttpProvider() {
7455 var JSON_START = /^\s*(\[|\{[^\{])/,
7456 JSON_END = /[\}\]]\s*$/,
7457 PROTECTION_PREFIX = /^\)\]\}',?\n/,
7458 CONTENT_TYPE_APPLICATION_JSON = {'Content-Type': 'application/json;charset=utf-8'};
7462 * @name $httpProvider#defaults
7465 * Object containing default values for all {@link ng.$http $http} requests.
7467 * - **`defaults.xsrfCookieName`** - {string} - Name of cookie containing the XSRF token.
7468 * Defaults value is `'XSRF-TOKEN'`.
7470 * - **`defaults.xsrfHeaderName`** - {string} - Name of HTTP header to populate with the
7471 * XSRF token. Defaults value is `'X-XSRF-TOKEN'`.
7473 * - **`defaults.headers`** - {Object} - Default headers for all $http requests.
7474 * Refer to {@link ng.$http#setting-http-headers $http} for documentation on
7475 * setting default headers.
7476 * - **`defaults.headers.common`**
7477 * - **`defaults.headers.post`**
7478 * - **`defaults.headers.put`**
7479 * - **`defaults.headers.patch`**
7481 var defaults = this.defaults = {
7482 // transform incoming response data
7483 transformResponse: [function(data) {
7484 if (isString(data)) {
7485 // strip json vulnerability protection prefix
7486 data = data.replace(PROTECTION_PREFIX, '');
7487 if (JSON_START.test(data) && JSON_END.test(data))
7488 data = fromJson(data);
7493 // transform outgoing request data
7494 transformRequest: [function(d) {
7495 return isObject(d) && !isFile(d) && !isBlob(d) ? toJson(d) : d;
7501 'Accept': 'application/json, text/plain, */*'
7503 post: shallowCopy(CONTENT_TYPE_APPLICATION_JSON),
7504 put: shallowCopy(CONTENT_TYPE_APPLICATION_JSON),
7505 patch: shallowCopy(CONTENT_TYPE_APPLICATION_JSON)
7508 xsrfCookieName: 'XSRF-TOKEN',
7509 xsrfHeaderName: 'X-XSRF-TOKEN'
7513 * Are ordered by request, i.e. they are applied in the same order as the
7514 * array, on request, but reverse order, on response.
7516 var interceptorFactories = this.interceptors = [];
7519 * For historical reasons, response interceptors are ordered by the order in which
7520 * they are applied to the response. (This is the opposite of interceptorFactories)
7522 var responseInterceptorFactories = this.responseInterceptors = [];
7524 this.$get = ['$httpBackend', '$browser', '$cacheFactory', '$rootScope', '$q', '$injector',
7525 function($httpBackend, $browser, $cacheFactory, $rootScope, $q, $injector) {
7527 var defaultCache = $cacheFactory('$http');
7530 * Interceptors stored in reverse order. Inner interceptors before outer interceptors.
7531 * The reversal is needed so that we can build up the interception chain around the
7534 var reversedInterceptors = [];
7536 forEach(interceptorFactories, function(interceptorFactory) {
7537 reversedInterceptors.unshift(isString(interceptorFactory)
7538 ? $injector.get(interceptorFactory) : $injector.invoke(interceptorFactory));
7541 forEach(responseInterceptorFactories, function(interceptorFactory, index) {
7542 var responseFn = isString(interceptorFactory)
7543 ? $injector.get(interceptorFactory)
7544 : $injector.invoke(interceptorFactory);
7547 * Response interceptors go before "around" interceptors (no real reason, just
7548 * had to pick one.) But they are already reversed, so we can't use unshift, hence
7551 reversedInterceptors.splice(index, 0, {
7552 response: function(response) {
7553 return responseFn($q.when(response));
7555 responseError: function(response) {
7556 return responseFn($q.reject(response));
7566 * @requires ng.$httpBackend
7567 * @requires $cacheFactory
7568 * @requires $rootScope
7570 * @requires $injector
7573 * The `$http` service is a core Angular service that facilitates communication with the remote
7574 * HTTP servers via the browser's [XMLHttpRequest](https://developer.mozilla.org/en/xmlhttprequest)
7575 * object or via [JSONP](http://en.wikipedia.org/wiki/JSONP).
7577 * For unit testing applications that use `$http` service, see
7578 * {@link ngMock.$httpBackend $httpBackend mock}.
7580 * For a higher level of abstraction, please check out the {@link ngResource.$resource
7581 * $resource} service.
7583 * The $http API is based on the {@link ng.$q deferred/promise APIs} exposed by
7584 * the $q service. While for simple usage patterns this doesn't matter much, for advanced usage
7585 * it is important to familiarize yourself with these APIs and the guarantees they provide.
7589 * The `$http` service is a function which takes a single argument — a configuration object —
7590 * that is used to generate an HTTP request and returns a {@link ng.$q promise}
7591 * with two $http specific methods: `success` and `error`.
7594 * $http({method: 'GET', url: '/someUrl'}).
7595 * success(function(data, status, headers, config) {
7596 * // this callback will be called asynchronously
7597 * // when the response is available
7599 * error(function(data, status, headers, config) {
7600 * // called asynchronously if an error occurs
7601 * // or server returns response with an error status.
7605 * Since the returned value of calling the $http function is a `promise`, you can also use
7606 * the `then` method to register callbacks, and these callbacks will receive a single argument –
7607 * an object representing the response. See the API signature and type info below for more
7610 * A response status code between 200 and 299 is considered a success status and
7611 * will result in the success callback being called. Note that if the response is a redirect,
7612 * XMLHttpRequest will transparently follow it, meaning that the error callback will not be
7613 * called for such responses.
7615 * # Writing Unit Tests that use $http
7616 * When unit testing (using {@link ngMock ngMock}), it is necessary to call
7617 * {@link ngMock.$httpBackend#flush $httpBackend.flush()} to flush each pending
7618 * request using trained responses.
7621 * $httpBackend.expectGET(...);
7623 * $httpBackend.flush();
7626 * # Shortcut methods
7628 * Shortcut methods are also available. All shortcut methods require passing in the URL, and
7629 * request data must be passed in for POST/PUT requests.
7632 * $http.get('/someUrl').success(successCallback);
7633 * $http.post('/someUrl', data).success(successCallback);
7636 * Complete list of shortcut methods:
7638 * - {@link ng.$http#get $http.get}
7639 * - {@link ng.$http#head $http.head}
7640 * - {@link ng.$http#post $http.post}
7641 * - {@link ng.$http#put $http.put}
7642 * - {@link ng.$http#delete $http.delete}
7643 * - {@link ng.$http#jsonp $http.jsonp}
7644 * - {@link ng.$http#patch $http.patch}
7647 * # Setting HTTP Headers
7649 * The $http service will automatically add certain HTTP headers to all requests. These defaults
7650 * can be fully configured by accessing the `$httpProvider.defaults.headers` configuration
7651 * object, which currently contains this default configuration:
7653 * - `$httpProvider.defaults.headers.common` (headers that are common for all requests):
7654 * - `Accept: application/json, text/plain, * / *`
7655 * - `$httpProvider.defaults.headers.post`: (header defaults for POST requests)
7656 * - `Content-Type: application/json`
7657 * - `$httpProvider.defaults.headers.put` (header defaults for PUT requests)
7658 * - `Content-Type: application/json`
7660 * To add or overwrite these defaults, simply add or remove a property from these configuration
7661 * objects. To add headers for an HTTP method other than POST or PUT, simply add a new object
7662 * with the lowercased HTTP method name as the key, e.g.
7663 * `$httpProvider.defaults.headers.get = { 'My-Header' : 'value' }.
7665 * The defaults can also be set at runtime via the `$http.defaults` object in the same
7666 * fashion. For example:
7669 * module.run(function($http) {
7670 * $http.defaults.headers.common.Authorization = 'Basic YmVlcDpib29w'
7674 * In addition, you can supply a `headers` property in the config object passed when
7675 * calling `$http(config)`, which overrides the defaults without changing them globally.
7678 * # Transforming Requests and Responses
7680 * Both requests and responses can be transformed using transform functions. By default, Angular
7681 * applies these transformations:
7683 * Request transformations:
7685 * - If the `data` property of the request configuration object contains an object, serialize it
7688 * Response transformations:
7690 * - If XSRF prefix is detected, strip it (see Security Considerations section below).
7691 * - If JSON response is detected, deserialize it using a JSON parser.
7693 * To globally augment or override the default transforms, modify the
7694 * `$httpProvider.defaults.transformRequest` and `$httpProvider.defaults.transformResponse`
7695 * properties. These properties are by default an array of transform functions, which allows you
7696 * to `push` or `unshift` a new transformation function into the transformation chain. You can
7697 * also decide to completely override any default transformations by assigning your
7698 * transformation functions to these properties directly without the array wrapper. These defaults
7699 * are again available on the $http factory at run-time, which may be useful if you have run-time
7700 * services you wish to be involved in your transformations.
7702 * Similarly, to locally override the request/response transforms, augment the
7703 * `transformRequest` and/or `transformResponse` properties of the configuration object passed
7709 * To enable caching, set the request configuration `cache` property to `true` (to use default
7710 * cache) or to a custom cache object (built with {@link ng.$cacheFactory `$cacheFactory`}).
7711 * When the cache is enabled, `$http` stores the response from the server in the specified
7712 * cache. The next time the same request is made, the response is served from the cache without
7713 * sending a request to the server.
7715 * Note that even if the response is served from cache, delivery of the data is asynchronous in
7716 * the same way that real requests are.
7718 * If there are multiple GET requests for the same URL that should be cached using the same
7719 * cache, but the cache is not populated yet, only one request to the server will be made and
7720 * the remaining requests will be fulfilled using the response from the first request.
7722 * You can change the default cache to a new object (built with
7723 * {@link ng.$cacheFactory `$cacheFactory`}) by updating the
7724 * {@link ng.$http#properties_defaults `$http.defaults.cache`} property. All requests who set
7725 * their `cache` property to `true` will now use this cache object.
7727 * If you set the default cache to `false` then only requests that specify their own custom
7728 * cache object will be cached.
7732 * Before you start creating interceptors, be sure to understand the
7733 * {@link ng.$q $q and deferred/promise APIs}.
7735 * For purposes of global error handling, authentication, or any kind of synchronous or
7736 * asynchronous pre-processing of request or postprocessing of responses, it is desirable to be
7737 * able to intercept requests before they are handed to the server and
7738 * responses before they are handed over to the application code that
7739 * initiated these requests. The interceptors leverage the {@link ng.$q
7740 * promise APIs} to fulfill this need for both synchronous and asynchronous pre-processing.
7742 * The interceptors are service factories that are registered with the `$httpProvider` by
7743 * adding them to the `$httpProvider.interceptors` array. The factory is called and
7744 * injected with dependencies (if specified) and returns the interceptor.
7746 * There are two kinds of interceptors (and two kinds of rejection interceptors):
7748 * * `request`: interceptors get called with a http `config` object. The function is free to
7749 * modify the `config` object or create a new one. The function needs to return the `config`
7750 * object directly, or a promise containing the `config` or a new `config` object.
7751 * * `requestError`: interceptor gets called when a previous interceptor threw an error or
7752 * resolved with a rejection.
7753 * * `response`: interceptors get called with http `response` object. The function is free to
7754 * modify the `response` object or create a new one. The function needs to return the `response`
7755 * object directly, or as a promise containing the `response` or a new `response` object.
7756 * * `responseError`: interceptor gets called when a previous interceptor threw an error or
7757 * resolved with a rejection.
7761 * // register the interceptor as a service
7762 * $provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) {
7764 * // optional method
7765 * 'request': function(config) {
7766 * // do something on success
7770 * // optional method
7771 * 'requestError': function(rejection) {
7772 * // do something on error
7773 * if (canRecover(rejection)) {
7774 * return responseOrNewPromise
7776 * return $q.reject(rejection);
7781 * // optional method
7782 * 'response': function(response) {
7783 * // do something on success
7787 * // optional method
7788 * 'responseError': function(rejection) {
7789 * // do something on error
7790 * if (canRecover(rejection)) {
7791 * return responseOrNewPromise
7793 * return $q.reject(rejection);
7798 * $httpProvider.interceptors.push('myHttpInterceptor');
7801 * // alternatively, register the interceptor via an anonymous factory
7802 * $httpProvider.interceptors.push(function($q, dependency1, dependency2) {
7804 * 'request': function(config) {
7808 * 'response': function(response) {
7815 * # Response interceptors (DEPRECATED)
7817 * Before you start creating interceptors, be sure to understand the
7818 * {@link ng.$q $q and deferred/promise APIs}.
7820 * For purposes of global error handling, authentication or any kind of synchronous or
7821 * asynchronous preprocessing of received responses, it is desirable to be able to intercept
7822 * responses for http requests before they are handed over to the application code that
7823 * initiated these requests. The response interceptors leverage the {@link ng.$q
7824 * promise apis} to fulfil this need for both synchronous and asynchronous preprocessing.
7826 * The interceptors are service factories that are registered with the $httpProvider by
7827 * adding them to the `$httpProvider.responseInterceptors` array. The factory is called and
7828 * injected with dependencies (if specified) and returns the interceptor — a function that
7829 * takes a {@link ng.$q promise} and returns the original or a new promise.
7832 * // register the interceptor as a service
7833 * $provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) {
7834 * return function(promise) {
7835 * return promise.then(function(response) {
7836 * // do something on success
7838 * }, function(response) {
7839 * // do something on error
7840 * if (canRecover(response)) {
7841 * return responseOrNewPromise
7843 * return $q.reject(response);
7848 * $httpProvider.responseInterceptors.push('myHttpInterceptor');
7851 * // register the interceptor via an anonymous factory
7852 * $httpProvider.responseInterceptors.push(function($q, dependency1, dependency2) {
7853 * return function(promise) {
7860 * # Security Considerations
7862 * When designing web applications, consider security threats from:
7864 * - [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx)
7865 * - [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery)
7867 * Both server and the client must cooperate in order to eliminate these threats. Angular comes
7868 * pre-configured with strategies that address these issues, but for this to work backend server
7869 * cooperation is required.
7871 * ## JSON Vulnerability Protection
7873 * A [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx)
7874 * allows third party website to turn your JSON resource URL into
7875 * [JSONP](http://en.wikipedia.org/wiki/JSONP) request under some conditions. To
7876 * counter this your server can prefix all JSON requests with following string `")]}',\n"`.
7877 * Angular will automatically strip the prefix before processing it as JSON.
7879 * For example if your server needs to return:
7884 * which is vulnerable to attack, your server can return:
7890 * Angular will strip the prefix, before processing the JSON.
7893 * ## Cross Site Request Forgery (XSRF) Protection
7895 * [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery) is a technique by which
7896 * an unauthorized site can gain your user's private data. Angular provides a mechanism
7897 * to counter XSRF. When performing XHR requests, the $http service reads a token from a cookie
7898 * (by default, `XSRF-TOKEN`) and sets it as an HTTP header (`X-XSRF-TOKEN`). Since only
7899 * JavaScript that runs on your domain could read the cookie, your server can be assured that
7900 * the XHR came from JavaScript running on your domain. The header will not be set for
7901 * cross-domain requests.
7903 * To take advantage of this, your server needs to set a token in a JavaScript readable session
7904 * cookie called `XSRF-TOKEN` on the first HTTP GET request. On subsequent XHR requests the
7905 * server can verify that the cookie matches `X-XSRF-TOKEN` HTTP header, and therefore be sure
7906 * that only JavaScript running on your domain could have sent the request. The token must be
7907 * unique for each user and must be verifiable by the server (to prevent the JavaScript from
7908 * making up its own tokens). We recommend that the token is a digest of your site's
7909 * authentication cookie with a [salt](https://en.wikipedia.org/wiki/Salt_(cryptography))
7910 * for added security.
7912 * The name of the headers can be specified using the xsrfHeaderName and xsrfCookieName
7913 * properties of either $httpProvider.defaults at config-time, $http.defaults at run-time,
7914 * or the per-request config object.
7917 * @param {object} config Object describing the request to be made and how it should be
7918 * processed. The object has following properties:
7920 * - **method** – `{string}` – HTTP method (e.g. 'GET', 'POST', etc)
7921 * - **url** – `{string}` – Absolute or relative URL of the resource that is being requested.
7922 * - **params** – `{Object.<string|Object>}` – Map of strings or objects which will be turned
7923 * to `?key1=value1&key2=value2` after the url. If the value is not a string, it will be
7925 * - **data** – `{string|Object}` – Data to be sent as the request message data.
7926 * - **headers** – `{Object}` – Map of strings or functions which return strings representing
7927 * HTTP headers to send to the server. If the return value of a function is null, the
7928 * header will not be sent.
7929 * - **xsrfHeaderName** – `{string}` – Name of HTTP header to populate with the XSRF token.
7930 * - **xsrfCookieName** – `{string}` – Name of cookie containing the XSRF token.
7931 * - **transformRequest** –
7932 * `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` –
7933 * transform function or an array of such functions. The transform function takes the http
7934 * request body and headers and returns its transformed (typically serialized) version.
7935 * - **transformResponse** –
7936 * `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` –
7937 * transform function or an array of such functions. The transform function takes the http
7938 * response body and headers and returns its transformed (typically deserialized) version.
7939 * - **cache** – `{boolean|Cache}` – If true, a default $http cache will be used to cache the
7940 * GET request, otherwise if a cache instance built with
7941 * {@link ng.$cacheFactory $cacheFactory}, this cache will be used for
7943 * - **timeout** – `{number|Promise}` – timeout in milliseconds, or {@link ng.$q promise}
7944 * that should abort the request when resolved.
7945 * - **withCredentials** - `{boolean}` - whether to set the `withCredentials` flag on the
7946 * XHR object. See [requests with credentials](https://developer.mozilla.org/docs/Web/HTTP/Access_control_CORS#Requests_with_credentials)
7947 * for more information.
7948 * - **responseType** - `{string}` - see
7949 * [requestType](https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest#responseType).
7951 * @returns {HttpPromise} Returns a {@link ng.$q promise} object with the
7952 * standard `then` method and two http specific methods: `success` and `error`. The `then`
7953 * method takes two arguments a success and an error callback which will be called with a
7954 * response object. The `success` and `error` methods take a single argument - a function that
7955 * will be called when the request succeeds or fails respectively. The arguments passed into
7956 * these functions are destructured representation of the response object passed into the
7957 * `then` method. The response object has these properties:
7959 * - **data** – `{string|Object}` – The response body transformed with the transform
7961 * - **status** – `{number}` – HTTP status code of the response.
7962 * - **headers** – `{function([headerName])}` – Header getter function.
7963 * - **config** – `{Object}` – The configuration object that was used to generate the request.
7964 * - **statusText** – `{string}` – HTTP status text of the response.
7966 * @property {Array.<Object>} pendingRequests Array of config objects for currently pending
7967 * requests. This is primarily meant to be used for debugging purposes.
7971 <example module="httpExample">
7972 <file name="index.html">
7973 <div ng-controller="FetchController">
7974 <select ng-model="method">
7975 <option>GET</option>
7976 <option>JSONP</option>
7978 <input type="text" ng-model="url" size="80"/>
7979 <button id="fetchbtn" ng-click="fetch()">fetch</button><br>
7980 <button id="samplegetbtn" ng-click="updateModel('GET', 'http-hello.html')">Sample GET</button>
7981 <button id="samplejsonpbtn"
7982 ng-click="updateModel('JSONP',
7983 'https://angularjs.org/greet.php?callback=JSON_CALLBACK&name=Super%20Hero')">
7986 <button id="invalidjsonpbtn"
7987 ng-click="updateModel('JSONP', 'https://angularjs.org/doesntexist&callback=JSON_CALLBACK')">
7990 <pre>http status code: {{status}}</pre>
7991 <pre>http response data: {{data}}</pre>
7994 <file name="script.js">
7995 angular.module('httpExample', [])
7996 .controller('FetchController', ['$scope', '$http', '$templateCache',
7997 function($scope, $http, $templateCache) {
7998 $scope.method = 'GET';
7999 $scope.url = 'http-hello.html';
8001 $scope.fetch = function() {
8003 $scope.response = null;
8005 $http({method: $scope.method, url: $scope.url, cache: $templateCache}).
8006 success(function(data, status) {
8007 $scope.status = status;
8010 error(function(data, status) {
8011 $scope.data = data || "Request failed";
8012 $scope.status = status;
8016 $scope.updateModel = function(method, url) {
8017 $scope.method = method;
8022 <file name="http-hello.html">
8025 <file name="protractor.js" type="protractor">
8026 var status = element(by.binding('status'));
8027 var data = element(by.binding('data'));
8028 var fetchBtn = element(by.id('fetchbtn'));
8029 var sampleGetBtn = element(by.id('samplegetbtn'));
8030 var sampleJsonpBtn = element(by.id('samplejsonpbtn'));
8031 var invalidJsonpBtn = element(by.id('invalidjsonpbtn'));
8033 it('should make an xhr GET request', function() {
8034 sampleGetBtn.click();
8036 expect(status.getText()).toMatch('200');
8037 expect(data.getText()).toMatch(/Hello, \$http!/);
8040 it('should make a JSONP request to angularjs.org', function() {
8041 sampleJsonpBtn.click();
8043 expect(status.getText()).toMatch('200');
8044 expect(data.getText()).toMatch(/Super Hero!/);
8047 it('should make JSONP request to invalid URL and invoke the error handler',
8049 invalidJsonpBtn.click();
8051 expect(status.getText()).toMatch('0');
8052 expect(data.getText()).toMatch('Request failed');
8057 function $http(requestConfig) {
8060 transformRequest: defaults.transformRequest,
8061 transformResponse: defaults.transformResponse
8063 var headers = mergeHeaders(requestConfig);
8065 extend(config, requestConfig);
8066 config.headers = headers;
8067 config.method = uppercase(config.method);
8069 var serverRequest = function(config) {
8070 headers = config.headers;
8071 var reqData = transformData(config.data, headersGetter(headers), config.transformRequest);
8073 // strip content-type if data is undefined
8074 if (isUndefined(reqData)) {
8075 forEach(headers, function(value, header) {
8076 if (lowercase(header) === 'content-type') {
8077 delete headers[header];
8082 if (isUndefined(config.withCredentials) && !isUndefined(defaults.withCredentials)) {
8083 config.withCredentials = defaults.withCredentials;
8087 return sendReq(config, reqData, headers).then(transformResponse, transformResponse);
8090 var chain = [serverRequest, undefined];
8091 var promise = $q.when(config);
8093 // apply interceptors
8094 forEach(reversedInterceptors, function(interceptor) {
8095 if (interceptor.request || interceptor.requestError) {
8096 chain.unshift(interceptor.request, interceptor.requestError);
8098 if (interceptor.response || interceptor.responseError) {
8099 chain.push(interceptor.response, interceptor.responseError);
8103 while(chain.length) {
8104 var thenFn = chain.shift();
8105 var rejectFn = chain.shift();
8107 promise = promise.then(thenFn, rejectFn);
8110 promise.success = function(fn) {
8111 promise.then(function(response) {
8112 fn(response.data, response.status, response.headers, config);
8117 promise.error = function(fn) {
8118 promise.then(null, function(response) {
8119 fn(response.data, response.status, response.headers, config);
8126 function transformResponse(response) {
8127 // make a copy since the response must be cacheable
8128 var resp = extend({}, response, {
8129 data: transformData(response.data, response.headers, config.transformResponse)
8131 return (isSuccess(response.status))
8136 function mergeHeaders(config) {
8137 var defHeaders = defaults.headers,
8138 reqHeaders = extend({}, config.headers),
8139 defHeaderName, lowercaseDefHeaderName, reqHeaderName;
8141 defHeaders = extend({}, defHeaders.common, defHeaders[lowercase(config.method)]);
8143 // using for-in instead of forEach to avoid unecessary iteration after header has been found
8144 defaultHeadersIteration:
8145 for (defHeaderName in defHeaders) {
8146 lowercaseDefHeaderName = lowercase(defHeaderName);
8148 for (reqHeaderName in reqHeaders) {
8149 if (lowercase(reqHeaderName) === lowercaseDefHeaderName) {
8150 continue defaultHeadersIteration;
8154 reqHeaders[defHeaderName] = defHeaders[defHeaderName];
8157 // execute if header value is a function for merged headers
8158 execHeaders(reqHeaders);
8161 function execHeaders(headers) {
8164 forEach(headers, function(headerFn, header) {
8165 if (isFunction(headerFn)) {
8166 headerContent = headerFn();
8167 if (headerContent != null) {
8168 headers[header] = headerContent;
8170 delete headers[header];
8178 $http.pendingRequests = [];
8185 * Shortcut method to perform `GET` request.
8187 * @param {string} url Relative or absolute URL specifying the destination of the request
8188 * @param {Object=} config Optional configuration object
8189 * @returns {HttpPromise} Future object
8194 * @name $http#delete
8197 * Shortcut method to perform `DELETE` request.
8199 * @param {string} url Relative or absolute URL specifying the destination of the request
8200 * @param {Object=} config Optional configuration object
8201 * @returns {HttpPromise} Future object
8209 * Shortcut method to perform `HEAD` request.
8211 * @param {string} url Relative or absolute URL specifying the destination of the request
8212 * @param {Object=} config Optional configuration object
8213 * @returns {HttpPromise} Future object
8221 * Shortcut method to perform `JSONP` request.
8223 * @param {string} url Relative or absolute URL specifying the destination of the request.
8224 * The name of the callback should be the string `JSON_CALLBACK`.
8225 * @param {Object=} config Optional configuration object
8226 * @returns {HttpPromise} Future object
8228 createShortMethods('get', 'delete', 'head', 'jsonp');
8235 * Shortcut method to perform `POST` request.
8237 * @param {string} url Relative or absolute URL specifying the destination of the request
8238 * @param {*} data Request content
8239 * @param {Object=} config Optional configuration object
8240 * @returns {HttpPromise} Future object
8248 * Shortcut method to perform `PUT` request.
8250 * @param {string} url Relative or absolute URL specifying the destination of the request
8251 * @param {*} data Request content
8252 * @param {Object=} config Optional configuration object
8253 * @returns {HttpPromise} Future object
8255 createShortMethodsWithData('post', 'put');
8259 * @name $http#defaults
8262 * Runtime equivalent of the `$httpProvider.defaults` property. Allows configuration of
8263 * default headers, withCredentials as well as request and response transformations.
8265 * See "Setting HTTP Headers" and "Transforming Requests and Responses" sections above.
8267 $http.defaults = defaults;
8273 function createShortMethods(names) {
8274 forEach(arguments, function(name) {
8275 $http[name] = function(url, config) {
8276 return $http(extend(config || {}, {
8285 function createShortMethodsWithData(name) {
8286 forEach(arguments, function(name) {
8287 $http[name] = function(url, data, config) {
8288 return $http(extend(config || {}, {
8299 * Makes the request.
8301 * !!! ACCESSES CLOSURE VARS:
8302 * $httpBackend, defaults, $log, $rootScope, defaultCache, $http.pendingRequests
8304 function sendReq(config, reqData, reqHeaders) {
8305 var deferred = $q.defer(),
8306 promise = deferred.promise,
8309 url = buildUrl(config.url, config.params);
8311 $http.pendingRequests.push(config);
8312 promise.then(removePendingReq, removePendingReq);
8315 if ((config.cache || defaults.cache) && config.cache !== false &&
8316 (config.method === 'GET' || config.method === 'JSONP')) {
8317 cache = isObject(config.cache) ? config.cache
8318 : isObject(defaults.cache) ? defaults.cache
8323 cachedResp = cache.get(url);
8324 if (isDefined(cachedResp)) {
8325 if (isPromiseLike(cachedResp)) {
8326 // cached request has already been sent, but there is no response yet
8327 cachedResp.then(removePendingReq, removePendingReq);
8330 // serving from cache
8331 if (isArray(cachedResp)) {
8332 resolvePromise(cachedResp[1], cachedResp[0], shallowCopy(cachedResp[2]), cachedResp[3]);
8334 resolvePromise(cachedResp, 200, {}, 'OK');
8338 // put the promise for the non-transformed response into cache as a placeholder
8339 cache.put(url, promise);
8344 // if we won't have the response in cache, set the xsrf headers and
8345 // send the request to the backend
8346 if (isUndefined(cachedResp)) {
8347 var xsrfValue = urlIsSameOrigin(config.url)
8348 ? $browser.cookies()[config.xsrfCookieName || defaults.xsrfCookieName]
8351 reqHeaders[(config.xsrfHeaderName || defaults.xsrfHeaderName)] = xsrfValue;
8354 $httpBackend(config.method, url, reqData, done, reqHeaders, config.timeout,
8355 config.withCredentials, config.responseType);
8362 * Callback registered to $httpBackend():
8363 * - caches the response if desired
8364 * - resolves the raw $http promise
8367 function done(status, response, headersString, statusText) {
8369 if (isSuccess(status)) {
8370 cache.put(url, [status, response, parseHeaders(headersString), statusText]);
8372 // remove promise from the cache
8377 resolvePromise(response, status, headersString, statusText);
8378 if (!$rootScope.$$phase) $rootScope.$apply();
8383 * Resolves the raw $http promise.
8385 function resolvePromise(response, status, headers, statusText) {
8386 // normalize internal statuses to 0
8387 status = Math.max(status, 0);
8389 (isSuccess(status) ? deferred.resolve : deferred.reject)({
8392 headers: headersGetter(headers),
8394 statusText : statusText
8399 function removePendingReq() {
8400 var idx = indexOf($http.pendingRequests, config);
8401 if (idx !== -1) $http.pendingRequests.splice(idx, 1);
8406 function buildUrl(url, params) {
8407 if (!params) return url;
8409 forEachSorted(params, function(value, key) {
8410 if (value === null || isUndefined(value)) return;
8411 if (!isArray(value)) value = [value];
8413 forEach(value, function(v) {
8416 v = v.toISOString();
8421 parts.push(encodeUriQuery(key) + '=' +
8425 if(parts.length > 0) {
8426 url += ((url.indexOf('?') == -1) ? '?' : '&') + parts.join('&');
8433 function createXhr(method) {
8434 //if IE and the method is not RFC2616 compliant, or if XMLHttpRequest
8435 //is not available, try getting an ActiveXObject. Otherwise, use XMLHttpRequest
8436 //if it is available
8437 if (msie <= 8 && (!method.match(/^(get|post|head|put|delete|options)$/i) ||
8438 !window.XMLHttpRequest)) {
8439 return new window.ActiveXObject("Microsoft.XMLHTTP");
8440 } else if (window.XMLHttpRequest) {
8441 return new window.XMLHttpRequest();
8444 throw minErr('$httpBackend')('noxhr', "This browser does not support XMLHttpRequest.");
8449 * @name $httpBackend
8451 * @requires $document
8454 * HTTP backend used by the {@link ng.$http service} that delegates to
8455 * XMLHttpRequest object or JSONP and deals with browser incompatibilities.
8457 * You should never need to use this service directly, instead use the higher-level abstractions:
8458 * {@link ng.$http $http} or {@link ngResource.$resource $resource}.
8460 * During testing this implementation is swapped with {@link ngMock.$httpBackend mock
8461 * $httpBackend} which can be trained with responses.
8463 function $HttpBackendProvider() {
8464 this.$get = ['$browser', '$window', '$document', function($browser, $window, $document) {
8465 return createHttpBackend($browser, createXhr, $browser.defer, $window.angular.callbacks, $document[0]);
8469 function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDocument) {
8472 // TODO(vojta): fix the signature
8473 return function(method, url, post, callback, headers, timeout, withCredentials, responseType) {
8475 $browser.$$incOutstandingRequestCount();
8476 url = url || $browser.url();
8478 if (lowercase(method) == 'jsonp') {
8479 var callbackId = '_' + (callbacks.counter++).toString(36);
8480 callbacks[callbackId] = function(data) {
8481 callbacks[callbackId].data = data;
8482 callbacks[callbackId].called = true;
8485 var jsonpDone = jsonpReq(url.replace('JSON_CALLBACK', 'angular.callbacks.' + callbackId),
8486 callbackId, function(status, text) {
8487 completeRequest(callback, status, callbacks[callbackId].data, "", text);
8488 callbacks[callbackId] = noop;
8492 var xhr = createXhr(method);
8494 xhr.open(method, url, true);
8495 forEach(headers, function(value, key) {
8496 if (isDefined(value)) {
8497 xhr.setRequestHeader(key, value);
8501 // In IE6 and 7, this might be called synchronously when xhr.send below is called and the
8502 // response is in the cache. the promise api will ensure that to the app code the api is
8504 xhr.onreadystatechange = function() {
8505 // onreadystatechange might get called multiple times with readyState === 4 on mobile webkit caused by
8506 // xhrs that are resolved while the app is in the background (see #5426).
8507 // since calling completeRequest sets the `xhr` variable to null, we just check if it's not null before
8510 // we can't set xhr.onreadystatechange to undefined or delete it because that breaks IE8 (method=PATCH) and
8511 // Safari respectively.
8512 if (xhr && xhr.readyState == 4) {
8513 var responseHeaders = null,
8517 if(status !== ABORTED) {
8518 responseHeaders = xhr.getAllResponseHeaders();
8520 // responseText is the old-school way of retrieving response (supported by IE8 & 9)
8521 // response/responseType properties were introduced in XHR Level2 spec (supported by IE10)
8522 response = ('response' in xhr) ? xhr.response : xhr.responseText;
8525 // Accessing statusText on an aborted xhr object will
8526 // throw an 'c00c023f error' in IE9 and lower, don't touch it.
8527 if (!(status === ABORTED && msie < 10)) {
8528 statusText = xhr.statusText;
8531 completeRequest(callback,
8532 status || xhr.status,
8539 if (withCredentials) {
8540 xhr.withCredentials = true;
8545 xhr.responseType = responseType;
8547 // WebKit added support for the json responseType value on 09/03/2013
8548 // https://bugs.webkit.org/show_bug.cgi?id=73648. Versions of Safari prior to 7 are
8549 // known to throw when setting the value "json" as the response type. Other older
8550 // browsers implementing the responseType
8552 // The json response type can be ignored if not supported, because JSON payloads are
8553 // parsed on the client-side regardless.
8554 if (responseType !== 'json') {
8560 xhr.send(post || null);
8564 var timeoutId = $browserDefer(timeoutRequest, timeout);
8565 } else if (isPromiseLike(timeout)) {
8566 timeout.then(timeoutRequest);
8570 function timeoutRequest() {
8572 jsonpDone && jsonpDone();
8576 function completeRequest(callback, status, response, headersString, statusText) {
8577 // cancel timeout and subsequent timeout promise resolution
8578 timeoutId && $browserDefer.cancel(timeoutId);
8579 jsonpDone = xhr = null;
8581 // fix status code when it is 0 (0 status is undocumented).
8582 // Occurs when accessing file resources or on Android 4.1 stock browser
8583 // while retrieving files from application cache.
8585 status = response ? 200 : urlResolve(url).protocol == 'file' ? 404 : 0;
8588 // normalize IE bug (http://bugs.jquery.com/ticket/1450)
8589 status = status === 1223 ? 204 : status;
8590 statusText = statusText || '';
8592 callback(status, response, headersString, statusText);
8593 $browser.$$completeOutstandingRequest(noop);
8597 function jsonpReq(url, callbackId, done) {
8598 // we can't use jQuery/jqLite here because jQuery does crazy shit with script elements, e.g.:
8599 // - fetches local scripts via XHR and evals them
8600 // - adds and immediately removes script elements from the document
8601 var script = rawDocument.createElement('script'), callback = null;
8602 script.type = "text/javascript";
8604 script.async = true;
8606 callback = function(event) {
8607 removeEventListenerFn(script, "load", callback);
8608 removeEventListenerFn(script, "error", callback);
8609 rawDocument.body.removeChild(script);
8612 var text = "unknown";
8615 if (event.type === "load" && !callbacks[callbackId].called) {
8616 event = { type: "error" };
8619 status = event.type === "error" ? 404 : 200;
8627 addEventListenerFn(script, "load", callback);
8628 addEventListenerFn(script, "error", callback);
8631 script.onreadystatechange = function() {
8632 if (isString(script.readyState) && /loaded|complete/.test(script.readyState)) {
8633 script.onreadystatechange = null;
8641 rawDocument.body.appendChild(script);
8646 var $interpolateMinErr = minErr('$interpolate');
8650 * @name $interpolateProvider
8655 * Used for configuring the interpolation markup. Defaults to `{{` and `}}`.
8658 <example module="customInterpolationApp">
8659 <file name="index.html">
8661 var customInterpolationApp = angular.module('customInterpolationApp', []);
8663 customInterpolationApp.config(function($interpolateProvider) {
8664 $interpolateProvider.startSymbol('//');
8665 $interpolateProvider.endSymbol('//');
8669 customInterpolationApp.controller('DemoController', function() {
8670 this.label = "This binding is brought you by // interpolation symbols.";
8673 <div ng-app="App" ng-controller="DemoController as demo">
8677 <file name="protractor.js" type="protractor">
8678 it('should interpolate binding with custom symbols', function() {
8679 expect(element(by.binding('demo.label')).getText()).toBe('This binding is brought you by // interpolation symbols.');
8684 function $InterpolateProvider() {
8685 var startSymbol = '{{';
8686 var endSymbol = '}}';
8690 * @name $interpolateProvider#startSymbol
8692 * Symbol to denote start of expression in the interpolated string. Defaults to `{{`.
8694 * @param {string=} value new value to set the starting symbol to.
8695 * @returns {string|self} Returns the symbol when used as getter and self if used as setter.
8697 this.startSymbol = function(value){
8699 startSymbol = value;
8708 * @name $interpolateProvider#endSymbol
8710 * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
8712 * @param {string=} value new value to set the ending symbol to.
8713 * @returns {string|self} Returns the symbol when used as getter and self if used as setter.
8715 this.endSymbol = function(value){
8725 this.$get = ['$parse', '$exceptionHandler', '$sce', function($parse, $exceptionHandler, $sce) {
8726 var startSymbolLength = startSymbol.length,
8727 endSymbolLength = endSymbol.length;
8731 * @name $interpolate
8739 * Compiles a string with markup into an interpolation function. This service is used by the
8740 * HTML {@link ng.$compile $compile} service for data binding. See
8741 * {@link ng.$interpolateProvider $interpolateProvider} for configuring the
8742 * interpolation markup.
8746 * var $interpolate = ...; // injected
8747 * var exp = $interpolate('Hello {{name | uppercase}}!');
8748 * expect(exp({name:'Angular'}).toEqual('Hello ANGULAR!');
8752 * @param {string} text The text with markup to interpolate.
8753 * @param {boolean=} mustHaveExpression if set to true then the interpolation string must have
8754 * embedded expression in order to return an interpolation function. Strings with no
8755 * embedded expression will return null for the interpolation function.
8756 * @param {string=} trustedContext when provided, the returned function passes the interpolated
8757 * result through {@link ng.$sce#getTrusted $sce.getTrusted(interpolatedResult,
8758 * trustedContext)} before returning it. Refer to the {@link ng.$sce $sce} service that
8759 * provides Strict Contextual Escaping for details.
8760 * @returns {function(context)} an interpolation function which is used to compute the
8761 * interpolated string. The function has these parameters:
8763 * * `context`: an object against which any expressions embedded in the strings are evaluated
8767 function $interpolate(text, mustHaveExpression, trustedContext) {
8772 length = text.length,
8773 hasInterpolation = false,
8778 while(index < length) {
8779 if ( ((startIndex = text.indexOf(startSymbol, index)) != -1) &&
8780 ((endIndex = text.indexOf(endSymbol, startIndex + startSymbolLength)) != -1) ) {
8781 (index != startIndex) && parts.push(text.substring(index, startIndex));
8782 parts.push(fn = $parse(exp = text.substring(startIndex + startSymbolLength, endIndex)));
8784 index = endIndex + endSymbolLength;
8785 hasInterpolation = true;
8787 // we did not find anything, so we have to add the remainder to the parts array
8788 (index != length) && parts.push(text.substring(index));
8793 if (!(length = parts.length)) {
8794 // we added, nothing, must have been an empty string.
8799 // Concatenating expressions makes it hard to reason about whether some combination of
8800 // concatenated values are unsafe to use and could easily lead to XSS. By requiring that a
8801 // single expression be used for iframe[src], object[src], etc., we ensure that the value
8802 // that's used is assigned or constructed by some JS code somewhere that is more testable or
8803 // make it obvious that you bound the value to some user controlled value. This helps reduce
8804 // the load when auditing for XSS issues.
8805 if (trustedContext && parts.length > 1) {
8806 throw $interpolateMinErr('noconcat',
8807 "Error while interpolating: {0}\nStrict Contextual Escaping disallows " +
8808 "interpolations that concatenate multiple expressions when a trusted value is " +
8809 "required. See http://docs.angularjs.org/api/ng.$sce", text);
8812 if (!mustHaveExpression || hasInterpolation) {
8813 concat.length = length;
8814 fn = function(context) {
8816 for(var i = 0, ii = length, part; i<ii; i++) {
8817 if (typeof (part = parts[i]) == 'function') {
8818 part = part(context);
8819 if (trustedContext) {
8820 part = $sce.getTrusted(trustedContext, part);
8822 part = $sce.valueOf(part);
8824 if (part == null) { // null || undefined
8827 switch (typeof part) {
8839 part = toJson(part);
8846 return concat.join('');
8849 var newErr = $interpolateMinErr('interr', "Can't interpolate: {0}\n{1}", text,
8851 $exceptionHandler(newErr);
8863 * @name $interpolate#startSymbol
8865 * Symbol to denote the start of expression in the interpolated string. Defaults to `{{`.
8867 * Use {@link ng.$interpolateProvider#startSymbol `$interpolateProvider.startSymbol`} to change
8870 * @returns {string} start symbol.
8872 $interpolate.startSymbol = function() {
8879 * @name $interpolate#endSymbol
8881 * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
8883 * Use {@link ng.$interpolateProvider#endSymbol `$interpolateProvider.endSymbol`} to change
8886 * @returns {string} end symbol.
8888 $interpolate.endSymbol = function() {
8892 return $interpolate;
8896 function $IntervalProvider() {
8897 this.$get = ['$rootScope', '$window', '$q',
8898 function($rootScope, $window, $q) {
8907 * Angular's wrapper for `window.setInterval`. The `fn` function is executed every `delay`
8910 * The return value of registering an interval function is a promise. This promise will be
8911 * notified upon each tick of the interval, and will be resolved after `count` iterations, or
8912 * run indefinitely if `count` is not defined. The value of the notification will be the
8913 * number of iterations that have run.
8914 * To cancel an interval, call `$interval.cancel(promise)`.
8916 * In tests you can use {@link ngMock.$interval#flush `$interval.flush(millis)`} to
8917 * move forward by `millis` milliseconds and trigger any functions scheduled to run in that
8920 * <div class="alert alert-warning">
8921 * **Note**: Intervals created by this service must be explicitly destroyed when you are finished
8922 * with them. In particular they are not automatically destroyed when a controller's scope or a
8923 * directive's element are destroyed.
8924 * You should take this into consideration and make sure to always cancel the interval at the
8925 * appropriate moment. See the example below for more details on how and when to do this.
8928 * @param {function()} fn A function that should be called repeatedly.
8929 * @param {number} delay Number of milliseconds between each function call.
8930 * @param {number=} [count=0] Number of times to repeat. If not set, or 0, will repeat
8932 * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
8933 * will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
8934 * @returns {promise} A promise which will be notified on each iteration.
8937 * <example module="intervalExample">
8938 * <file name="index.html">
8940 * angular.module('intervalExample', [])
8941 * .controller('ExampleController', ['$scope', '$interval',
8942 * function($scope, $interval) {
8943 * $scope.format = 'M/d/yy h:mm:ss a';
8944 * $scope.blood_1 = 100;
8945 * $scope.blood_2 = 120;
8948 * $scope.fight = function() {
8949 * // Don't start a new fight if we are already fighting
8950 * if ( angular.isDefined(stop) ) return;
8952 * stop = $interval(function() {
8953 * if ($scope.blood_1 > 0 && $scope.blood_2 > 0) {
8954 * $scope.blood_1 = $scope.blood_1 - 3;
8955 * $scope.blood_2 = $scope.blood_2 - 4;
8957 * $scope.stopFight();
8962 * $scope.stopFight = function() {
8963 * if (angular.isDefined(stop)) {
8964 * $interval.cancel(stop);
8969 * $scope.resetFight = function() {
8970 * $scope.blood_1 = 100;
8971 * $scope.blood_2 = 120;
8974 * $scope.$on('$destroy', function() {
8975 * // Make sure that the interval is destroyed too
8976 * $scope.stopFight();
8979 * // Register the 'myCurrentTime' directive factory method.
8980 * // We inject $interval and dateFilter service since the factory method is DI.
8981 * .directive('myCurrentTime', ['$interval', 'dateFilter',
8982 * function($interval, dateFilter) {
8983 * // return the directive link function. (compile function not needed)
8984 * return function(scope, element, attrs) {
8985 * var format, // date format
8986 * stopTime; // so that we can cancel the time updates
8988 * // used to update the UI
8989 * function updateTime() {
8990 * element.text(dateFilter(new Date(), format));
8993 * // watch the expression, and update the UI on change.
8994 * scope.$watch(attrs.myCurrentTime, function(value) {
8999 * stopTime = $interval(updateTime, 1000);
9001 * // listen on DOM destroy (removal) event, and cancel the next UI update
9002 * // to prevent updating time after the DOM element was removed.
9003 * element.bind('$destroy', function() {
9004 * $interval.cancel(stopTime);
9011 * <div ng-controller="ExampleController">
9012 * Date format: <input ng-model="format"> <hr/>
9013 * Current time is: <span my-current-time="format"></span>
9015 * Blood 1 : <font color='red'>{{blood_1}}</font>
9016 * Blood 2 : <font color='red'>{{blood_2}}</font>
9017 * <button type="button" data-ng-click="fight()">Fight</button>
9018 * <button type="button" data-ng-click="stopFight()">StopFight</button>
9019 * <button type="button" data-ng-click="resetFight()">resetFight</button>
9026 function interval(fn, delay, count, invokeApply) {
9027 var setInterval = $window.setInterval,
9028 clearInterval = $window.clearInterval,
9029 deferred = $q.defer(),
9030 promise = deferred.promise,
9032 skipApply = (isDefined(invokeApply) && !invokeApply);
9034 count = isDefined(count) ? count : 0;
9036 promise.then(null, null, fn);
9038 promise.$$intervalId = setInterval(function tick() {
9039 deferred.notify(iteration++);
9041 if (count > 0 && iteration >= count) {
9042 deferred.resolve(iteration);
9043 clearInterval(promise.$$intervalId);
9044 delete intervals[promise.$$intervalId];
9047 if (!skipApply) $rootScope.$apply();
9051 intervals[promise.$$intervalId] = deferred;
9059 * @name $interval#cancel
9062 * Cancels a task associated with the `promise`.
9064 * @param {promise} promise returned by the `$interval` function.
9065 * @returns {boolean} Returns `true` if the task was successfully canceled.
9067 interval.cancel = function(promise) {
9068 if (promise && promise.$$intervalId in intervals) {
9069 intervals[promise.$$intervalId].reject('canceled');
9070 $window.clearInterval(promise.$$intervalId);
9071 delete intervals[promise.$$intervalId];
9086 * $locale service provides localization rules for various Angular components. As of right now the
9087 * only public api is:
9089 * * `id` – `{string}` – locale id formatted as `languageId-countryId` (e.g. `en-us`)
9091 function $LocaleProvider(){
9092 this.$get = function() {
9100 { // Decimal Pattern
9110 },{ //Currency Pattern
9127 'January,February,March,April,May,June,July,August,September,October,November,December'
9129 SHORTMONTH: 'Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec'.split(','),
9130 DAY: 'Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday'.split(','),
9131 SHORTDAY: 'Sun,Mon,Tue,Wed,Thu,Fri,Sat'.split(','),
9133 medium: 'MMM d, y h:mm:ss a',
9134 short: 'M/d/yy h:mm a',
9135 fullDate: 'EEEE, MMMM d, y',
9136 longDate: 'MMMM d, y',
9137 mediumDate: 'MMM d, y',
9138 shortDate: 'M/d/yy',
9139 mediumTime: 'h:mm:ss a',
9143 pluralCat: function(num) {
9153 var PATH_MATCH = /^([^\?#]*)(\?([^#]*))?(#(.*))?$/,
9154 DEFAULT_PORTS = {'http': 80, 'https': 443, 'ftp': 21};
9155 var $locationMinErr = minErr('$location');
9159 * Encode path using encodeUriSegment, ignoring forward slashes
9161 * @param {string} path Path to encode
9164 function encodePath(path) {
9165 var segments = path.split('/'),
9166 i = segments.length;
9169 segments[i] = encodeUriSegment(segments[i]);
9172 return segments.join('/');
9175 function parseAbsoluteUrl(absoluteUrl, locationObj, appBase) {
9176 var parsedUrl = urlResolve(absoluteUrl, appBase);
9178 locationObj.$$protocol = parsedUrl.protocol;
9179 locationObj.$$host = parsedUrl.hostname;
9180 locationObj.$$port = int(parsedUrl.port) || DEFAULT_PORTS[parsedUrl.protocol] || null;
9184 function parseAppUrl(relativeUrl, locationObj, appBase) {
9185 var prefixed = (relativeUrl.charAt(0) !== '/');
9187 relativeUrl = '/' + relativeUrl;
9189 var match = urlResolve(relativeUrl, appBase);
9190 locationObj.$$path = decodeURIComponent(prefixed && match.pathname.charAt(0) === '/' ?
9191 match.pathname.substring(1) : match.pathname);
9192 locationObj.$$search = parseKeyValue(match.search);
9193 locationObj.$$hash = decodeURIComponent(match.hash);
9195 // make sure path starts with '/';
9196 if (locationObj.$$path && locationObj.$$path.charAt(0) != '/') {
9197 locationObj.$$path = '/' + locationObj.$$path;
9204 * @param {string} begin
9205 * @param {string} whole
9206 * @returns {string} returns text from whole after begin or undefined if it does not begin with
9209 function beginsWith(begin, whole) {
9210 if (whole.indexOf(begin) === 0) {
9211 return whole.substr(begin.length);
9216 function stripHash(url) {
9217 var index = url.indexOf('#');
9218 return index == -1 ? url : url.substr(0, index);
9222 function stripFile(url) {
9223 return url.substr(0, stripHash(url).lastIndexOf('/') + 1);
9226 /* return the server only (scheme://host:port) */
9227 function serverBase(url) {
9228 return url.substring(0, url.indexOf('/', url.indexOf('//') + 2));
9233 * LocationHtml5Url represents an url
9234 * This object is exposed as $location service when HTML5 mode is enabled and supported
9237 * @param {string} appBase application base URL
9238 * @param {string} basePrefix url path prefix
9240 function LocationHtml5Url(appBase, basePrefix) {
9241 this.$$html5 = true;
9242 basePrefix = basePrefix || '';
9243 var appBaseNoFile = stripFile(appBase);
9244 parseAbsoluteUrl(appBase, this, appBase);
9248 * Parse given html5 (regular) url string into properties
9249 * @param {string} newAbsoluteUrl HTML5 url
9252 this.$$parse = function(url) {
9253 var pathUrl = beginsWith(appBaseNoFile, url);
9254 if (!isString(pathUrl)) {
9255 throw $locationMinErr('ipthprfx', 'Invalid url "{0}", missing path prefix "{1}".', url,
9259 parseAppUrl(pathUrl, this, appBase);
9269 * Compose url and update `absUrl` property
9272 this.$$compose = function() {
9273 var search = toKeyValue(this.$$search),
9274 hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
9276 this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
9277 this.$$absUrl = appBaseNoFile + this.$$url.substr(1); // first char is always '/'
9280 this.$$rewrite = function(url) {
9281 var appUrl, prevAppUrl;
9283 if ( (appUrl = beginsWith(appBase, url)) !== undefined ) {
9284 prevAppUrl = appUrl;
9285 if ( (appUrl = beginsWith(basePrefix, appUrl)) !== undefined ) {
9286 return appBaseNoFile + (beginsWith('/', appUrl) || appUrl);
9288 return appBase + prevAppUrl;
9290 } else if ( (appUrl = beginsWith(appBaseNoFile, url)) !== undefined ) {
9291 return appBaseNoFile + appUrl;
9292 } else if (appBaseNoFile == url + '/') {
9293 return appBaseNoFile;
9300 * LocationHashbangUrl represents url
9301 * This object is exposed as $location service when developer doesn't opt into html5 mode.
9302 * It also serves as the base class for html5 mode fallback on legacy browsers.
9305 * @param {string} appBase application base URL
9306 * @param {string} hashPrefix hashbang prefix
9308 function LocationHashbangUrl(appBase, hashPrefix) {
9309 var appBaseNoFile = stripFile(appBase);
9311 parseAbsoluteUrl(appBase, this, appBase);
9315 * Parse given hashbang url into properties
9316 * @param {string} url Hashbang url
9319 this.$$parse = function(url) {
9320 var withoutBaseUrl = beginsWith(appBase, url) || beginsWith(appBaseNoFile, url);
9321 var withoutHashUrl = withoutBaseUrl.charAt(0) == '#'
9322 ? beginsWith(hashPrefix, withoutBaseUrl)
9327 if (!isString(withoutHashUrl)) {
9328 throw $locationMinErr('ihshprfx', 'Invalid url "{0}", missing hash prefix "{1}".', url,
9331 parseAppUrl(withoutHashUrl, this, appBase);
9333 this.$$path = removeWindowsDriveName(this.$$path, withoutHashUrl, appBase);
9338 * In Windows, on an anchor node on documents loaded from
9339 * the filesystem, the browser will return a pathname
9340 * prefixed with the drive name ('/C:/path') when a
9341 * pathname without a drive is set:
9342 * * a.setAttribute('href', '/foo')
9343 * * a.pathname === '/C:/foo' //true
9345 * Inside of Angular, we're always using pathnames that
9346 * do not include drive names for routing.
9348 function removeWindowsDriveName (path, url, base) {
9350 Matches paths for file protocol on windows,
9351 such as /C:/foo/bar, and captures only /foo/bar.
9353 var windowsFilePathExp = /^\/[A-Z]:(\/.*)/;
9355 var firstPathSegmentMatch;
9357 //Get the relative path from the input URL.
9358 if (url.indexOf(base) === 0) {
9359 url = url.replace(base, '');
9362 // The input URL intentionally contains a first path segment that ends with a colon.
9363 if (windowsFilePathExp.exec(url)) {
9367 firstPathSegmentMatch = windowsFilePathExp.exec(path);
9368 return firstPathSegmentMatch ? firstPathSegmentMatch[1] : path;
9373 * Compose hashbang url and update `absUrl` property
9376 this.$$compose = function() {
9377 var search = toKeyValue(this.$$search),
9378 hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
9380 this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
9381 this.$$absUrl = appBase + (this.$$url ? hashPrefix + this.$$url : '');
9384 this.$$rewrite = function(url) {
9385 if(stripHash(appBase) == stripHash(url)) {
9393 * LocationHashbangUrl represents url
9394 * This object is exposed as $location service when html5 history api is enabled but the browser
9395 * does not support it.
9398 * @param {string} appBase application base URL
9399 * @param {string} hashPrefix hashbang prefix
9401 function LocationHashbangInHtml5Url(appBase, hashPrefix) {
9402 this.$$html5 = true;
9403 LocationHashbangUrl.apply(this, arguments);
9405 var appBaseNoFile = stripFile(appBase);
9407 this.$$rewrite = function(url) {
9410 if ( appBase == stripHash(url) ) {
9412 } else if ( (appUrl = beginsWith(appBaseNoFile, url)) ) {
9413 return appBase + hashPrefix + appUrl;
9414 } else if ( appBaseNoFile === url + '/') {
9415 return appBaseNoFile;
9419 this.$$compose = function() {
9420 var search = toKeyValue(this.$$search),
9421 hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
9423 this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
9424 // include hashPrefix in $$absUrl when $$url is empty so IE8 & 9 do not reload page because of removal of '#'
9425 this.$$absUrl = appBase + hashPrefix + this.$$url;
9431 LocationHashbangInHtml5Url.prototype =
9432 LocationHashbangUrl.prototype =
9433 LocationHtml5Url.prototype = {
9436 * Are we in html5 mode?
9442 * Has any change been replacing ?
9449 * @name $location#absUrl
9452 * This method is getter only.
9454 * Return full url representation with all segments encoded according to rules specified in
9455 * [RFC 3986](http://www.ietf.org/rfc/rfc3986.txt).
9457 * @return {string} full url
9459 absUrl: locationGetter('$$absUrl'),
9463 * @name $location#url
9466 * This method is getter / setter.
9468 * Return url (e.g. `/path?a=b#hash`) when called without any parameter.
9470 * Change path, search and hash, when called with parameter and return `$location`.
9472 * @param {string=} url New url without base prefix (e.g. `/path?a=b#hash`)
9473 * @return {string} url
9475 url: function(url) {
9476 if (isUndefined(url))
9479 var match = PATH_MATCH.exec(url);
9480 if (match[1]) this.path(decodeURIComponent(match[1]));
9481 if (match[2] || match[1]) this.search(match[3] || '');
9482 this.hash(match[5] || '');
9489 * @name $location#protocol
9492 * This method is getter only.
9494 * Return protocol of current url.
9496 * @return {string} protocol of current url
9498 protocol: locationGetter('$$protocol'),
9502 * @name $location#host
9505 * This method is getter only.
9507 * Return host of current url.
9509 * @return {string} host of current url.
9511 host: locationGetter('$$host'),
9515 * @name $location#port
9518 * This method is getter only.
9520 * Return port of current url.
9522 * @return {Number} port
9524 port: locationGetter('$$port'),
9528 * @name $location#path
9531 * This method is getter / setter.
9533 * Return path of current url when called without any parameter.
9535 * Change path when called with parameter and return `$location`.
9537 * Note: Path should always begin with forward slash (/), this method will add the forward slash
9540 * @param {(string|number)=} path New path
9541 * @return {string} path
9543 path: locationGetterSetter('$$path', function(path) {
9544 path = path ? path.toString() : '';
9545 return path.charAt(0) == '/' ? path : '/' + path;
9550 * @name $location#search
9553 * This method is getter / setter.
9555 * Return search part (as object) of current url when called without any parameter.
9557 * Change search part when called with parameter and return `$location`.
9561 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
9562 * var searchObject = $location.search();
9563 * // => {foo: 'bar', baz: 'xoxo'}
9566 * // set foo to 'yipee'
9567 * $location.search('foo', 'yipee');
9571 * @param {string|Object.<string>|Object.<Array.<string>>} search New search params - string or
9574 * When called with a single argument the method acts as a setter, setting the `search` component
9575 * of `$location` to the specified value.
9577 * If the argument is a hash object containing an array of values, these values will be encoded
9578 * as duplicate search parameters in the url.
9580 * @param {(string|Number|Array<string>|boolean)=} paramValue If `search` is a string or number, then `paramValue`
9581 * will override only a single search property.
9583 * If `paramValue` is an array, it will override the property of the `search` component of
9584 * `$location` specified via the first argument.
9586 * If `paramValue` is `null`, the property specified via the first argument will be deleted.
9588 * If `paramValue` is `true`, the property specified via the first argument will be added with no
9589 * value nor trailing equal sign.
9591 * @return {Object} If called with no arguments returns the parsed `search` object. If called with
9592 * one or more arguments returns `$location` object itself.
9594 search: function(search, paramValue) {
9595 switch (arguments.length) {
9597 return this.$$search;
9599 if (isString(search) || isNumber(search)) {
9600 search = search.toString();
9601 this.$$search = parseKeyValue(search);
9602 } else if (isObject(search)) {
9603 // remove object undefined or null properties
9604 forEach(search, function(value, key) {
9605 if (value == null) delete search[key];
9608 this.$$search = search;
9610 throw $locationMinErr('isrcharg',
9611 'The first argument of the `$location#search()` call must be a string or an object.');
9615 if (isUndefined(paramValue) || paramValue === null) {
9616 delete this.$$search[search];
9618 this.$$search[search] = paramValue;
9628 * @name $location#hash
9631 * This method is getter / setter.
9633 * Return hash fragment when called without any parameter.
9635 * Change hash fragment when called with parameter and return `$location`.
9637 * @param {(string|number)=} hash New hash fragment
9638 * @return {string} hash
9640 hash: locationGetterSetter('$$hash', function(hash) {
9641 return hash ? hash.toString() : '';
9646 * @name $location#replace
9649 * If called, all changes to $location during current `$digest` will be replacing current history
9650 * record, instead of adding new one.
9652 replace: function() {
9653 this.$$replace = true;
9658 function locationGetter(property) {
9660 return this[property];
9665 function locationGetterSetter(property, preprocess) {
9666 return function(value) {
9667 if (isUndefined(value))
9668 return this[property];
9670 this[property] = preprocess(value);
9682 * @requires $rootElement
9685 * The $location service parses the URL in the browser address bar (based on the
9686 * [window.location](https://developer.mozilla.org/en/window.location)) and makes the URL
9687 * available to your application. Changes to the URL in the address bar are reflected into
9688 * $location service and changes to $location are reflected into the browser address bar.
9690 * **The $location service:**
9692 * - Exposes the current URL in the browser address bar, so you can
9693 * - Watch and observe the URL.
9695 * - Synchronizes the URL with the browser when the user
9696 * - Changes the address bar.
9697 * - Clicks the back or forward button (or clicks a History link).
9698 * - Clicks on a link.
9699 * - Represents the URL object as a set of methods (protocol, host, port, path, search, hash).
9701 * For more information see {@link guide/$location Developer Guide: Using $location}
9706 * @name $locationProvider
9708 * Use the `$locationProvider` to configure how the application deep linking paths are stored.
9710 function $LocationProvider(){
9711 var hashPrefix = '',
9716 * @name $locationProvider#hashPrefix
9718 * @param {string=} prefix Prefix for hash part (containing path and search)
9719 * @returns {*} current value if used as getter or itself (chaining) if used as setter
9721 this.hashPrefix = function(prefix) {
9722 if (isDefined(prefix)) {
9723 hashPrefix = prefix;
9732 * @name $locationProvider#html5Mode
9734 * @param {boolean=} mode Use HTML5 strategy if available.
9735 * @returns {*} current value if used as getter or itself (chaining) if used as setter
9737 this.html5Mode = function(mode) {
9738 if (isDefined(mode)) {
9748 * @name $location#$locationChangeStart
9749 * @eventType broadcast on root scope
9751 * Broadcasted before a URL will change. This change can be prevented by calling
9752 * `preventDefault` method of the event. See {@link ng.$rootScope.Scope#$on} for more
9753 * details about event object. Upon successful change
9754 * {@link ng.$location#events_$locationChangeSuccess $locationChangeSuccess} is fired.
9756 * @param {Object} angularEvent Synthetic event object.
9757 * @param {string} newUrl New URL
9758 * @param {string=} oldUrl URL that was before it was changed.
9763 * @name $location#$locationChangeSuccess
9764 * @eventType broadcast on root scope
9766 * Broadcasted after a URL was changed.
9768 * @param {Object} angularEvent Synthetic event object.
9769 * @param {string} newUrl New URL
9770 * @param {string=} oldUrl URL that was before it was changed.
9773 this.$get = ['$rootScope', '$browser', '$sniffer', '$rootElement',
9774 function( $rootScope, $browser, $sniffer, $rootElement) {
9777 baseHref = $browser.baseHref(), // if base[href] is undefined, it defaults to ''
9778 initialUrl = $browser.url(),
9782 appBase = serverBase(initialUrl) + (baseHref || '/');
9783 LocationMode = $sniffer.history ? LocationHtml5Url : LocationHashbangInHtml5Url;
9785 appBase = stripHash(initialUrl);
9786 LocationMode = LocationHashbangUrl;
9788 $location = new LocationMode(appBase, '#' + hashPrefix);
9789 $location.$$parse($location.$$rewrite(initialUrl));
9791 var IGNORE_URI_REGEXP = /^\s*(javascript|mailto):/i;
9793 $rootElement.on('click', function(event) {
9794 // TODO(vojta): rewrite link when opening in new tab/window (in legacy browser)
9795 // currently we open nice url link and redirect then
9797 if (event.ctrlKey || event.metaKey || event.which == 2) return;
9799 var elm = jqLite(event.target);
9801 // traverse the DOM up to find first A tag
9802 while (lowercase(elm[0].nodeName) !== 'a') {
9803 // ignore rewriting if no A tag (reached root element, or no parent - removed from document)
9804 if (elm[0] === $rootElement[0] || !(elm = elm.parent())[0]) return;
9807 var absHref = elm.prop('href');
9809 if (isObject(absHref) && absHref.toString() === '[object SVGAnimatedString]') {
9810 // SVGAnimatedString.animVal should be identical to SVGAnimatedString.baseVal, unless during
9812 absHref = urlResolve(absHref.animVal).href;
9815 // Ignore when url is started with javascript: or mailto:
9816 if (IGNORE_URI_REGEXP.test(absHref)) return;
9818 // Make relative links work in HTML5 mode for legacy browsers (or at least IE8 & 9)
9819 // The href should be a regular url e.g. /link/somewhere or link/somewhere or ../somewhere or
9820 // somewhere#anchor or http://example.com/somewhere
9821 if (LocationMode === LocationHashbangInHtml5Url) {
9822 // get the actual href attribute - see
9823 // http://msdn.microsoft.com/en-us/library/ie/dd347148(v=vs.85).aspx
9824 var href = elm.attr('href') || elm.attr('xlink:href');
9826 if (href && href.indexOf('://') < 0) { // Ignore absolute URLs
9827 var prefix = '#' + hashPrefix;
9828 if (href[0] == '/') {
9829 // absolute path - replace old path
9830 absHref = appBase + prefix + href;
9831 } else if (href[0] == '#') {
9833 absHref = appBase + prefix + ($location.path() || '/') + href;
9835 // relative path - join with current path
9836 var stack = $location.path().split("/"),
9837 parts = href.split("/");
9838 if (stack.length === 2 && !stack[1]) stack.length = 1;
9839 for (var i=0; i<parts.length; i++) {
9840 if (parts[i] == ".")
9842 else if (parts[i] == "..")
9844 else if (parts[i].length)
9845 stack.push(parts[i]);
9847 absHref = appBase + prefix + stack.join('/');
9852 var rewrittenUrl = $location.$$rewrite(absHref);
9854 if (absHref && !elm.attr('target') && rewrittenUrl && !event.isDefaultPrevented()) {
9855 event.preventDefault();
9856 if (rewrittenUrl != $browser.url()) {
9857 // update location manually
9858 $location.$$parse(rewrittenUrl);
9859 $rootScope.$apply();
9860 // hack to work around FF6 bug 684208 when scenario runner clicks on links
9861 window.angular['ff-684208-preventDefault'] = true;
9867 // rewrite hashbang url <> html5 url
9868 if ($location.absUrl() != initialUrl) {
9869 $browser.url($location.absUrl(), true);
9872 // update $location when $browser url changes
9873 $browser.onUrlChange(function(newUrl) {
9874 if ($location.absUrl() != newUrl) {
9875 $rootScope.$evalAsync(function() {
9876 var oldUrl = $location.absUrl();
9878 $location.$$parse(newUrl);
9879 if ($rootScope.$broadcast('$locationChangeStart', newUrl,
9880 oldUrl).defaultPrevented) {
9881 $location.$$parse(oldUrl);
9882 $browser.url(oldUrl);
9884 afterLocationChange(oldUrl);
9887 if (!$rootScope.$$phase) $rootScope.$digest();
9892 var changeCounter = 0;
9893 $rootScope.$watch(function $locationWatch() {
9894 var oldUrl = $browser.url();
9895 var currentReplace = $location.$$replace;
9897 if (!changeCounter || oldUrl != $location.absUrl()) {
9899 $rootScope.$evalAsync(function() {
9900 if ($rootScope.$broadcast('$locationChangeStart', $location.absUrl(), oldUrl).
9902 $location.$$parse(oldUrl);
9904 $browser.url($location.absUrl(), currentReplace);
9905 afterLocationChange(oldUrl);
9909 $location.$$replace = false;
9911 return changeCounter;
9916 function afterLocationChange(oldUrl) {
9917 $rootScope.$broadcast('$locationChangeSuccess', $location.absUrl(), oldUrl);
9928 * Simple service for logging. Default implementation safely writes the message
9929 * into the browser's console (if present).
9931 * The main purpose of this service is to simplify debugging and troubleshooting.
9933 * The default is to log `debug` messages. You can use
9934 * {@link ng.$logProvider ng.$logProvider#debugEnabled} to change this.
9937 <example module="logExample">
9938 <file name="script.js">
9939 angular.module('logExample', [])
9940 .controller('LogController', ['$scope', '$log', function($scope, $log) {
9942 $scope.message = 'Hello World!';
9945 <file name="index.html">
9946 <div ng-controller="LogController">
9947 <p>Reload this page with open console, enter text and hit the log button...</p>
9949 <input type="text" ng-model="message"/>
9950 <button ng-click="$log.log(message)">log</button>
9951 <button ng-click="$log.warn(message)">warn</button>
9952 <button ng-click="$log.info(message)">info</button>
9953 <button ng-click="$log.error(message)">error</button>
9961 * @name $logProvider
9963 * Use the `$logProvider` to configure how the application logs messages
9965 function $LogProvider(){
9971 * @name $logProvider#debugEnabled
9973 * @param {boolean=} flag enable or disable debug level messages
9974 * @returns {*} current value if used as getter or itself (chaining) if used as setter
9976 this.debugEnabled = function(flag) {
9977 if (isDefined(flag)) {
9985 this.$get = ['$window', function($window){
9992 * Write a log message
9994 log: consoleLog('log'),
10001 * Write an information message
10003 info: consoleLog('info'),
10010 * Write a warning message
10012 warn: consoleLog('warn'),
10019 * Write an error message
10021 error: consoleLog('error'),
10028 * Write a debug message
10030 debug: (function () {
10031 var fn = consoleLog('debug');
10033 return function() {
10035 fn.apply(self, arguments);
10041 function formatError(arg) {
10042 if (arg instanceof Error) {
10044 arg = (arg.message && arg.stack.indexOf(arg.message) === -1)
10045 ? 'Error: ' + arg.message + '\n' + arg.stack
10047 } else if (arg.sourceURL) {
10048 arg = arg.message + '\n' + arg.sourceURL + ':' + arg.line;
10054 function consoleLog(type) {
10055 var console = $window.console || {},
10056 logFn = console[type] || console.log || noop,
10059 // Note: reading logFn.apply throws an error in IE11 in IE8 document mode.
10060 // The reason behind this is that console.log has type "object" in IE8...
10062 hasApply = !!logFn.apply;
10066 return function() {
10068 forEach(arguments, function(arg) {
10069 args.push(formatError(arg));
10071 return logFn.apply(console, args);
10075 // we are IE which either doesn't have window.console => this is noop and we do nothing,
10076 // or we are IE where console.log doesn't have apply so we log at least first 2 args
10077 return function(arg1, arg2) {
10078 logFn(arg1, arg2 == null ? '' : arg2);
10084 var $parseMinErr = minErr('$parse');
10085 var promiseWarningCache = {};
10086 var promiseWarning;
10088 // Sandboxing Angular Expressions
10089 // ------------------------------
10090 // Angular expressions are generally considered safe because these expressions only have direct
10091 // access to $scope and locals. However, one can obtain the ability to execute arbitrary JS code by
10092 // obtaining a reference to native JS functions such as the Function constructor.
10094 // As an example, consider the following Angular expression:
10096 // {}.toString.constructor('alert("evil JS code")')
10098 // This sandboxing technique is not perfect and doesn't aim to be. The goal is to prevent exploits
10099 // against the expression language, but not to prevent exploits that were enabled by exposing
10100 // sensitive JavaScript or browser apis on Scope. Exposing such objects on a Scope is never a good
10101 // practice and therefore we are not even trying to protect against interaction with an object
10102 // explicitly exposed in this way.
10104 // In general, it is not possible to access a Window object from an angular expression unless a
10105 // window or some DOM object that has a reference to window is published onto a Scope.
10106 // Similarly we prevent invocations of function known to be dangerous, as well as assignments to
10110 function ensureSafeMemberName(name, fullExpression) {
10111 if (name === "__defineGetter__" || name === "__defineSetter__"
10112 || name === "__lookupGetter__" || name === "__lookupSetter__"
10113 || name === "__proto__") {
10114 throw $parseMinErr('isecfld',
10115 'Attempting to access a disallowed field in Angular expressions! '
10116 +'Expression: {0}', fullExpression);
10121 function ensureSafeObject(obj, fullExpression) {
10122 // nifty check if obj is Function that is fast and works across iframes and other contexts
10124 if (obj.constructor === obj) {
10125 throw $parseMinErr('isecfn',
10126 'Referencing Function in Angular expressions is disallowed! Expression: {0}',
10128 } else if (// isWindow(obj)
10129 obj.document && obj.location && obj.alert && obj.setInterval) {
10130 throw $parseMinErr('isecwindow',
10131 'Referencing the Window in Angular expressions is disallowed! Expression: {0}',
10133 } else if (// isElement(obj)
10134 obj.children && (obj.nodeName || (obj.prop && obj.attr && obj.find))) {
10135 throw $parseMinErr('isecdom',
10136 'Referencing DOM nodes in Angular expressions is disallowed! Expression: {0}',
10138 } else if (// block Object so that we can't get hold of dangerous Object.* methods
10140 throw $parseMinErr('isecobj',
10141 'Referencing Object in Angular expressions is disallowed! Expression: {0}',
10148 var CALL = Function.prototype.call;
10149 var APPLY = Function.prototype.apply;
10150 var BIND = Function.prototype.bind;
10152 function ensureSafeFunction(obj, fullExpression) {
10154 if (obj.constructor === obj) {
10155 throw $parseMinErr('isecfn',
10156 'Referencing Function in Angular expressions is disallowed! Expression: {0}',
10158 } else if (obj === CALL || obj === APPLY || (BIND && obj === BIND)) {
10159 throw $parseMinErr('isecff',
10160 'Referencing call, apply or bind in Angular expressions is disallowed! Expression: {0}',
10167 /* jshint bitwise : false */
10168 'null':function(){return null;},
10169 'true':function(){return true;},
10170 'false':function(){return false;},
10172 '+':function(self, locals, a,b){
10173 a=a(self, locals); b=b(self, locals);
10174 if (isDefined(a)) {
10175 if (isDefined(b)) {
10180 return isDefined(b)?b:undefined;},
10181 '-':function(self, locals, a,b){
10182 a=a(self, locals); b=b(self, locals);
10183 return (isDefined(a)?a:0)-(isDefined(b)?b:0);
10185 '*':function(self, locals, a,b){return a(self, locals)*b(self, locals);},
10186 '/':function(self, locals, a,b){return a(self, locals)/b(self, locals);},
10187 '%':function(self, locals, a,b){return a(self, locals)%b(self, locals);},
10188 '^':function(self, locals, a,b){return a(self, locals)^b(self, locals);},
10190 '===':function(self, locals, a, b){return a(self, locals)===b(self, locals);},
10191 '!==':function(self, locals, a, b){return a(self, locals)!==b(self, locals);},
10192 '==':function(self, locals, a,b){return a(self, locals)==b(self, locals);},
10193 '!=':function(self, locals, a,b){return a(self, locals)!=b(self, locals);},
10194 '<':function(self, locals, a,b){return a(self, locals)<b(self, locals);},
10195 '>':function(self, locals, a,b){return a(self, locals)>b(self, locals);},
10196 '<=':function(self, locals, a,b){return a(self, locals)<=b(self, locals);},
10197 '>=':function(self, locals, a,b){return a(self, locals)>=b(self, locals);},
10198 '&&':function(self, locals, a,b){return a(self, locals)&&b(self, locals);},
10199 '||':function(self, locals, a,b){return a(self, locals)||b(self, locals);},
10200 '&':function(self, locals, a,b){return a(self, locals)&b(self, locals);},
10201 // '|':function(self, locals, a,b){return a|b;},
10202 '|':function(self, locals, a,b){return b(self, locals)(self, locals, a(self, locals));},
10203 '!':function(self, locals, a){return !a(self, locals);}
10205 /* jshint bitwise: true */
10206 var ESCAPE = {"n":"\n", "f":"\f", "r":"\r", "t":"\t", "v":"\v", "'":"'", '"':'"'};
10209 /////////////////////////////////////////
10215 var Lexer = function (options) {
10216 this.options = options;
10219 Lexer.prototype = {
10220 constructor: Lexer,
10222 lex: function (text) {
10226 this.ch = undefined;
10227 this.lastCh = ':'; // can start regexp
10231 while (this.index < this.text.length) {
10232 this.ch = this.text.charAt(this.index);
10233 if (this.is('"\'')) {
10234 this.readString(this.ch);
10235 } else if (this.isNumber(this.ch) || this.is('.') && this.isNumber(this.peek())) {
10237 } else if (this.isIdent(this.ch)) {
10239 } else if (this.is('(){}[].,;:?')) {
10245 } else if (this.isWhitespace(this.ch)) {
10249 var ch2 = this.ch + this.peek();
10250 var ch3 = ch2 + this.peek(2);
10251 var fn = OPERATORS[this.ch];
10252 var fn2 = OPERATORS[ch2];
10253 var fn3 = OPERATORS[ch3];
10255 this.tokens.push({index: this.index, text: ch3, fn: fn3});
10258 this.tokens.push({index: this.index, text: ch2, fn: fn2});
10268 this.throwError('Unexpected next character ', this.index, this.index + 1);
10271 this.lastCh = this.ch;
10273 return this.tokens;
10276 is: function(chars) {
10277 return chars.indexOf(this.ch) !== -1;
10280 was: function(chars) {
10281 return chars.indexOf(this.lastCh) !== -1;
10284 peek: function(i) {
10286 return (this.index + num < this.text.length) ? this.text.charAt(this.index + num) : false;
10289 isNumber: function(ch) {
10290 return ('0' <= ch && ch <= '9');
10293 isWhitespace: function(ch) {
10294 // IE treats non-breaking space as \u00A0
10295 return (ch === ' ' || ch === '\r' || ch === '\t' ||
10296 ch === '\n' || ch === '\v' || ch === '\u00A0');
10299 isIdent: function(ch) {
10300 return ('a' <= ch && ch <= 'z' ||
10301 'A' <= ch && ch <= 'Z' ||
10302 '_' === ch || ch === '$');
10305 isExpOperator: function(ch) {
10306 return (ch === '-' || ch === '+' || this.isNumber(ch));
10309 throwError: function(error, start, end) {
10310 end = end || this.index;
10311 var colStr = (isDefined(start)
10312 ? 's ' + start + '-' + this.index + ' [' + this.text.substring(start, end) + ']'
10314 throw $parseMinErr('lexerr', 'Lexer Error: {0} at column{1} in expression [{2}].',
10315 error, colStr, this.text);
10318 readNumber: function() {
10320 var start = this.index;
10321 while (this.index < this.text.length) {
10322 var ch = lowercase(this.text.charAt(this.index));
10323 if (ch == '.' || this.isNumber(ch)) {
10326 var peekCh = this.peek();
10327 if (ch == 'e' && this.isExpOperator(peekCh)) {
10329 } else if (this.isExpOperator(ch) &&
10330 peekCh && this.isNumber(peekCh) &&
10331 number.charAt(number.length - 1) == 'e') {
10333 } else if (this.isExpOperator(ch) &&
10334 (!peekCh || !this.isNumber(peekCh)) &&
10335 number.charAt(number.length - 1) == 'e') {
10336 this.throwError('Invalid exponent');
10343 number = 1 * number;
10349 fn: function() { return number; }
10353 readIdent: function() {
10357 var start = this.index;
10359 var lastDot, peekIndex, methodName, ch;
10361 while (this.index < this.text.length) {
10362 ch = this.text.charAt(this.index);
10363 if (ch === '.' || this.isIdent(ch) || this.isNumber(ch)) {
10364 if (ch === '.') lastDot = this.index;
10372 //check if this is not a method invocation and if it is back out to last dot
10374 peekIndex = this.index;
10375 while (peekIndex < this.text.length) {
10376 ch = this.text.charAt(peekIndex);
10378 methodName = ident.substr(lastDot - start + 1);
10379 ident = ident.substr(0, lastDot - start);
10380 this.index = peekIndex;
10383 if (this.isWhitespace(ch)) {
10397 // OPERATORS is our own object so we don't need to use special hasOwnPropertyFn
10398 if (OPERATORS.hasOwnProperty(ident)) {
10399 token.fn = OPERATORS[ident];
10400 token.literal = true;
10401 token.constant = true;
10403 var getter = getterFn(ident, this.options, this.text);
10404 token.fn = extend(function(self, locals) {
10405 return (getter(self, locals));
10407 assign: function(self, value) {
10408 return setter(self, ident, value, parser.text, parser.options);
10413 this.tokens.push(token);
10421 index: lastDot + 1,
10427 readString: function(quote) {
10428 var start = this.index;
10431 var rawString = quote;
10432 var escape = false;
10433 while (this.index < this.text.length) {
10434 var ch = this.text.charAt(this.index);
10438 var hex = this.text.substring(this.index + 1, this.index + 5);
10439 if (!hex.match(/[\da-f]{4}/i))
10440 this.throwError('Invalid unicode escape [\\u' + hex + ']');
10442 string += String.fromCharCode(parseInt(hex, 16));
10444 var rep = ESCAPE[ch];
10445 string = string + (rep || ch);
10448 } else if (ch === '\\') {
10450 } else if (ch === quote) {
10458 fn: function() { return string; }
10466 this.throwError('Unterminated quote', start);
10474 var Parser = function (lexer, $filter, options) {
10475 this.lexer = lexer;
10476 this.$filter = $filter;
10477 this.options = options;
10480 Parser.ZERO = extend(function () {
10486 Parser.prototype = {
10487 constructor: Parser,
10489 parse: function (text) {
10492 this.tokens = this.lexer.lex(text);
10494 var value = this.statements();
10496 if (this.tokens.length !== 0) {
10497 this.throwError('is an unexpected token', this.tokens[0]);
10500 value.literal = !!value.literal;
10501 value.constant = !!value.constant;
10506 primary: function () {
10508 if (this.expect('(')) {
10509 primary = this.filterChain();
10511 } else if (this.expect('[')) {
10512 primary = this.arrayDeclaration();
10513 } else if (this.expect('{')) {
10514 primary = this.object();
10516 var token = this.expect();
10517 primary = token.fn;
10519 this.throwError('not a primary expression', token);
10521 primary.literal = !!token.literal;
10522 primary.constant = !!token.constant;
10526 while ((next = this.expect('(', '[', '.'))) {
10527 if (next.text === '(') {
10528 primary = this.functionCall(primary, context);
10530 } else if (next.text === '[') {
10532 primary = this.objectIndex(primary);
10533 } else if (next.text === '.') {
10535 primary = this.fieldAccess(primary);
10537 this.throwError('IMPOSSIBLE');
10543 throwError: function(msg, token) {
10544 throw $parseMinErr('syntax',
10545 'Syntax Error: Token \'{0}\' {1} at column {2} of the expression [{3}] starting at [{4}].',
10546 token.text, msg, (token.index + 1), this.text, this.text.substring(token.index));
10549 peekToken: function() {
10550 if (this.tokens.length === 0)
10551 throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text);
10552 return this.tokens[0];
10555 peek: function(e1, e2, e3, e4) {
10556 if (this.tokens.length > 0) {
10557 var token = this.tokens[0];
10558 var t = token.text;
10559 if (t === e1 || t === e2 || t === e3 || t === e4 ||
10560 (!e1 && !e2 && !e3 && !e4)) {
10567 expect: function(e1, e2, e3, e4){
10568 var token = this.peek(e1, e2, e3, e4);
10570 this.tokens.shift();
10576 consume: function(e1){
10577 if (!this.expect(e1)) {
10578 this.throwError('is unexpected, expecting [' + e1 + ']', this.peek());
10582 unaryFn: function(fn, right) {
10583 return extend(function(self, locals) {
10584 return fn(self, locals, right);
10586 constant:right.constant
10590 ternaryFn: function(left, middle, right){
10591 return extend(function(self, locals){
10592 return left(self, locals) ? middle(self, locals) : right(self, locals);
10594 constant: left.constant && middle.constant && right.constant
10598 binaryFn: function(left, fn, right) {
10599 return extend(function(self, locals) {
10600 return fn(self, locals, left, right);
10602 constant:left.constant && right.constant
10606 statements: function() {
10607 var statements = [];
10609 if (this.tokens.length > 0 && !this.peek('}', ')', ';', ']'))
10610 statements.push(this.filterChain());
10611 if (!this.expect(';')) {
10612 // optimize for the common case where there is only one statement.
10613 // TODO(size): maybe we should not support multiple statements?
10614 return (statements.length === 1)
10616 : function(self, locals) {
10618 for (var i = 0; i < statements.length; i++) {
10619 var statement = statements[i];
10621 value = statement(self, locals);
10630 filterChain: function() {
10631 var left = this.expression();
10634 if ((token = this.expect('|'))) {
10635 left = this.binaryFn(left, token.fn, this.filter());
10642 filter: function() {
10643 var token = this.expect();
10644 var fn = this.$filter(token.text);
10647 if ((token = this.expect(':'))) {
10648 argsFn.push(this.expression());
10650 var fnInvoke = function(self, locals, input) {
10651 var args = [input];
10652 for (var i = 0; i < argsFn.length; i++) {
10653 args.push(argsFn[i](self, locals));
10655 return fn.apply(self, args);
10657 return function() {
10664 expression: function() {
10665 return this.assignment();
10668 assignment: function() {
10669 var left = this.ternary();
10672 if ((token = this.expect('='))) {
10673 if (!left.assign) {
10674 this.throwError('implies assignment but [' +
10675 this.text.substring(0, token.index) + '] can not be assigned to', token);
10677 right = this.ternary();
10678 return function(scope, locals) {
10679 return left.assign(scope, right(scope, locals), locals);
10685 ternary: function() {
10686 var left = this.logicalOR();
10689 if ((token = this.expect('?'))) {
10690 middle = this.assignment();
10691 if ((token = this.expect(':'))) {
10692 return this.ternaryFn(left, middle, this.assignment());
10694 this.throwError('expected :', token);
10701 logicalOR: function() {
10702 var left = this.logicalAND();
10705 if ((token = this.expect('||'))) {
10706 left = this.binaryFn(left, token.fn, this.logicalAND());
10713 logicalAND: function() {
10714 var left = this.equality();
10716 if ((token = this.expect('&&'))) {
10717 left = this.binaryFn(left, token.fn, this.logicalAND());
10722 equality: function() {
10723 var left = this.relational();
10725 if ((token = this.expect('==','!=','===','!=='))) {
10726 left = this.binaryFn(left, token.fn, this.equality());
10731 relational: function() {
10732 var left = this.additive();
10734 if ((token = this.expect('<', '>', '<=', '>='))) {
10735 left = this.binaryFn(left, token.fn, this.relational());
10740 additive: function() {
10741 var left = this.multiplicative();
10743 while ((token = this.expect('+','-'))) {
10744 left = this.binaryFn(left, token.fn, this.multiplicative());
10749 multiplicative: function() {
10750 var left = this.unary();
10752 while ((token = this.expect('*','/','%'))) {
10753 left = this.binaryFn(left, token.fn, this.unary());
10758 unary: function() {
10760 if (this.expect('+')) {
10761 return this.primary();
10762 } else if ((token = this.expect('-'))) {
10763 return this.binaryFn(Parser.ZERO, token.fn, this.unary());
10764 } else if ((token = this.expect('!'))) {
10765 return this.unaryFn(token.fn, this.unary());
10767 return this.primary();
10771 fieldAccess: function(object) {
10773 var field = this.expect().text;
10774 var getter = getterFn(field, this.options, this.text);
10776 return extend(function(scope, locals, self) {
10777 return getter(self || object(scope, locals));
10779 assign: function(scope, value, locals) {
10780 var o = object(scope, locals);
10781 if (!o) object.assign(scope, o = {});
10782 return setter(o, field, value, parser.text, parser.options);
10787 objectIndex: function(obj) {
10790 var indexFn = this.expression();
10793 return extend(function(self, locals) {
10794 var o = obj(self, locals),
10795 i = indexFn(self, locals),
10798 ensureSafeMemberName(i, parser.text);
10799 if (!o) return undefined;
10800 v = ensureSafeObject(o[i], parser.text);
10801 if (v && v.then && parser.options.unwrapPromises) {
10803 if (!('$$v' in v)) {
10805 p.then(function(val) { p.$$v = val; });
10811 assign: function(self, value, locals) {
10812 var key = ensureSafeMemberName(indexFn(self, locals), parser.text);
10813 // prevent overwriting of Function.constructor which would break ensureSafeObject check
10814 var o = ensureSafeObject(obj(self, locals), parser.text);
10815 if (!o) obj.assign(self, o = {});
10816 return o[key] = value;
10821 functionCall: function(fn, contextGetter) {
10823 if (this.peekToken().text !== ')') {
10825 argsFn.push(this.expression());
10826 } while (this.expect(','));
10832 return function(scope, locals) {
10834 var context = contextGetter ? contextGetter(scope, locals) : scope;
10836 for (var i = 0; i < argsFn.length; i++) {
10837 args.push(ensureSafeObject(argsFn[i](scope, locals), parser.text));
10839 var fnPtr = fn(scope, locals, context) || noop;
10841 ensureSafeObject(context, parser.text);
10842 ensureSafeFunction(fnPtr, parser.text);
10844 // IE stupidity! (IE doesn't have apply for some native functions)
10845 var v = fnPtr.apply
10846 ? fnPtr.apply(context, args)
10847 : fnPtr(args[0], args[1], args[2], args[3], args[4]);
10849 return ensureSafeObject(v, parser.text);
10853 // This is used with json array declaration
10854 arrayDeclaration: function () {
10855 var elementFns = [];
10856 var allConstant = true;
10857 if (this.peekToken().text !== ']') {
10859 if (this.peek(']')) {
10860 // Support trailing commas per ES5.1.
10863 var elementFn = this.expression();
10864 elementFns.push(elementFn);
10865 if (!elementFn.constant) {
10866 allConstant = false;
10868 } while (this.expect(','));
10872 return extend(function(self, locals) {
10874 for (var i = 0; i < elementFns.length; i++) {
10875 array.push(elementFns[i](self, locals));
10880 constant: allConstant
10884 object: function () {
10885 var keyValues = [];
10886 var allConstant = true;
10887 if (this.peekToken().text !== '}') {
10889 if (this.peek('}')) {
10890 // Support trailing commas per ES5.1.
10893 var token = this.expect(),
10894 key = token.string || token.text;
10896 var value = this.expression();
10897 keyValues.push({key: key, value: value});
10898 if (!value.constant) {
10899 allConstant = false;
10901 } while (this.expect(','));
10905 return extend(function(self, locals) {
10907 for (var i = 0; i < keyValues.length; i++) {
10908 var keyValue = keyValues[i];
10909 object[keyValue.key] = keyValue.value(self, locals);
10914 constant: allConstant
10920 //////////////////////////////////////////////////
10921 // Parser helper functions
10922 //////////////////////////////////////////////////
10924 function setter(obj, path, setValue, fullExp, options) {
10925 ensureSafeObject(obj, fullExp);
10928 options = options || {};
10930 var element = path.split('.'), key;
10931 for (var i = 0; element.length > 1; i++) {
10932 key = ensureSafeMemberName(element.shift(), fullExp);
10933 var propertyObj = ensureSafeObject(obj[key], fullExp);
10934 if (!propertyObj) {
10936 obj[key] = propertyObj;
10939 if (obj.then && options.unwrapPromises) {
10940 promiseWarning(fullExp);
10941 if (!("$$v" in obj)) {
10942 (function(promise) {
10943 promise.then(function(val) { promise.$$v = val; }); }
10946 if (obj.$$v === undefined) {
10952 key = ensureSafeMemberName(element.shift(), fullExp);
10953 ensureSafeObject(obj[key], fullExp);
10954 obj[key] = setValue;
10958 var getterFnCache = {};
10961 * Implementation of the "Black Hole" variant from:
10962 * - http://jsperf.com/angularjs-parse-getter/4
10963 * - http://jsperf.com/path-evaluation-simplified/7
10965 function cspSafeGetterFn(key0, key1, key2, key3, key4, fullExp, options) {
10966 ensureSafeMemberName(key0, fullExp);
10967 ensureSafeMemberName(key1, fullExp);
10968 ensureSafeMemberName(key2, fullExp);
10969 ensureSafeMemberName(key3, fullExp);
10970 ensureSafeMemberName(key4, fullExp);
10972 return !options.unwrapPromises
10973 ? function cspSafeGetter(scope, locals) {
10974 var pathVal = (locals && locals.hasOwnProperty(key0)) ? locals : scope;
10976 if (pathVal == null) return pathVal;
10977 pathVal = pathVal[key0];
10979 if (!key1) return pathVal;
10980 if (pathVal == null) return undefined;
10981 pathVal = pathVal[key1];
10983 if (!key2) return pathVal;
10984 if (pathVal == null) return undefined;
10985 pathVal = pathVal[key2];
10987 if (!key3) return pathVal;
10988 if (pathVal == null) return undefined;
10989 pathVal = pathVal[key3];
10991 if (!key4) return pathVal;
10992 if (pathVal == null) return undefined;
10993 pathVal = pathVal[key4];
10997 : function cspSafePromiseEnabledGetter(scope, locals) {
10998 var pathVal = (locals && locals.hasOwnProperty(key0)) ? locals : scope,
11001 if (pathVal == null) return pathVal;
11003 pathVal = pathVal[key0];
11004 if (pathVal && pathVal.then) {
11005 promiseWarning(fullExp);
11006 if (!("$$v" in pathVal)) {
11008 promise.$$v = undefined;
11009 promise.then(function(val) { promise.$$v = val; });
11011 pathVal = pathVal.$$v;
11014 if (!key1) return pathVal;
11015 if (pathVal == null) return undefined;
11016 pathVal = pathVal[key1];
11017 if (pathVal && pathVal.then) {
11018 promiseWarning(fullExp);
11019 if (!("$$v" in pathVal)) {
11021 promise.$$v = undefined;
11022 promise.then(function(val) { promise.$$v = val; });
11024 pathVal = pathVal.$$v;
11027 if (!key2) return pathVal;
11028 if (pathVal == null) return undefined;
11029 pathVal = pathVal[key2];
11030 if (pathVal && pathVal.then) {
11031 promiseWarning(fullExp);
11032 if (!("$$v" in pathVal)) {
11034 promise.$$v = undefined;
11035 promise.then(function(val) { promise.$$v = val; });
11037 pathVal = pathVal.$$v;
11040 if (!key3) return pathVal;
11041 if (pathVal == null) return undefined;
11042 pathVal = pathVal[key3];
11043 if (pathVal && pathVal.then) {
11044 promiseWarning(fullExp);
11045 if (!("$$v" in pathVal)) {
11047 promise.$$v = undefined;
11048 promise.then(function(val) { promise.$$v = val; });
11050 pathVal = pathVal.$$v;
11053 if (!key4) return pathVal;
11054 if (pathVal == null) return undefined;
11055 pathVal = pathVal[key4];
11056 if (pathVal && pathVal.then) {
11057 promiseWarning(fullExp);
11058 if (!("$$v" in pathVal)) {
11060 promise.$$v = undefined;
11061 promise.then(function(val) { promise.$$v = val; });
11063 pathVal = pathVal.$$v;
11069 function getterFn(path, options, fullExp) {
11070 // Check whether the cache has this getter already.
11071 // We can use hasOwnProperty directly on the cache because we ensure,
11072 // see below, that the cache never stores a path called 'hasOwnProperty'
11073 if (getterFnCache.hasOwnProperty(path)) {
11074 return getterFnCache[path];
11077 var pathKeys = path.split('.'),
11078 pathKeysLength = pathKeys.length,
11081 // http://jsperf.com/angularjs-parse-getter/6
11083 if (pathKeysLength < 6) {
11084 fn = cspSafeGetterFn(pathKeys[0], pathKeys[1], pathKeys[2], pathKeys[3], pathKeys[4], fullExp,
11087 fn = function(scope, locals) {
11090 val = cspSafeGetterFn(pathKeys[i++], pathKeys[i++], pathKeys[i++], pathKeys[i++],
11091 pathKeys[i++], fullExp, options)(scope, locals);
11093 locals = undefined; // clear after first iteration
11095 } while (i < pathKeysLength);
11100 var code = 'var p;\n';
11101 forEach(pathKeys, function(key, index) {
11102 ensureSafeMemberName(key, fullExp);
11103 code += 'if(s == null) return undefined;\n' +
11105 // we simply dereference 's' on any .dot notation
11107 // but if we are first then we check locals first, and if so read it first
11108 : '((k&&k.hasOwnProperty("' + key + '"))?k:s)') + '["' + key + '"]' + ';\n' +
11109 (options.unwrapPromises
11110 ? 'if (s && s.then) {\n' +
11111 ' pw("' + fullExp.replace(/(["\r\n])/g, '\\$1') + '");\n' +
11112 ' if (!("$$v" in s)) {\n' +
11114 ' p.$$v = undefined;\n' +
11115 ' p.then(function(v) {p.$$v=v;});\n' +
11121 code += 'return s;';
11124 var evaledFnGetter = new Function('s', 'k', 'pw', code); // s=scope, k=locals, pw=promiseWarning
11126 evaledFnGetter.toString = valueFn(code);
11127 fn = options.unwrapPromises ? function(scope, locals) {
11128 return evaledFnGetter(scope, locals, promiseWarning);
11129 } : evaledFnGetter;
11132 // Only cache the value if it's not going to mess up the cache object
11133 // This is more performant that using Object.prototype.hasOwnProperty.call
11134 if (path !== 'hasOwnProperty') {
11135 getterFnCache[path] = fn;
11140 ///////////////////////////////////
11149 * Converts Angular {@link guide/expression expression} into a function.
11152 * var getter = $parse('user.name');
11153 * var setter = getter.assign;
11154 * var context = {user:{name:'angular'}};
11155 * var locals = {user:{name:'local'}};
11157 * expect(getter(context)).toEqual('angular');
11158 * setter(context, 'newValue');
11159 * expect(context.user.name).toEqual('newValue');
11160 * expect(getter(context, locals)).toEqual('local');
11164 * @param {string} expression String expression to compile.
11165 * @returns {function(context, locals)} a function which represents the compiled expression:
11167 * * `context` – `{object}` – an object against which any expressions embedded in the strings
11168 * are evaluated against (typically a scope object).
11169 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
11172 * The returned function also has the following properties:
11173 * * `literal` – `{boolean}` – whether the expression's top-level node is a JavaScript
11175 * * `constant` – `{boolean}` – whether the expression is made entirely of JavaScript
11176 * constant literals.
11177 * * `assign` – `{?function(context, value)}` – if the expression is assignable, this will be
11178 * set to a function to change its value on the given context.
11185 * @name $parseProvider
11189 * `$parseProvider` can be used for configuring the default behavior of the {@link ng.$parse $parse}
11192 function $ParseProvider() {
11195 var $parseOptions = {
11197 unwrapPromises: false,
11198 logPromiseWarnings: true
11203 * @deprecated Promise unwrapping via $parse is deprecated and will be removed in the future.
11206 * @name $parseProvider#unwrapPromises
11209 * **This feature is deprecated, see deprecation notes below for more info**
11211 * If set to true (default is false), $parse will unwrap promises automatically when a promise is
11212 * found at any part of the expression. In other words, if set to true, the expression will always
11213 * result in a non-promise value.
11215 * While the promise is unresolved, it's treated as undefined, but once resolved and fulfilled,
11216 * the fulfillment value is used in place of the promise while evaluating the expression.
11218 * **Deprecation notice**
11220 * This is a feature that didn't prove to be wildly useful or popular, primarily because of the
11221 * dichotomy between data access in templates (accessed as raw values) and controller code
11222 * (accessed as promises).
11224 * In most code we ended up resolving promises manually in controllers anyway and thus unifying
11225 * the model access there.
11227 * Other downsides of automatic promise unwrapping:
11229 * - when building components it's often desirable to receive the raw promises
11230 * - adds complexity and slows down expression evaluation
11231 * - makes expression code pre-generation unattractive due to the amount of code that needs to be
11233 * - makes IDE auto-completion and tool support hard
11237 * If the unwrapping is enabled, Angular will log a warning about each expression that unwraps a
11238 * promise (to reduce the noise, each expression is logged only once). To disable this logging use
11239 * `$parseProvider.logPromiseWarnings(false)` api.
11242 * @param {boolean=} value New value.
11243 * @returns {boolean|self} Returns the current setting when used as getter and self if used as
11246 this.unwrapPromises = function(value) {
11247 if (isDefined(value)) {
11248 $parseOptions.unwrapPromises = !!value;
11251 return $parseOptions.unwrapPromises;
11257 * @deprecated Promise unwrapping via $parse is deprecated and will be removed in the future.
11260 * @name $parseProvider#logPromiseWarnings
11263 * Controls whether Angular should log a warning on any encounter of a promise in an expression.
11265 * The default is set to `true`.
11267 * This setting applies only if `$parseProvider.unwrapPromises` setting is set to true as well.
11269 * @param {boolean=} value New value.
11270 * @returns {boolean|self} Returns the current setting when used as getter and self if used as
11273 this.logPromiseWarnings = function(value) {
11274 if (isDefined(value)) {
11275 $parseOptions.logPromiseWarnings = value;
11278 return $parseOptions.logPromiseWarnings;
11283 this.$get = ['$filter', '$sniffer', '$log', function($filter, $sniffer, $log) {
11284 $parseOptions.csp = $sniffer.csp;
11286 promiseWarning = function promiseWarningFn(fullExp) {
11287 if (!$parseOptions.logPromiseWarnings || promiseWarningCache.hasOwnProperty(fullExp)) return;
11288 promiseWarningCache[fullExp] = true;
11289 $log.warn('[$parse] Promise found in the expression `' + fullExp + '`. ' +
11290 'Automatic unwrapping of promises in Angular expressions is deprecated.');
11293 return function(exp) {
11294 var parsedExpression;
11296 switch (typeof exp) {
11299 if (cache.hasOwnProperty(exp)) {
11303 var lexer = new Lexer($parseOptions);
11304 var parser = new Parser(lexer, $filter, $parseOptions);
11305 parsedExpression = parser.parse(exp);
11307 if (exp !== 'hasOwnProperty') {
11308 // Only cache the value if it's not going to mess up the cache object
11309 // This is more performant that using Object.prototype.hasOwnProperty.call
11310 cache[exp] = parsedExpression;
11313 return parsedExpression;
11328 * @requires $rootScope
11331 * A promise/deferred implementation inspired by [Kris Kowal's Q](https://github.com/kriskowal/q).
11333 * [The CommonJS Promise proposal](http://wiki.commonjs.org/wiki/Promises) describes a promise as an
11334 * interface for interacting with an object that represents the result of an action that is
11335 * performed asynchronously, and may or may not be finished at any given point in time.
11337 * From the perspective of dealing with error handling, deferred and promise APIs are to
11338 * asynchronous programming what `try`, `catch` and `throw` keywords are to synchronous programming.
11341 * // for the purpose of this example let's assume that variables `$q`, `scope` and `okToGreet`
11342 * // are available in the current lexical scope (they could have been injected or passed in).
11344 * function asyncGreet(name) {
11345 * var deferred = $q.defer();
11347 * setTimeout(function() {
11348 * deferred.notify('About to greet ' + name + '.');
11350 * if (okToGreet(name)) {
11351 * deferred.resolve('Hello, ' + name + '!');
11353 * deferred.reject('Greeting ' + name + ' is not allowed.');
11357 * return deferred.promise;
11360 * var promise = asyncGreet('Robin Hood');
11361 * promise.then(function(greeting) {
11362 * alert('Success: ' + greeting);
11363 * }, function(reason) {
11364 * alert('Failed: ' + reason);
11365 * }, function(update) {
11366 * alert('Got notification: ' + update);
11370 * At first it might not be obvious why this extra complexity is worth the trouble. The payoff
11371 * comes in the way of guarantees that promise and deferred APIs make, see
11372 * https://github.com/kriskowal/uncommonjs/blob/master/promises/specification.md.
11374 * Additionally the promise api allows for composition that is very hard to do with the
11375 * traditional callback ([CPS](http://en.wikipedia.org/wiki/Continuation-passing_style)) approach.
11376 * For more on this please see the [Q documentation](https://github.com/kriskowal/q) especially the
11377 * section on serial or parallel joining of promises.
11380 * # The Deferred API
11382 * A new instance of deferred is constructed by calling `$q.defer()`.
11384 * The purpose of the deferred object is to expose the associated Promise instance as well as APIs
11385 * that can be used for signaling the successful or unsuccessful completion, as well as the status
11390 * - `resolve(value)` – resolves the derived promise with the `value`. If the value is a rejection
11391 * constructed via `$q.reject`, the promise will be rejected instead.
11392 * - `reject(reason)` – rejects the derived promise with the `reason`. This is equivalent to
11393 * resolving it with a rejection constructed via `$q.reject`.
11394 * - `notify(value)` - provides updates on the status of the promise's execution. This may be called
11395 * multiple times before the promise is either resolved or rejected.
11399 * - promise – `{Promise}` – promise object associated with this deferred.
11402 * # The Promise API
11404 * A new promise instance is created when a deferred instance is created and can be retrieved by
11405 * calling `deferred.promise`.
11407 * The purpose of the promise object is to allow for interested parties to get access to the result
11408 * of the deferred task when it completes.
11412 * - `then(successCallback, errorCallback, notifyCallback)` – regardless of when the promise was or
11413 * will be resolved or rejected, `then` calls one of the success or error callbacks asynchronously
11414 * as soon as the result is available. The callbacks are called with a single argument: the result
11415 * or rejection reason. Additionally, the notify callback may be called zero or more times to
11416 * provide a progress indication, before the promise is resolved or rejected.
11418 * This method *returns a new promise* which is resolved or rejected via the return value of the
11419 * `successCallback`, `errorCallback`. It also notifies via the return value of the
11420 * `notifyCallback` method. The promise can not be resolved or rejected from the notifyCallback
11423 * - `catch(errorCallback)` – shorthand for `promise.then(null, errorCallback)`
11425 * - `finally(callback)` – allows you to observe either the fulfillment or rejection of a promise,
11426 * but to do so without modifying the final value. This is useful to release resources or do some
11427 * clean-up that needs to be done whether the promise was rejected or resolved. See the [full
11428 * specification](https://github.com/kriskowal/q/wiki/API-Reference#promisefinallycallback) for
11429 * more information.
11431 * Because `finally` is a reserved word in JavaScript and reserved keywords are not supported as
11432 * property names by ES3, you'll need to invoke the method like `promise['finally'](callback)` to
11433 * make your code IE8 and Android 2.x compatible.
11435 * # Chaining promises
11437 * Because calling the `then` method of a promise returns a new derived promise, it is easily
11438 * possible to create a chain of promises:
11441 * promiseB = promiseA.then(function(result) {
11442 * return result + 1;
11445 * // promiseB will be resolved immediately after promiseA is resolved and its value
11446 * // will be the result of promiseA incremented by 1
11449 * It is possible to create chains of any length and since a promise can be resolved with another
11450 * promise (which will defer its resolution further), it is possible to pause/defer resolution of
11451 * the promises at any point in the chain. This makes it possible to implement powerful APIs like
11452 * $http's response interceptors.
11455 * # Differences between Kris Kowal's Q and $q
11457 * There are two main differences:
11459 * - $q is integrated with the {@link ng.$rootScope.Scope} Scope model observation
11460 * mechanism in angular, which means faster propagation of resolution or rejection into your
11461 * models and avoiding unnecessary browser repaints, which would result in flickering UI.
11462 * - Q has many more features than $q, but that comes at a cost of bytes. $q is tiny, but contains
11463 * all the important functionality needed for common async tasks.
11468 * it('should simulate promise', inject(function($q, $rootScope) {
11469 * var deferred = $q.defer();
11470 * var promise = deferred.promise;
11471 * var resolvedValue;
11473 * promise.then(function(value) { resolvedValue = value; });
11474 * expect(resolvedValue).toBeUndefined();
11476 * // Simulate resolving of promise
11477 * deferred.resolve(123);
11478 * // Note that the 'then' function does not get called synchronously.
11479 * // This is because we want the promise API to always be async, whether or not
11480 * // it got called synchronously or asynchronously.
11481 * expect(resolvedValue).toBeUndefined();
11483 * // Propagate promise resolution to 'then' functions using $apply().
11484 * $rootScope.$apply();
11485 * expect(resolvedValue).toEqual(123);
11489 function $QProvider() {
11491 this.$get = ['$rootScope', '$exceptionHandler', function($rootScope, $exceptionHandler) {
11492 return qFactory(function(callback) {
11493 $rootScope.$evalAsync(callback);
11494 }, $exceptionHandler);
11500 * Constructs a promise manager.
11502 * @param {function(Function)} nextTick Function for executing functions in the next turn.
11503 * @param {function(...*)} exceptionHandler Function into which unexpected exceptions are passed for
11504 * debugging purposes.
11505 * @returns {object} Promise manager.
11507 function qFactory(nextTick, exceptionHandler) {
11515 * Creates a `Deferred` object which represents a task which will finish in the future.
11517 * @returns {Deferred} Returns a new instance of deferred.
11519 var defer = function() {
11525 resolve: function(val) {
11527 var callbacks = pending;
11528 pending = undefined;
11531 if (callbacks.length) {
11532 nextTick(function() {
11534 for (var i = 0, ii = callbacks.length; i < ii; i++) {
11535 callback = callbacks[i];
11536 value.then(callback[0], callback[1], callback[2]);
11544 reject: function(reason) {
11545 deferred.resolve(createInternalRejectedPromise(reason));
11549 notify: function(progress) {
11551 var callbacks = pending;
11553 if (pending.length) {
11554 nextTick(function() {
11556 for (var i = 0, ii = callbacks.length; i < ii; i++) {
11557 callback = callbacks[i];
11558 callback[2](progress);
11567 then: function(callback, errback, progressback) {
11568 var result = defer();
11570 var wrappedCallback = function(value) {
11572 result.resolve((isFunction(callback) ? callback : defaultCallback)(value));
11575 exceptionHandler(e);
11579 var wrappedErrback = function(reason) {
11581 result.resolve((isFunction(errback) ? errback : defaultErrback)(reason));
11584 exceptionHandler(e);
11588 var wrappedProgressback = function(progress) {
11590 result.notify((isFunction(progressback) ? progressback : defaultCallback)(progress));
11592 exceptionHandler(e);
11597 pending.push([wrappedCallback, wrappedErrback, wrappedProgressback]);
11599 value.then(wrappedCallback, wrappedErrback, wrappedProgressback);
11602 return result.promise;
11605 "catch": function(callback) {
11606 return this.then(null, callback);
11609 "finally": function(callback) {
11611 function makePromise(value, resolved) {
11612 var result = defer();
11614 result.resolve(value);
11616 result.reject(value);
11618 return result.promise;
11621 function handleCallback(value, isResolved) {
11622 var callbackOutput = null;
11624 callbackOutput = (callback ||defaultCallback)();
11626 return makePromise(e, false);
11628 if (isPromiseLike(callbackOutput)) {
11629 return callbackOutput.then(function() {
11630 return makePromise(value, isResolved);
11631 }, function(error) {
11632 return makePromise(error, false);
11635 return makePromise(value, isResolved);
11639 return this.then(function(value) {
11640 return handleCallback(value, true);
11641 }, function(error) {
11642 return handleCallback(error, false);
11652 var ref = function(value) {
11653 if (isPromiseLike(value)) return value;
11655 then: function(callback) {
11656 var result = defer();
11657 nextTick(function() {
11658 result.resolve(callback(value));
11660 return result.promise;
11672 * Creates a promise that is resolved as rejected with the specified `reason`. This api should be
11673 * used to forward rejection in a chain of promises. If you are dealing with the last promise in
11674 * a promise chain, you don't need to worry about it.
11676 * When comparing deferreds/promises to the familiar behavior of try/catch/throw, think of
11677 * `reject` as the `throw` keyword in JavaScript. This also means that if you "catch" an error via
11678 * a promise error callback and you want to forward the error to the promise derived from the
11679 * current promise, you have to "rethrow" the error by returning a rejection constructed via
11683 * promiseB = promiseA.then(function(result) {
11684 * // success: do something and resolve promiseB
11685 * // with the old or a new result
11687 * }, function(reason) {
11688 * // error: handle the error if possible and
11689 * // resolve promiseB with newPromiseOrValue,
11690 * // otherwise forward the rejection to promiseB
11691 * if (canHandle(reason)) {
11692 * // handle the error and recover
11693 * return newPromiseOrValue;
11695 * return $q.reject(reason);
11699 * @param {*} reason Constant, message, exception or an object representing the rejection reason.
11700 * @returns {Promise} Returns a promise that was already resolved as rejected with the `reason`.
11702 var reject = function(reason) {
11703 var result = defer();
11704 result.reject(reason);
11705 return result.promise;
11708 var createInternalRejectedPromise = function(reason) {
11710 then: function(callback, errback) {
11711 var result = defer();
11712 nextTick(function() {
11714 result.resolve((isFunction(errback) ? errback : defaultErrback)(reason));
11717 exceptionHandler(e);
11720 return result.promise;
11732 * Wraps an object that might be a value or a (3rd party) then-able promise into a $q promise.
11733 * This is useful when you are dealing with an object that might or might not be a promise, or if
11734 * the promise comes from a source that can't be trusted.
11736 * @param {*} value Value or a promise
11737 * @returns {Promise} Returns a promise of the passed value or promise
11739 var when = function(value, callback, errback, progressback) {
11740 var result = defer(),
11743 var wrappedCallback = function(value) {
11745 return (isFunction(callback) ? callback : defaultCallback)(value);
11747 exceptionHandler(e);
11752 var wrappedErrback = function(reason) {
11754 return (isFunction(errback) ? errback : defaultErrback)(reason);
11756 exceptionHandler(e);
11761 var wrappedProgressback = function(progress) {
11763 return (isFunction(progressback) ? progressback : defaultCallback)(progress);
11765 exceptionHandler(e);
11769 nextTick(function() {
11770 ref(value).then(function(value) {
11773 result.resolve(ref(value).then(wrappedCallback, wrappedErrback, wrappedProgressback));
11774 }, function(reason) {
11777 result.resolve(wrappedErrback(reason));
11778 }, function(progress) {
11780 result.notify(wrappedProgressback(progress));
11784 return result.promise;
11788 function defaultCallback(value) {
11793 function defaultErrback(reason) {
11794 return reject(reason);
11804 * Combines multiple promises into a single promise that is resolved when all of the input
11805 * promises are resolved.
11807 * @param {Array.<Promise>|Object.<Promise>} promises An array or hash of promises.
11808 * @returns {Promise} Returns a single promise that will be resolved with an array/hash of values,
11809 * each value corresponding to the promise at the same index/key in the `promises` array/hash.
11810 * If any of the promises is resolved with a rejection, this resulting promise will be rejected
11811 * with the same rejection value.
11813 function all(promises) {
11814 var deferred = defer(),
11816 results = isArray(promises) ? [] : {};
11818 forEach(promises, function(promise, key) {
11820 ref(promise).then(function(value) {
11821 if (results.hasOwnProperty(key)) return;
11822 results[key] = value;
11823 if (!(--counter)) deferred.resolve(results);
11824 }, function(reason) {
11825 if (results.hasOwnProperty(key)) return;
11826 deferred.reject(reason);
11830 if (counter === 0) {
11831 deferred.resolve(results);
11834 return deferred.promise;
11845 function $$RAFProvider(){ //rAF
11846 this.$get = ['$window', '$timeout', function($window, $timeout) {
11847 var requestAnimationFrame = $window.requestAnimationFrame ||
11848 $window.webkitRequestAnimationFrame ||
11849 $window.mozRequestAnimationFrame;
11851 var cancelAnimationFrame = $window.cancelAnimationFrame ||
11852 $window.webkitCancelAnimationFrame ||
11853 $window.mozCancelAnimationFrame ||
11854 $window.webkitCancelRequestAnimationFrame;
11856 var rafSupported = !!requestAnimationFrame;
11857 var raf = rafSupported
11859 var id = requestAnimationFrame(fn);
11860 return function() {
11861 cancelAnimationFrame(id);
11865 var timer = $timeout(fn, 16.66, false); // 1000 / 60 = 16.666
11866 return function() {
11867 $timeout.cancel(timer);
11871 raf.supported = rafSupported;
11880 * The design decisions behind the scope are heavily favored for speed and memory consumption.
11882 * The typical use of scope is to watch the expressions, which most of the time return the same
11883 * value as last time so we optimize the operation.
11885 * Closures construction is expensive in terms of speed as well as memory:
11886 * - No closures, instead use prototypical inheritance for API
11887 * - Internal state needs to be stored on scope directly, which means that private state is
11888 * exposed as $$____ properties
11890 * Loop operations are optimized by using while(count--) { ... }
11891 * - this means that in order to keep the same order of execution as addition we have to add
11892 * items to the array at the beginning (unshift) instead of at the end (push)
11894 * Child scopes are created and removed often
11895 * - Using an array would be slow since inserts in middle are expensive so we use linked list
11897 * There are few watches then a lot of observers. This is why you don't want the observer to be
11898 * implemented in the same way as watch. Watch requires return of initialization function which
11899 * are expensive to construct.
11905 * @name $rootScopeProvider
11908 * Provider for the $rootScope service.
11913 * @name $rootScopeProvider#digestTtl
11916 * Sets the number of `$digest` iterations the scope should attempt to execute before giving up and
11917 * assuming that the model is unstable.
11919 * The current default is 10 iterations.
11921 * In complex applications it's possible that the dependencies between `$watch`s will result in
11922 * several digest iterations. However if an application needs more than the default 10 digest
11923 * iterations for its model to stabilize then you should investigate what is causing the model to
11924 * continuously change during the digest.
11926 * Increasing the TTL could have performance implications, so you should not change it without
11927 * proper justification.
11929 * @param {number} limit The number of digest iterations.
11938 * Every application has a single root {@link ng.$rootScope.Scope scope}.
11939 * All other scopes are descendant scopes of the root scope. Scopes provide separation
11940 * between the model and the view, via a mechanism for watching the model for changes.
11941 * They also provide an event emission/broadcast and subscription facility. See the
11942 * {@link guide/scope developer guide on scopes}.
11944 function $RootScopeProvider(){
11946 var $rootScopeMinErr = minErr('$rootScope');
11947 var lastDirtyWatch = null;
11949 this.digestTtl = function(value) {
11950 if (arguments.length) {
11956 this.$get = ['$injector', '$exceptionHandler', '$parse', '$browser',
11957 function( $injector, $exceptionHandler, $parse, $browser) {
11961 * @name $rootScope.Scope
11964 * A root scope can be retrieved using the {@link ng.$rootScope $rootScope} key from the
11965 * {@link auto.$injector $injector}. Child scopes are created using the
11966 * {@link ng.$rootScope.Scope#$new $new()} method. (Most scopes are created automatically when
11967 * compiled HTML template is executed.)
11969 * Here is a simple scope snippet to show how you can interact with the scope.
11971 * <file src="./test/ng/rootScopeSpec.js" tag="docs1" />
11975 * A scope can inherit from a parent scope, as in this example:
11977 var parent = $rootScope;
11978 var child = parent.$new();
11980 parent.salutation = "Hello";
11981 child.name = "World";
11982 expect(child.salutation).toEqual('Hello');
11984 child.salutation = "Welcome";
11985 expect(child.salutation).toEqual('Welcome');
11986 expect(parent.salutation).toEqual('Hello');
11990 * @param {Object.<string, function()>=} providers Map of service factory which need to be
11991 * provided for the current scope. Defaults to {@link ng}.
11992 * @param {Object.<string, *>=} instanceCache Provides pre-instantiated services which should
11993 * append/override services provided by `providers`. This is handy
11994 * when unit-testing and having the need to override a default
11996 * @returns {Object} Newly created scope.
12000 this.$id = nextUid();
12001 this.$$phase = this.$parent = this.$$watchers =
12002 this.$$nextSibling = this.$$prevSibling =
12003 this.$$childHead = this.$$childTail = null;
12004 this['this'] = this.$root = this;
12005 this.$$destroyed = false;
12006 this.$$asyncQueue = [];
12007 this.$$postDigestQueue = [];
12008 this.$$listeners = {};
12009 this.$$listenerCount = {};
12010 this.$$isolateBindings = {};
12015 * @name $rootScope.Scope#$id
12018 * Unique scope ID (monotonically increasing) useful for debugging.
12023 * @name $rootScope.Scope#$parent
12026 * Reference to the parent scope.
12031 * @name $rootScope.Scope#$root
12034 * Reference to the root scope.
12037 Scope.prototype = {
12038 constructor: Scope,
12041 * @name $rootScope.Scope#$new
12045 * Creates a new child {@link ng.$rootScope.Scope scope}.
12047 * The parent scope will propagate the {@link ng.$rootScope.Scope#$digest $digest()} event.
12048 * The scope can be removed from the scope hierarchy using {@link ng.$rootScope.Scope#$destroy $destroy()}.
12050 * {@link ng.$rootScope.Scope#$destroy $destroy()} must be called on a scope when it is
12051 * desired for the scope and its child scopes to be permanently detached from the parent and
12052 * thus stop participating in model change detection and listener notification by invoking.
12054 * @param {boolean} isolate If true, then the scope does not prototypically inherit from the
12055 * parent scope. The scope is isolated, as it can not see parent scope properties.
12056 * When creating widgets, it is useful for the widget to not accidentally read parent
12059 * @returns {Object} The newly created child scope.
12062 $new: function(isolate) {
12067 child = new Scope();
12068 child.$root = this.$root;
12069 // ensure that there is just one async queue per $rootScope and its children
12070 child.$$asyncQueue = this.$$asyncQueue;
12071 child.$$postDigestQueue = this.$$postDigestQueue;
12073 // Only create a child scope class if somebody asks for one,
12074 // but cache it to allow the VM to optimize lookups.
12075 if (!this.$$childScopeClass) {
12076 this.$$childScopeClass = function() {
12077 this.$$watchers = this.$$nextSibling =
12078 this.$$childHead = this.$$childTail = null;
12079 this.$$listeners = {};
12080 this.$$listenerCount = {};
12081 this.$id = nextUid();
12082 this.$$childScopeClass = null;
12084 this.$$childScopeClass.prototype = this;
12086 child = new this.$$childScopeClass();
12088 child['this'] = child;
12089 child.$parent = this;
12090 child.$$prevSibling = this.$$childTail;
12091 if (this.$$childHead) {
12092 this.$$childTail.$$nextSibling = child;
12093 this.$$childTail = child;
12095 this.$$childHead = this.$$childTail = child;
12102 * @name $rootScope.Scope#$watch
12106 * Registers a `listener` callback to be executed whenever the `watchExpression` changes.
12108 * - The `watchExpression` is called on every call to {@link ng.$rootScope.Scope#$digest
12109 * $digest()} and should return the value that will be watched. (Since
12110 * {@link ng.$rootScope.Scope#$digest $digest()} reruns when it detects changes the
12111 * `watchExpression` can execute multiple times per
12112 * {@link ng.$rootScope.Scope#$digest $digest()} and should be idempotent.)
12113 * - The `listener` is called only when the value from the current `watchExpression` and the
12114 * previous call to `watchExpression` are not equal (with the exception of the initial run,
12115 * see below). Inequality is determined according to reference inequality,
12116 * [strict comparison](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators)
12117 * via the `!==` Javascript operator, unless `objectEquality == true`
12119 * - When `objectEquality == true`, inequality of the `watchExpression` is determined
12120 * according to the {@link angular.equals} function. To save the value of the object for
12121 * later comparison, the {@link angular.copy} function is used. This therefore means that
12122 * watching complex objects will have adverse memory and performance implications.
12123 * - The watch `listener` may change the model, which may trigger other `listener`s to fire.
12124 * This is achieved by rerunning the watchers until no changes are detected. The rerun
12125 * iteration limit is 10 to prevent an infinite loop deadlock.
12128 * If you want to be notified whenever {@link ng.$rootScope.Scope#$digest $digest} is called,
12129 * you can register a `watchExpression` function with no `listener`. (Since `watchExpression`
12130 * can execute multiple times per {@link ng.$rootScope.Scope#$digest $digest} cycle when a
12131 * change is detected, be prepared for multiple calls to your listener.)
12133 * After a watcher is registered with the scope, the `listener` fn is called asynchronously
12134 * (via {@link ng.$rootScope.Scope#$evalAsync $evalAsync}) to initialize the
12135 * watcher. In rare cases, this is undesirable because the listener is called when the result
12136 * of `watchExpression` didn't change. To detect this scenario within the `listener` fn, you
12137 * can compare the `newVal` and `oldVal`. If these two values are identical (`===`) then the
12138 * listener was called due to initialization.
12140 * The example below contains an illustration of using a function as your $watch listener
12145 // let's assume that scope was dependency injected as the $rootScope
12146 var scope = $rootScope;
12147 scope.name = 'misko';
12150 expect(scope.counter).toEqual(0);
12151 scope.$watch('name', function(newValue, oldValue) {
12152 scope.counter = scope.counter + 1;
12154 expect(scope.counter).toEqual(0);
12157 // the listener is always called during the first $digest loop after it was registered
12158 expect(scope.counter).toEqual(1);
12161 // but now it will not be called unless the value changes
12162 expect(scope.counter).toEqual(1);
12164 scope.name = 'adam';
12166 expect(scope.counter).toEqual(2);
12170 // Using a listener function
12172 scope.foodCounter = 0;
12173 expect(scope.foodCounter).toEqual(0);
12175 // This is the listener function
12176 function() { return food; },
12177 // This is the change handler
12178 function(newValue, oldValue) {
12179 if ( newValue !== oldValue ) {
12180 // Only increment the counter if the value changed
12181 scope.foodCounter = scope.foodCounter + 1;
12185 // No digest has been run so the counter will be zero
12186 expect(scope.foodCounter).toEqual(0);
12188 // Run the digest but since food has not changed count will still be zero
12190 expect(scope.foodCounter).toEqual(0);
12192 // Update food and run digest. Now the counter will increment
12193 food = 'cheeseburger';
12195 expect(scope.foodCounter).toEqual(1);
12201 * @param {(function()|string)} watchExpression Expression that is evaluated on each
12202 * {@link ng.$rootScope.Scope#$digest $digest} cycle. A change in the return value triggers
12203 * a call to the `listener`.
12205 * - `string`: Evaluated as {@link guide/expression expression}
12206 * - `function(scope)`: called with current `scope` as a parameter.
12207 * @param {(function()|string)=} listener Callback called whenever the return value of
12208 * the `watchExpression` changes.
12210 * - `string`: Evaluated as {@link guide/expression expression}
12211 * - `function(newValue, oldValue, scope)`: called with current and previous values as
12214 * @param {boolean=} objectEquality Compare for object equality using {@link angular.equals} instead of
12215 * comparing for reference equality.
12216 * @returns {function()} Returns a deregistration function for this listener.
12218 $watch: function(watchExp, listener, objectEquality) {
12220 get = compileToFn(watchExp, 'watch'),
12221 array = scope.$$watchers,
12224 last: initWatchVal,
12227 eq: !!objectEquality
12230 lastDirtyWatch = null;
12232 // in the case user pass string, we need to compile it, do we really need this ?
12233 if (!isFunction(listener)) {
12234 var listenFn = compileToFn(listener || noop, 'listener');
12235 watcher.fn = function(newVal, oldVal, scope) {listenFn(scope);};
12238 if (typeof watchExp == 'string' && get.constant) {
12239 var originalFn = watcher.fn;
12240 watcher.fn = function(newVal, oldVal, scope) {
12241 originalFn.call(this, newVal, oldVal, scope);
12242 arrayRemove(array, watcher);
12247 array = scope.$$watchers = [];
12249 // we use unshift since we use a while loop in $digest for speed.
12250 // the while loop reads in reverse order.
12251 array.unshift(watcher);
12253 return function deregisterWatch() {
12254 arrayRemove(array, watcher);
12255 lastDirtyWatch = null;
12262 * @name $rootScope.Scope#$watchCollection
12266 * Shallow watches the properties of an object and fires whenever any of the properties change
12267 * (for arrays, this implies watching the array items; for object maps, this implies watching
12268 * the properties). If a change is detected, the `listener` callback is fired.
12270 * - The `obj` collection is observed via standard $watch operation and is examined on every
12271 * call to $digest() to see if any items have been added, removed, or moved.
12272 * - The `listener` is called whenever anything within the `obj` has changed. Examples include
12273 * adding, removing, and moving items belonging to an object or array.
12278 $scope.names = ['igor', 'matias', 'misko', 'james'];
12279 $scope.dataCount = 4;
12281 $scope.$watchCollection('names', function(newNames, oldNames) {
12282 $scope.dataCount = newNames.length;
12285 expect($scope.dataCount).toEqual(4);
12288 //still at 4 ... no changes
12289 expect($scope.dataCount).toEqual(4);
12291 $scope.names.pop();
12294 //now there's been a change
12295 expect($scope.dataCount).toEqual(3);
12299 * @param {string|function(scope)} obj Evaluated as {@link guide/expression expression}. The
12300 * expression value should evaluate to an object or an array which is observed on each
12301 * {@link ng.$rootScope.Scope#$digest $digest} cycle. Any shallow change within the
12302 * collection will trigger a call to the `listener`.
12304 * @param {function(newCollection, oldCollection, scope)} listener a callback function called
12305 * when a change is detected.
12306 * - The `newCollection` object is the newly modified data obtained from the `obj` expression
12307 * - The `oldCollection` object is a copy of the former collection data.
12308 * Due to performance considerations, the`oldCollection` value is computed only if the
12309 * `listener` function declares two or more arguments.
12310 * - The `scope` argument refers to the current scope.
12312 * @returns {function()} Returns a de-registration function for this listener. When the
12313 * de-registration function is executed, the internal watch operation is terminated.
12315 $watchCollection: function(obj, listener) {
12317 // the current value, updated on each dirty-check run
12319 // a shallow copy of the newValue from the last dirty-check run,
12320 // updated to match newValue during dirty-check run
12322 // a shallow copy of the newValue from when the last change happened
12324 // only track veryOldValue if the listener is asking for it
12325 var trackVeryOldValue = (listener.length > 1);
12326 var changeDetected = 0;
12327 var objGetter = $parse(obj);
12328 var internalArray = [];
12329 var internalObject = {};
12330 var initRun = true;
12333 function $watchCollectionWatch() {
12334 newValue = objGetter(self);
12335 var newLength, key, bothNaN;
12337 if (!isObject(newValue)) { // if primitive
12338 if (oldValue !== newValue) {
12339 oldValue = newValue;
12342 } else if (isArrayLike(newValue)) {
12343 if (oldValue !== internalArray) {
12344 // we are transitioning from something which was not an array into array.
12345 oldValue = internalArray;
12346 oldLength = oldValue.length = 0;
12350 newLength = newValue.length;
12352 if (oldLength !== newLength) {
12353 // if lengths do not match we need to trigger change notification
12355 oldValue.length = oldLength = newLength;
12357 // copy the items to oldValue and look for changes.
12358 for (var i = 0; i < newLength; i++) {
12359 bothNaN = (oldValue[i] !== oldValue[i]) &&
12360 (newValue[i] !== newValue[i]);
12361 if (!bothNaN && (oldValue[i] !== newValue[i])) {
12363 oldValue[i] = newValue[i];
12367 if (oldValue !== internalObject) {
12368 // we are transitioning from something which was not an object into object.
12369 oldValue = internalObject = {};
12373 // copy the items to oldValue and look for changes.
12375 for (key in newValue) {
12376 if (newValue.hasOwnProperty(key)) {
12378 if (oldValue.hasOwnProperty(key)) {
12379 bothNaN = (oldValue[key] !== oldValue[key]) &&
12380 (newValue[key] !== newValue[key]);
12381 if (!bothNaN && (oldValue[key] !== newValue[key])) {
12383 oldValue[key] = newValue[key];
12387 oldValue[key] = newValue[key];
12392 if (oldLength > newLength) {
12393 // we used to have more keys, need to find them and destroy them.
12395 for(key in oldValue) {
12396 if (oldValue.hasOwnProperty(key) && !newValue.hasOwnProperty(key)) {
12398 delete oldValue[key];
12403 return changeDetected;
12406 function $watchCollectionAction() {
12409 listener(newValue, newValue, self);
12411 listener(newValue, veryOldValue, self);
12414 // make a copy for the next time a collection is changed
12415 if (trackVeryOldValue) {
12416 if (!isObject(newValue)) {
12418 veryOldValue = newValue;
12419 } else if (isArrayLike(newValue)) {
12420 veryOldValue = new Array(newValue.length);
12421 for (var i = 0; i < newValue.length; i++) {
12422 veryOldValue[i] = newValue[i];
12424 } else { // if object
12426 for (var key in newValue) {
12427 if (hasOwnProperty.call(newValue, key)) {
12428 veryOldValue[key] = newValue[key];
12435 return this.$watch($watchCollectionWatch, $watchCollectionAction);
12440 * @name $rootScope.Scope#$digest
12444 * Processes all of the {@link ng.$rootScope.Scope#$watch watchers} of the current scope and
12445 * its children. Because a {@link ng.$rootScope.Scope#$watch watcher}'s listener can change
12446 * the model, the `$digest()` keeps calling the {@link ng.$rootScope.Scope#$watch watchers}
12447 * until no more listeners are firing. This means that it is possible to get into an infinite
12448 * loop. This function will throw `'Maximum iteration limit exceeded.'` if the number of
12449 * iterations exceeds 10.
12451 * Usually, you don't call `$digest()` directly in
12452 * {@link ng.directive:ngController controllers} or in
12453 * {@link ng.$compileProvider#directive directives}.
12454 * Instead, you should call {@link ng.$rootScope.Scope#$apply $apply()} (typically from within
12455 * a {@link ng.$compileProvider#directive directives}), which will force a `$digest()`.
12457 * If you want to be notified whenever `$digest()` is called,
12458 * you can register a `watchExpression` function with
12459 * {@link ng.$rootScope.Scope#$watch $watch()} with no `listener`.
12461 * In unit tests, you may need to call `$digest()` to simulate the scope life cycle.
12466 scope.name = 'misko';
12469 expect(scope.counter).toEqual(0);
12470 scope.$watch('name', function(newValue, oldValue) {
12471 scope.counter = scope.counter + 1;
12473 expect(scope.counter).toEqual(0);
12476 // the listener is always called during the first $digest loop after it was registered
12477 expect(scope.counter).toEqual(1);
12480 // but now it will not be called unless the value changes
12481 expect(scope.counter).toEqual(1);
12483 scope.name = 'adam';
12485 expect(scope.counter).toEqual(2);
12489 $digest: function() {
12490 var watch, value, last,
12492 asyncQueue = this.$$asyncQueue,
12493 postDigestQueue = this.$$postDigestQueue,
12496 next, current, target = this,
12498 logIdx, logMsg, asyncTask;
12500 beginPhase('$digest');
12501 // Check for changes to browser url that happened in sync before the call to $digest
12502 $browser.$$checkUrlChange();
12504 lastDirtyWatch = null;
12506 do { // "while dirty" loop
12510 while(asyncQueue.length) {
12512 asyncTask = asyncQueue.shift();
12513 asyncTask.scope.$eval(asyncTask.expression);
12516 $exceptionHandler(e);
12518 lastDirtyWatch = null;
12521 traverseScopesLoop:
12522 do { // "traverse the scopes" loop
12523 if ((watchers = current.$$watchers)) {
12524 // process our watches
12525 length = watchers.length;
12528 watch = watchers[length];
12529 // Most common watches are on primitives, in which case we can short
12530 // circuit it with === operator, only when === fails do we use .equals
12532 if ((value = watch.get(current)) !== (last = watch.last) &&
12534 ? equals(value, last)
12535 : (typeof value === 'number' && typeof last === 'number'
12536 && isNaN(value) && isNaN(last)))) {
12538 lastDirtyWatch = watch;
12539 watch.last = watch.eq ? copy(value, null) : value;
12540 watch.fn(value, ((last === initWatchVal) ? value : last), current);
12543 if (!watchLog[logIdx]) watchLog[logIdx] = [];
12544 logMsg = (isFunction(watch.exp))
12545 ? 'fn: ' + (watch.exp.name || watch.exp.toString())
12547 logMsg += '; newVal: ' + toJson(value) + '; oldVal: ' + toJson(last);
12548 watchLog[logIdx].push(logMsg);
12550 } else if (watch === lastDirtyWatch) {
12551 // If the most recently dirty watcher is now clean, short circuit since the remaining watchers
12552 // have already been tested.
12554 break traverseScopesLoop;
12559 $exceptionHandler(e);
12564 // Insanity Warning: scope depth-first traversal
12565 // yes, this code is a bit crazy, but it works and we have tests to prove it!
12566 // this piece should be kept in sync with the traversal in $broadcast
12567 if (!(next = (current.$$childHead ||
12568 (current !== target && current.$$nextSibling)))) {
12569 while(current !== target && !(next = current.$$nextSibling)) {
12570 current = current.$parent;
12573 } while ((current = next));
12575 // `break traverseScopesLoop;` takes us to here
12577 if((dirty || asyncQueue.length) && !(ttl--)) {
12579 throw $rootScopeMinErr('infdig',
12580 '{0} $digest() iterations reached. Aborting!\n' +
12581 'Watchers fired in the last 5 iterations: {1}',
12582 TTL, toJson(watchLog));
12585 } while (dirty || asyncQueue.length);
12589 while(postDigestQueue.length) {
12591 postDigestQueue.shift()();
12593 $exceptionHandler(e);
12601 * @name $rootScope.Scope#$destroy
12602 * @eventType broadcast on scope being destroyed
12605 * Broadcasted when a scope and its children are being destroyed.
12607 * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to
12608 * clean up DOM bindings before an element is removed from the DOM.
12613 * @name $rootScope.Scope#$destroy
12617 * Removes the current scope (and all of its children) from the parent scope. Removal implies
12618 * that calls to {@link ng.$rootScope.Scope#$digest $digest()} will no longer
12619 * propagate to the current scope and its children. Removal also implies that the current
12620 * scope is eligible for garbage collection.
12622 * The `$destroy()` is usually used by directives such as
12623 * {@link ng.directive:ngRepeat ngRepeat} for managing the
12624 * unrolling of the loop.
12626 * Just before a scope is destroyed, a `$destroy` event is broadcasted on this scope.
12627 * Application code can register a `$destroy` event handler that will give it a chance to
12628 * perform any necessary cleanup.
12630 * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to
12631 * clean up DOM bindings before an element is removed from the DOM.
12633 $destroy: function() {
12634 // we can't destroy the root scope or a scope that has been already destroyed
12635 if (this.$$destroyed) return;
12636 var parent = this.$parent;
12638 this.$broadcast('$destroy');
12639 this.$$destroyed = true;
12640 if (this === $rootScope) return;
12642 forEach(this.$$listenerCount, bind(null, decrementListenerCount, this));
12644 // sever all the references to parent scopes (after this cleanup, the current scope should
12645 // not be retained by any of our references and should be eligible for garbage collection)
12646 if (parent.$$childHead == this) parent.$$childHead = this.$$nextSibling;
12647 if (parent.$$childTail == this) parent.$$childTail = this.$$prevSibling;
12648 if (this.$$prevSibling) this.$$prevSibling.$$nextSibling = this.$$nextSibling;
12649 if (this.$$nextSibling) this.$$nextSibling.$$prevSibling = this.$$prevSibling;
12652 // All of the code below is bogus code that works around V8's memory leak via optimized code
12653 // and inline caches.
12656 // - https://code.google.com/p/v8/issues/detail?id=2073#c26
12657 // - https://github.com/angular/angular.js/issues/6794#issuecomment-38648909
12658 // - https://github.com/angular/angular.js/issues/1313#issuecomment-10378451
12660 this.$parent = this.$$nextSibling = this.$$prevSibling = this.$$childHead =
12661 this.$$childTail = this.$root = null;
12663 // don't reset these to null in case some async task tries to register a listener/watch/task
12664 this.$$listeners = {};
12665 this.$$watchers = this.$$asyncQueue = this.$$postDigestQueue = [];
12667 // prevent NPEs since these methods have references to properties we nulled out
12668 this.$destroy = this.$digest = this.$apply = noop;
12669 this.$on = this.$watch = function() { return noop; };
12674 * @name $rootScope.Scope#$eval
12678 * Executes the `expression` on the current scope and returns the result. Any exceptions in
12679 * the expression are propagated (uncaught). This is useful when evaluating Angular
12684 var scope = ng.$rootScope.Scope();
12688 expect(scope.$eval('a+b')).toEqual(3);
12689 expect(scope.$eval(function(scope){ return scope.a + scope.b; })).toEqual(3);
12692 * @param {(string|function())=} expression An angular expression to be executed.
12694 * - `string`: execute using the rules as defined in {@link guide/expression expression}.
12695 * - `function(scope)`: execute the function with the current `scope` parameter.
12697 * @param {(object)=} locals Local variables object, useful for overriding values in scope.
12698 * @returns {*} The result of evaluating the expression.
12700 $eval: function(expr, locals) {
12701 return $parse(expr)(this, locals);
12706 * @name $rootScope.Scope#$evalAsync
12710 * Executes the expression on the current scope at a later point in time.
12712 * The `$evalAsync` makes no guarantees as to when the `expression` will be executed, only
12715 * - it will execute after the function that scheduled the evaluation (preferably before DOM
12717 * - at least one {@link ng.$rootScope.Scope#$digest $digest cycle} will be performed after
12718 * `expression` execution.
12720 * Any exceptions from the execution of the expression are forwarded to the
12721 * {@link ng.$exceptionHandler $exceptionHandler} service.
12723 * __Note:__ if this function is called outside of a `$digest` cycle, a new `$digest` cycle
12724 * will be scheduled. However, it is encouraged to always call code that changes the model
12725 * from within an `$apply` call. That includes code evaluated via `$evalAsync`.
12727 * @param {(string|function())=} expression An angular expression to be executed.
12729 * - `string`: execute using the rules as defined in {@link guide/expression expression}.
12730 * - `function(scope)`: execute the function with the current `scope` parameter.
12733 $evalAsync: function(expr) {
12734 // if we are outside of an $digest loop and this is the first time we are scheduling async
12735 // task also schedule async auto-flush
12736 if (!$rootScope.$$phase && !$rootScope.$$asyncQueue.length) {
12737 $browser.defer(function() {
12738 if ($rootScope.$$asyncQueue.length) {
12739 $rootScope.$digest();
12744 this.$$asyncQueue.push({scope: this, expression: expr});
12747 $$postDigest : function(fn) {
12748 this.$$postDigestQueue.push(fn);
12753 * @name $rootScope.Scope#$apply
12757 * `$apply()` is used to execute an expression in angular from outside of the angular
12758 * framework. (For example from browser DOM events, setTimeout, XHR or third party libraries).
12759 * Because we are calling into the angular framework we need to perform proper scope life
12760 * cycle of {@link ng.$exceptionHandler exception handling},
12761 * {@link ng.$rootScope.Scope#$digest executing watches}.
12765 * # Pseudo-Code of `$apply()`
12767 function $apply(expr) {
12769 return $eval(expr);
12771 $exceptionHandler(e);
12779 * Scope's `$apply()` method transitions through the following stages:
12781 * 1. The {@link guide/expression expression} is executed using the
12782 * {@link ng.$rootScope.Scope#$eval $eval()} method.
12783 * 2. Any exceptions from the execution of the expression are forwarded to the
12784 * {@link ng.$exceptionHandler $exceptionHandler} service.
12785 * 3. The {@link ng.$rootScope.Scope#$watch watch} listeners are fired immediately after the
12786 * expression was executed using the {@link ng.$rootScope.Scope#$digest $digest()} method.
12789 * @param {(string|function())=} exp An angular expression to be executed.
12791 * - `string`: execute using the rules as defined in {@link guide/expression expression}.
12792 * - `function(scope)`: execute the function with current `scope` parameter.
12794 * @returns {*} The result of evaluating the expression.
12796 $apply: function(expr) {
12798 beginPhase('$apply');
12799 return this.$eval(expr);
12801 $exceptionHandler(e);
12805 $rootScope.$digest();
12807 $exceptionHandler(e);
12815 * @name $rootScope.Scope#$on
12819 * Listens on events of a given type. See {@link ng.$rootScope.Scope#$emit $emit} for
12820 * discussion of event life cycle.
12822 * The event listener function format is: `function(event, args...)`. The `event` object
12823 * passed into the listener has the following attributes:
12825 * - `targetScope` - `{Scope}`: the scope on which the event was `$emit`-ed or
12827 * - `currentScope` - `{Scope}`: the current scope which is handling the event.
12828 * - `name` - `{string}`: name of the event.
12829 * - `stopPropagation` - `{function=}`: calling `stopPropagation` function will cancel
12830 * further event propagation (available only for events that were `$emit`-ed).
12831 * - `preventDefault` - `{function}`: calling `preventDefault` sets `defaultPrevented` flag
12833 * - `defaultPrevented` - `{boolean}`: true if `preventDefault` was called.
12835 * @param {string} name Event name to listen on.
12836 * @param {function(event, ...args)} listener Function to call when the event is emitted.
12837 * @returns {function()} Returns a deregistration function for this listener.
12839 $on: function(name, listener) {
12840 var namedListeners = this.$$listeners[name];
12841 if (!namedListeners) {
12842 this.$$listeners[name] = namedListeners = [];
12844 namedListeners.push(listener);
12846 var current = this;
12848 if (!current.$$listenerCount[name]) {
12849 current.$$listenerCount[name] = 0;
12851 current.$$listenerCount[name]++;
12852 } while ((current = current.$parent));
12855 return function() {
12856 namedListeners[indexOf(namedListeners, listener)] = null;
12857 decrementListenerCount(self, 1, name);
12864 * @name $rootScope.Scope#$emit
12868 * Dispatches an event `name` upwards through the scope hierarchy notifying the
12869 * registered {@link ng.$rootScope.Scope#$on} listeners.
12871 * The event life cycle starts at the scope on which `$emit` was called. All
12872 * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get
12873 * notified. Afterwards, the event traverses upwards toward the root scope and calls all
12874 * registered listeners along the way. The event will stop propagating if one of the listeners
12877 * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
12878 * onto the {@link ng.$exceptionHandler $exceptionHandler} service.
12880 * @param {string} name Event name to emit.
12881 * @param {...*} args Optional one or more arguments which will be passed onto the event listeners.
12882 * @return {Object} Event object (see {@link ng.$rootScope.Scope#$on}).
12884 $emit: function(name, args) {
12888 stopPropagation = false,
12891 targetScope: scope,
12892 stopPropagation: function() {stopPropagation = true;},
12893 preventDefault: function() {
12894 event.defaultPrevented = true;
12896 defaultPrevented: false
12898 listenerArgs = concat([event], arguments, 1),
12902 namedListeners = scope.$$listeners[name] || empty;
12903 event.currentScope = scope;
12904 for (i=0, length=namedListeners.length; i<length; i++) {
12906 // if listeners were deregistered, defragment the array
12907 if (!namedListeners[i]) {
12908 namedListeners.splice(i, 1);
12914 //allow all listeners attached to the current scope to run
12915 namedListeners[i].apply(null, listenerArgs);
12917 $exceptionHandler(e);
12920 //if any listener on the current scope stops propagation, prevent bubbling
12921 if (stopPropagation) return event;
12923 scope = scope.$parent;
12932 * @name $rootScope.Scope#$broadcast
12936 * Dispatches an event `name` downwards to all child scopes (and their children) notifying the
12937 * registered {@link ng.$rootScope.Scope#$on} listeners.
12939 * The event life cycle starts at the scope on which `$broadcast` was called. All
12940 * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get
12941 * notified. Afterwards, the event propagates to all direct and indirect scopes of the current
12942 * scope and calls all registered listeners along the way. The event cannot be canceled.
12944 * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
12945 * onto the {@link ng.$exceptionHandler $exceptionHandler} service.
12947 * @param {string} name Event name to broadcast.
12948 * @param {...*} args Optional one or more arguments which will be passed onto the event listeners.
12949 * @return {Object} Event object, see {@link ng.$rootScope.Scope#$on}
12951 $broadcast: function(name, args) {
12957 targetScope: target,
12958 preventDefault: function() {
12959 event.defaultPrevented = true;
12961 defaultPrevented: false
12963 listenerArgs = concat([event], arguments, 1),
12964 listeners, i, length;
12966 //down while you can, then up and next sibling or up and next sibling until back at root
12967 while ((current = next)) {
12968 event.currentScope = current;
12969 listeners = current.$$listeners[name] || [];
12970 for (i=0, length = listeners.length; i<length; i++) {
12971 // if listeners were deregistered, defragment the array
12972 if (!listeners[i]) {
12973 listeners.splice(i, 1);
12980 listeners[i].apply(null, listenerArgs);
12982 $exceptionHandler(e);
12986 // Insanity Warning: scope depth-first traversal
12987 // yes, this code is a bit crazy, but it works and we have tests to prove it!
12988 // this piece should be kept in sync with the traversal in $digest
12989 // (though it differs due to having the extra check for $$listenerCount)
12990 if (!(next = ((current.$$listenerCount[name] && current.$$childHead) ||
12991 (current !== target && current.$$nextSibling)))) {
12992 while(current !== target && !(next = current.$$nextSibling)) {
12993 current = current.$parent;
13002 var $rootScope = new Scope();
13007 function beginPhase(phase) {
13008 if ($rootScope.$$phase) {
13009 throw $rootScopeMinErr('inprog', '{0} already in progress', $rootScope.$$phase);
13012 $rootScope.$$phase = phase;
13015 function clearPhase() {
13016 $rootScope.$$phase = null;
13019 function compileToFn(exp, name) {
13020 var fn = $parse(exp);
13021 assertArgFn(fn, name);
13025 function decrementListenerCount(current, count, name) {
13027 current.$$listenerCount[name] -= count;
13029 if (current.$$listenerCount[name] === 0) {
13030 delete current.$$listenerCount[name];
13032 } while ((current = current.$parent));
13036 * function used as an initial value for watchers.
13037 * because it's unique we can easily tell it apart from other values
13039 function initWatchVal() {}
13045 * Private service to sanitize uris for links and images. Used by $compile and $sanitize.
13047 function $$SanitizeUriProvider() {
13048 var aHrefSanitizationWhitelist = /^\s*(https?|ftp|mailto|tel|file):/,
13049 imgSrcSanitizationWhitelist = /^\s*((https?|ftp|file):|data:image\/)/;
13053 * Retrieves or overrides the default regular expression that is used for whitelisting of safe
13054 * urls during a[href] sanitization.
13056 * The sanitization is a security measure aimed at prevent XSS attacks via html links.
13058 * Any url about to be assigned to a[href] via data-binding is first normalized and turned into
13059 * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist`
13060 * regular expression. If a match is found, the original url is written into the dom. Otherwise,
13061 * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
13063 * @param {RegExp=} regexp New regexp to whitelist urls with.
13064 * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
13065 * chaining otherwise.
13067 this.aHrefSanitizationWhitelist = function(regexp) {
13068 if (isDefined(regexp)) {
13069 aHrefSanitizationWhitelist = regexp;
13072 return aHrefSanitizationWhitelist;
13078 * Retrieves or overrides the default regular expression that is used for whitelisting of safe
13079 * urls during img[src] sanitization.
13081 * The sanitization is a security measure aimed at prevent XSS attacks via html links.
13083 * Any url about to be assigned to img[src] via data-binding is first normalized and turned into
13084 * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist`
13085 * regular expression. If a match is found, the original url is written into the dom. Otherwise,
13086 * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
13088 * @param {RegExp=} regexp New regexp to whitelist urls with.
13089 * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
13090 * chaining otherwise.
13092 this.imgSrcSanitizationWhitelist = function(regexp) {
13093 if (isDefined(regexp)) {
13094 imgSrcSanitizationWhitelist = regexp;
13097 return imgSrcSanitizationWhitelist;
13100 this.$get = function() {
13101 return function sanitizeUri(uri, isImage) {
13102 var regex = isImage ? imgSrcSanitizationWhitelist : aHrefSanitizationWhitelist;
13104 // NOTE: urlResolve() doesn't support IE < 8 so we don't sanitize for that case.
13105 if (!msie || msie >= 8 ) {
13106 normalizedVal = urlResolve(uri).href;
13107 if (normalizedVal !== '' && !normalizedVal.match(regex)) {
13108 return 'unsafe:'+normalizedVal;
13116 var $sceMinErr = minErr('$sce');
13118 var SCE_CONTEXTS = {
13122 // RESOURCE_URL is a subtype of URL used in contexts where a privileged resource is sourced from a
13123 // url. (e.g. ng-include, script src, templateUrl)
13124 RESOURCE_URL: 'resourceUrl',
13128 // Helper functions follow.
13131 // http://docs.closure-library.googlecode.com/git/closure_goog_string_string.js.source.html#line962
13132 // Prereq: s is a string.
13133 function escapeForRegexp(s) {
13134 return s.replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g, '\\$1').
13135 replace(/\x08/g, '\\x08');
13139 function adjustMatcher(matcher) {
13140 if (matcher === 'self') {
13142 } else if (isString(matcher)) {
13143 // Strings match exactly except for 2 wildcards - '*' and '**'.
13144 // '*' matches any character except those from the set ':/.?&'.
13145 // '**' matches any character (like .* in a RegExp).
13146 // More than 2 *'s raises an error as it's ill defined.
13147 if (matcher.indexOf('***') > -1) {
13148 throw $sceMinErr('iwcard',
13149 'Illegal sequence *** in string matcher. String: {0}', matcher);
13151 matcher = escapeForRegexp(matcher).
13152 replace('\\*\\*', '.*').
13153 replace('\\*', '[^:/.?&;]*');
13154 return new RegExp('^' + matcher + '$');
13155 } else if (isRegExp(matcher)) {
13156 // The only other type of matcher allowed is a Regexp.
13157 // Match entire URL / disallow partial matches.
13158 // Flags are reset (i.e. no global, ignoreCase or multiline)
13159 return new RegExp('^' + matcher.source + '$');
13161 throw $sceMinErr('imatcher',
13162 'Matchers may only be "self", string patterns or RegExp objects');
13167 function adjustMatchers(matchers) {
13168 var adjustedMatchers = [];
13169 if (isDefined(matchers)) {
13170 forEach(matchers, function(matcher) {
13171 adjustedMatchers.push(adjustMatcher(matcher));
13174 return adjustedMatchers;
13180 * @name $sceDelegate
13185 * `$sceDelegate` is a service that is used by the `$sce` service to provide {@link ng.$sce Strict
13186 * Contextual Escaping (SCE)} services to AngularJS.
13188 * Typically, you would configure or override the {@link ng.$sceDelegate $sceDelegate} instead of
13189 * the `$sce` service to customize the way Strict Contextual Escaping works in AngularJS. This is
13190 * because, while the `$sce` provides numerous shorthand methods, etc., you really only need to
13191 * override 3 core functions (`trustAs`, `getTrusted` and `valueOf`) to replace the way things
13192 * work because `$sce` delegates to `$sceDelegate` for these operations.
13194 * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} to configure this service.
13196 * The default instance of `$sceDelegate` should work out of the box with little pain. While you
13197 * can override it completely to change the behavior of `$sce`, the common case would
13198 * involve configuring the {@link ng.$sceDelegateProvider $sceDelegateProvider} instead by setting
13199 * your own whitelists and blacklists for trusting URLs used for loading AngularJS resources such as
13200 * templates. Refer {@link ng.$sceDelegateProvider#resourceUrlWhitelist
13201 * $sceDelegateProvider.resourceUrlWhitelist} and {@link
13202 * ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist}
13207 * @name $sceDelegateProvider
13210 * The `$sceDelegateProvider` provider allows developers to configure the {@link ng.$sceDelegate
13211 * $sceDelegate} service. This allows one to get/set the whitelists and blacklists used to ensure
13212 * that the URLs used for sourcing Angular templates are safe. Refer {@link
13213 * ng.$sceDelegateProvider#resourceUrlWhitelist $sceDelegateProvider.resourceUrlWhitelist} and
13214 * {@link ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist}
13216 * For the general details about this service in Angular, read the main page for {@link ng.$sce
13217 * Strict Contextual Escaping (SCE)}.
13219 * **Example**: Consider the following case. <a name="example"></a>
13221 * - your app is hosted at url `http://myapp.example.com/`
13222 * - but some of your templates are hosted on other domains you control such as
13223 * `http://srv01.assets.example.com/`, `http://srv02.assets.example.com/`, etc.
13224 * - and you have an open redirect at `http://myapp.example.com/clickThru?...`.
13226 * Here is what a secure configuration for this scenario might look like:
13229 * angular.module('myApp', []).config(function($sceDelegateProvider) {
13230 * $sceDelegateProvider.resourceUrlWhitelist([
13231 * // Allow same origin resource loads.
13233 * // Allow loading from our assets domain. Notice the difference between * and **.
13234 * 'http://srv*.assets.example.com/**'
13237 * // The blacklist overrides the whitelist so the open redirect here is blocked.
13238 * $sceDelegateProvider.resourceUrlBlacklist([
13239 * 'http://myapp.example.com/clickThru**'
13245 function $SceDelegateProvider() {
13246 this.SCE_CONTEXTS = SCE_CONTEXTS;
13248 // Resource URLs can also be trusted by policy.
13249 var resourceUrlWhitelist = ['self'],
13250 resourceUrlBlacklist = [];
13254 * @name $sceDelegateProvider#resourceUrlWhitelist
13257 * @param {Array=} whitelist When provided, replaces the resourceUrlWhitelist with the value
13258 * provided. This must be an array or null. A snapshot of this array is used so further
13259 * changes to the array are ignored.
13261 * Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
13262 * allowed in this array.
13264 * Note: **an empty whitelist array will block all URLs**!
13266 * @return {Array} the currently set whitelist array.
13268 * The **default value** when no whitelist has been explicitly set is `['self']` allowing only
13269 * same origin resource requests.
13272 * Sets/Gets the whitelist of trusted resource URLs.
13274 this.resourceUrlWhitelist = function (value) {
13275 if (arguments.length) {
13276 resourceUrlWhitelist = adjustMatchers(value);
13278 return resourceUrlWhitelist;
13283 * @name $sceDelegateProvider#resourceUrlBlacklist
13286 * @param {Array=} blacklist When provided, replaces the resourceUrlBlacklist with the value
13287 * provided. This must be an array or null. A snapshot of this array is used so further
13288 * changes to the array are ignored.
13290 * Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
13291 * allowed in this array.
13293 * The typical usage for the blacklist is to **block
13294 * [open redirects](http://cwe.mitre.org/data/definitions/601.html)** served by your domain as
13295 * these would otherwise be trusted but actually return content from the redirected domain.
13297 * Finally, **the blacklist overrides the whitelist** and has the final say.
13299 * @return {Array} the currently set blacklist array.
13301 * The **default value** when no whitelist has been explicitly set is the empty array (i.e. there
13302 * is no blacklist.)
13305 * Sets/Gets the blacklist of trusted resource URLs.
13308 this.resourceUrlBlacklist = function (value) {
13309 if (arguments.length) {
13310 resourceUrlBlacklist = adjustMatchers(value);
13312 return resourceUrlBlacklist;
13315 this.$get = ['$injector', function($injector) {
13317 var htmlSanitizer = function htmlSanitizer(html) {
13318 throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.');
13321 if ($injector.has('$sanitize')) {
13322 htmlSanitizer = $injector.get('$sanitize');
13326 function matchUrl(matcher, parsedUrl) {
13327 if (matcher === 'self') {
13328 return urlIsSameOrigin(parsedUrl);
13330 // definitely a regex. See adjustMatchers()
13331 return !!matcher.exec(parsedUrl.href);
13335 function isResourceUrlAllowedByPolicy(url) {
13336 var parsedUrl = urlResolve(url.toString());
13337 var i, n, allowed = false;
13338 // Ensure that at least one item from the whitelist allows this url.
13339 for (i = 0, n = resourceUrlWhitelist.length; i < n; i++) {
13340 if (matchUrl(resourceUrlWhitelist[i], parsedUrl)) {
13346 // Ensure that no item from the blacklist blocked this url.
13347 for (i = 0, n = resourceUrlBlacklist.length; i < n; i++) {
13348 if (matchUrl(resourceUrlBlacklist[i], parsedUrl)) {
13357 function generateHolderType(Base) {
13358 var holderType = function TrustedValueHolderType(trustedValue) {
13359 this.$$unwrapTrustedValue = function() {
13360 return trustedValue;
13364 holderType.prototype = new Base();
13366 holderType.prototype.valueOf = function sceValueOf() {
13367 return this.$$unwrapTrustedValue();
13369 holderType.prototype.toString = function sceToString() {
13370 return this.$$unwrapTrustedValue().toString();
13375 var trustedValueHolderBase = generateHolderType(),
13378 byType[SCE_CONTEXTS.HTML] = generateHolderType(trustedValueHolderBase);
13379 byType[SCE_CONTEXTS.CSS] = generateHolderType(trustedValueHolderBase);
13380 byType[SCE_CONTEXTS.URL] = generateHolderType(trustedValueHolderBase);
13381 byType[SCE_CONTEXTS.JS] = generateHolderType(trustedValueHolderBase);
13382 byType[SCE_CONTEXTS.RESOURCE_URL] = generateHolderType(byType[SCE_CONTEXTS.URL]);
13386 * @name $sceDelegate#trustAs
13389 * Returns an object that is trusted by angular for use in specified strict
13390 * contextual escaping contexts (such as ng-bind-html, ng-include, any src
13391 * attribute interpolation, any dom event binding attribute interpolation
13392 * such as for onclick, etc.) that uses the provided value.
13393 * See {@link ng.$sce $sce} for enabling strict contextual escaping.
13395 * @param {string} type The kind of context in which this value is safe for use. e.g. url,
13396 * resourceUrl, html, js and css.
13397 * @param {*} value The value that that should be considered trusted/safe.
13398 * @returns {*} A value that can be used to stand in for the provided `value` in places
13399 * where Angular expects a $sce.trustAs() return value.
13401 function trustAs(type, trustedValue) {
13402 var Constructor = (byType.hasOwnProperty(type) ? byType[type] : null);
13403 if (!Constructor) {
13404 throw $sceMinErr('icontext',
13405 'Attempted to trust a value in invalid context. Context: {0}; Value: {1}',
13406 type, trustedValue);
13408 if (trustedValue === null || trustedValue === undefined || trustedValue === '') {
13409 return trustedValue;
13411 // All the current contexts in SCE_CONTEXTS happen to be strings. In order to avoid trusting
13412 // mutable objects, we ensure here that the value passed in is actually a string.
13413 if (typeof trustedValue !== 'string') {
13414 throw $sceMinErr('itype',
13415 'Attempted to trust a non-string value in a content requiring a string: Context: {0}',
13418 return new Constructor(trustedValue);
13423 * @name $sceDelegate#valueOf
13426 * If the passed parameter had been returned by a prior call to {@link ng.$sceDelegate#trustAs
13427 * `$sceDelegate.trustAs`}, returns the value that had been passed to {@link
13428 * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}.
13430 * If the passed parameter is not a value that had been returned by {@link
13431 * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}, returns it as-is.
13433 * @param {*} value The result of a prior {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}
13434 * call or anything else.
13435 * @returns {*} The `value` that was originally provided to {@link ng.$sceDelegate#trustAs
13436 * `$sceDelegate.trustAs`} if `value` is the result of such a call. Otherwise, returns
13437 * `value` unchanged.
13439 function valueOf(maybeTrusted) {
13440 if (maybeTrusted instanceof trustedValueHolderBase) {
13441 return maybeTrusted.$$unwrapTrustedValue();
13443 return maybeTrusted;
13449 * @name $sceDelegate#getTrusted
13452 * Takes the result of a {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`} call and
13453 * returns the originally supplied value if the queried context type is a supertype of the
13454 * created type. If this condition isn't satisfied, throws an exception.
13456 * @param {string} type The kind of context in which this value is to be used.
13457 * @param {*} maybeTrusted The result of a prior {@link ng.$sceDelegate#trustAs
13458 * `$sceDelegate.trustAs`} call.
13459 * @returns {*} The value the was originally provided to {@link ng.$sceDelegate#trustAs
13460 * `$sceDelegate.trustAs`} if valid in this context. Otherwise, throws an exception.
13462 function getTrusted(type, maybeTrusted) {
13463 if (maybeTrusted === null || maybeTrusted === undefined || maybeTrusted === '') {
13464 return maybeTrusted;
13466 var constructor = (byType.hasOwnProperty(type) ? byType[type] : null);
13467 if (constructor && maybeTrusted instanceof constructor) {
13468 return maybeTrusted.$$unwrapTrustedValue();
13470 // If we get here, then we may only take one of two actions.
13471 // 1. sanitize the value for the requested type, or
13472 // 2. throw an exception.
13473 if (type === SCE_CONTEXTS.RESOURCE_URL) {
13474 if (isResourceUrlAllowedByPolicy(maybeTrusted)) {
13475 return maybeTrusted;
13477 throw $sceMinErr('insecurl',
13478 'Blocked loading resource from url not allowed by $sceDelegate policy. URL: {0}',
13479 maybeTrusted.toString());
13481 } else if (type === SCE_CONTEXTS.HTML) {
13482 return htmlSanitizer(maybeTrusted);
13484 throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.');
13487 return { trustAs: trustAs,
13488 getTrusted: getTrusted,
13489 valueOf: valueOf };
13496 * @name $sceProvider
13499 * The $sceProvider provider allows developers to configure the {@link ng.$sce $sce} service.
13500 * - enable/disable Strict Contextual Escaping (SCE) in a module
13501 * - override the default implementation with a custom delegate
13503 * Read more about {@link ng.$sce Strict Contextual Escaping (SCE)}.
13506 /* jshint maxlen: false*/
13515 * `$sce` is a service that provides Strict Contextual Escaping services to AngularJS.
13517 * # Strict Contextual Escaping
13519 * Strict Contextual Escaping (SCE) is a mode in which AngularJS requires bindings in certain
13520 * contexts to result in a value that is marked as safe to use for that context. One example of
13521 * such a context is binding arbitrary html controlled by the user via `ng-bind-html`. We refer
13522 * to these contexts as privileged or SCE contexts.
13524 * As of version 1.2, Angular ships with SCE enabled by default.
13526 * Note: When enabled (the default), IE8 in quirks mode is not supported. In this mode, IE8 allows
13527 * one to execute arbitrary javascript by the use of the expression() syntax. Refer
13528 * <http://blogs.msdn.com/b/ie/archive/2008/10/16/ending-expressions.aspx> to learn more about them.
13529 * You can ensure your document is in standards mode and not quirks mode by adding `<!doctype html>`
13530 * to the top of your HTML document.
13532 * SCE assists in writing code in way that (a) is secure by default and (b) makes auditing for
13533 * security vulnerabilities such as XSS, clickjacking, etc. a lot easier.
13535 * Here's an example of a binding in a privileged context:
13538 * <input ng-model="userHtml">
13539 * <div ng-bind-html="userHtml"></div>
13542 * Notice that `ng-bind-html` is bound to `userHtml` controlled by the user. With SCE
13543 * disabled, this application allows the user to render arbitrary HTML into the DIV.
13544 * In a more realistic example, one may be rendering user comments, blog articles, etc. via
13545 * bindings. (HTML is just one example of a context where rendering user controlled input creates
13546 * security vulnerabilities.)
13548 * For the case of HTML, you might use a library, either on the client side, or on the server side,
13549 * to sanitize unsafe HTML before binding to the value and rendering it in the document.
13551 * How would you ensure that every place that used these types of bindings was bound to a value that
13552 * was sanitized by your library (or returned as safe for rendering by your server?) How can you
13553 * ensure that you didn't accidentally delete the line that sanitized the value, or renamed some
13554 * properties/fields and forgot to update the binding to the sanitized value?
13556 * To be secure by default, you want to ensure that any such bindings are disallowed unless you can
13557 * determine that something explicitly says it's safe to use a value for binding in that
13558 * context. You can then audit your code (a simple grep would do) to ensure that this is only done
13559 * for those values that you can easily tell are safe - because they were received from your server,
13560 * sanitized by your library, etc. You can organize your codebase to help with this - perhaps
13561 * allowing only the files in a specific directory to do this. Ensuring that the internal API
13562 * exposed by that code doesn't markup arbitrary values as safe then becomes a more manageable task.
13564 * In the case of AngularJS' SCE service, one uses {@link ng.$sce#trustAs $sce.trustAs}
13565 * (and shorthand methods such as {@link ng.$sce#trustAsHtml $sce.trustAsHtml}, etc.) to
13566 * obtain values that will be accepted by SCE / privileged contexts.
13569 * ## How does it work?
13571 * In privileged contexts, directives and code will bind to the result of {@link ng.$sce#getTrusted
13572 * $sce.getTrusted(context, value)} rather than to the value directly. Directives use {@link
13573 * ng.$sce#parse $sce.parseAs} rather than `$parse` to watch attribute bindings, which performs the
13574 * {@link ng.$sce#getTrusted $sce.getTrusted} behind the scenes on non-constant literals.
13576 * As an example, {@link ng.directive:ngBindHtml ngBindHtml} uses {@link
13577 * ng.$sce#parseAsHtml $sce.parseAsHtml(binding expression)}. Here's the actual code (slightly
13581 * var ngBindHtmlDirective = ['$sce', function($sce) {
13582 * return function(scope, element, attr) {
13583 * scope.$watch($sce.parseAsHtml(attr.ngBindHtml), function(value) {
13584 * element.html(value || '');
13590 * ## Impact on loading templates
13592 * This applies both to the {@link ng.directive:ngInclude `ng-include`} directive as well as
13593 * `templateUrl`'s specified by {@link guide/directive directives}.
13595 * By default, Angular only loads templates from the same domain and protocol as the application
13596 * document. This is done by calling {@link ng.$sce#getTrustedResourceUrl
13597 * $sce.getTrustedResourceUrl} on the template URL. To load templates from other domains and/or
13598 * protocols, you may either either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist
13599 * them} or {@link ng.$sce#trustAsResourceUrl wrap it} into a trusted value.
13603 * [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest)
13604 * and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/)
13605 * policy apply in addition to this and may further restrict whether the template is successfully
13606 * loaded. This means that without the right CORS policy, loading templates from a different domain
13607 * won't work on all browsers. Also, loading templates from `file://` URL does not work on some
13610 * ## This feels like too much overhead for the developer?
13612 * It's important to remember that SCE only applies to interpolation expressions.
13614 * If your expressions are constant literals, they're automatically trusted and you don't need to
13615 * call `$sce.trustAs` on them (remember to include the `ngSanitize` module) (e.g.
13616 * `<div ng-bind-html="'<b>implicitly trusted</b>'"></div>`) just works.
13618 * Additionally, `a[href]` and `img[src]` automatically sanitize their URLs and do not pass them
13619 * through {@link ng.$sce#getTrusted $sce.getTrusted}. SCE doesn't play a role here.
13621 * The included {@link ng.$sceDelegate $sceDelegate} comes with sane defaults to allow you to load
13622 * templates in `ng-include` from your application's domain without having to even know about SCE.
13623 * It blocks loading templates from other domains or loading templates over http from an https
13624 * served document. You can change these by setting your own custom {@link
13625 * ng.$sceDelegateProvider#resourceUrlWhitelist whitelists} and {@link
13626 * ng.$sceDelegateProvider#resourceUrlBlacklist blacklists} for matching such URLs.
13628 * This significantly reduces the overhead. It is far easier to pay the small overhead and have an
13629 * application that's secure and can be audited to verify that with much more ease than bolting
13630 * security onto an application later.
13632 * <a name="contexts"></a>
13633 * ## What trusted context types are supported?
13635 * | Context | Notes |
13636 * |---------------------|----------------|
13637 * | `$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. |
13638 * | `$sce.CSS` | For CSS that's safe to source into the application. Currently unused. Feel free to use it in your own directives. |
13639 * | `$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. |
13640 * | `$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. |
13641 * | `$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. |
13643 * ## Format of items in {@link ng.$sceDelegateProvider#resourceUrlWhitelist resourceUrlWhitelist}/{@link ng.$sceDelegateProvider#resourceUrlBlacklist Blacklist} <a name="resourceUrlPatternItem"></a>
13645 * Each element in these arrays must be one of the following:
13648 * - The special **string**, `'self'`, can be used to match against all URLs of the **same
13649 * domain** as the application document using the **same protocol**.
13650 * - **String** (except the special value `'self'`)
13651 * - The string is matched against the full *normalized / absolute URL* of the resource
13652 * being tested (substring matches are not good enough.)
13653 * - There are exactly **two wildcard sequences** - `*` and `**`. All other characters
13654 * match themselves.
13655 * - `*`: matches zero or more occurrences of any character other than one of the following 6
13656 * characters: '`:`', '`/`', '`.`', '`?`', '`&`' and ';'. It's a useful wildcard for use
13658 * - `**`: matches zero or more occurrences of *any* character. As such, it's not
13659 * not appropriate to use in for a scheme, domain, etc. as it would match too much. (e.g.
13660 * http://**.example.com/ would match http://evil.com/?ignore=.example.com/ and that might
13661 * not have been the intention.) Its usage at the very end of the path is ok. (e.g.
13662 * http://foo.example.com/templates/**).
13663 * - **RegExp** (*see caveat below*)
13664 * - *Caveat*: While regular expressions are powerful and offer great flexibility, their syntax
13665 * (and all the inevitable escaping) makes them *harder to maintain*. It's easy to
13666 * accidentally introduce a bug when one updates a complex expression (imho, all regexes should
13667 * have good test coverage.). For instance, the use of `.` in the regex is correct only in a
13668 * small number of cases. A `.` character in the regex used when matching the scheme or a
13669 * subdomain could be matched against a `:` or literal `.` that was likely not intended. It
13670 * is highly recommended to use the string patterns and only fall back to regular expressions
13671 * if they as a last resort.
13672 * - The regular expression must be an instance of RegExp (i.e. not a string.) It is
13673 * matched against the **entire** *normalized / absolute URL* of the resource being tested
13674 * (even when the RegExp did not have the `^` and `$` codes.) In addition, any flags
13675 * present on the RegExp (such as multiline, global, ignoreCase) are ignored.
13676 * - If you are generating your JavaScript from some other templating engine (not
13677 * recommended, e.g. in issue [#4006](https://github.com/angular/angular.js/issues/4006)),
13678 * remember to escape your regular expression (and be aware that you might need more than
13679 * one level of escaping depending on your templating engine and the way you interpolated
13680 * the value.) Do make use of your platform's escaping mechanism as it might be good
13681 * enough before coding your own. e.g. Ruby has
13682 * [Regexp.escape(str)](http://www.ruby-doc.org/core-2.0.0/Regexp.html#method-c-escape)
13683 * and Python has [re.escape](http://docs.python.org/library/re.html#re.escape).
13684 * Javascript lacks a similar built in function for escaping. Take a look at Google
13685 * Closure library's [goog.string.regExpEscape(s)](
13686 * http://docs.closure-library.googlecode.com/git/closure_goog_string_string.js.source.html#line962).
13688 * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} for an example.
13690 * ## Show me an example using SCE.
13692 * <example module="mySceApp" deps="angular-sanitize.js">
13693 * <file name="index.html">
13694 * <div ng-controller="myAppController as myCtrl">
13695 * <i ng-bind-html="myCtrl.explicitlyTrustedHtml" id="explicitlyTrustedHtml"></i><br><br>
13696 * <b>User comments</b><br>
13697 * By default, HTML that isn't explicitly trusted (e.g. Alice's comment) is sanitized when
13698 * $sanitize is available. If $sanitize isn't available, this results in an error instead of an
13700 * <div class="well">
13701 * <div ng-repeat="userComment in myCtrl.userComments">
13702 * <b>{{userComment.name}}</b>:
13703 * <span ng-bind-html="userComment.htmlComment" class="htmlComment"></span>
13710 * <file name="script.js">
13711 * var mySceApp = angular.module('mySceApp', ['ngSanitize']);
13713 * mySceApp.controller("myAppController", function myAppController($http, $templateCache, $sce) {
13715 * $http.get("test_data.json", {cache: $templateCache}).success(function(userComments) {
13716 * self.userComments = userComments;
13718 * self.explicitlyTrustedHtml = $sce.trustAsHtml(
13719 * '<span onmouseover="this.textContent="Explicitly trusted HTML bypasses ' +
13720 * 'sanitization."">Hover over this text.</span>');
13724 * <file name="test_data.json">
13726 * { "name": "Alice",
13728 * "<span onmouseover='this.textContent=\"PWN3D!\"'>Is <i>anyone</i> reading this?</span>"
13731 * "htmlComment": "<i>Yes!</i> Am I the only other one?"
13736 * <file name="protractor.js" type="protractor">
13737 * describe('SCE doc demo', function() {
13738 * it('should sanitize untrusted values', function() {
13739 * expect(element.all(by.css('.htmlComment')).first().getInnerHtml())
13740 * .toBe('<span>Is <i>anyone</i> reading this?</span>');
13743 * it('should NOT sanitize explicitly trusted values', function() {
13744 * expect(element(by.id('explicitlyTrustedHtml')).getInnerHtml()).toBe(
13745 * '<span onmouseover="this.textContent="Explicitly trusted HTML bypasses ' +
13746 * 'sanitization."">Hover over this text.</span>');
13754 * ## Can I disable SCE completely?
13756 * Yes, you can. However, this is strongly discouraged. SCE gives you a lot of security benefits
13757 * for little coding overhead. It will be much harder to take an SCE disabled application and
13758 * either secure it on your own or enable SCE at a later stage. It might make sense to disable SCE
13759 * for cases where you have a lot of existing code that was written before SCE was introduced and
13760 * you're migrating them a module at a time.
13762 * That said, here's how you can completely disable SCE:
13765 * angular.module('myAppWithSceDisabledmyApp', []).config(function($sceProvider) {
13766 * // Completely disable SCE. For demonstration purposes only!
13767 * // Do not use in new projects.
13768 * $sceProvider.enabled(false);
13773 /* jshint maxlen: 100 */
13775 function $SceProvider() {
13776 var enabled = true;
13780 * @name $sceProvider#enabled
13783 * @param {boolean=} value If provided, then enables/disables SCE.
13784 * @return {boolean} true if SCE is enabled, false otherwise.
13787 * Enables/disables SCE and returns the current value.
13789 this.enabled = function (value) {
13790 if (arguments.length) {
13797 /* Design notes on the default implementation for SCE.
13799 * The API contract for the SCE delegate
13800 * -------------------------------------
13801 * The SCE delegate object must provide the following 3 methods:
13803 * - trustAs(contextEnum, value)
13804 * This method is used to tell the SCE service that the provided value is OK to use in the
13805 * contexts specified by contextEnum. It must return an object that will be accepted by
13806 * getTrusted() for a compatible contextEnum and return this value.
13809 * For values that were not produced by trustAs(), return them as is. For values that were
13810 * produced by trustAs(), return the corresponding input value to trustAs. Basically, if
13811 * trustAs is wrapping the given values into some type, this operation unwraps it when given
13814 * - getTrusted(contextEnum, value)
13815 * This function should return the a value that is safe to use in the context specified by
13816 * contextEnum or throw and exception otherwise.
13818 * NOTE: This contract deliberately does NOT state that values returned by trustAs() must be
13819 * opaque or wrapped in some holder object. That happens to be an implementation detail. For
13820 * instance, an implementation could maintain a registry of all trusted objects by context. In
13821 * such a case, trustAs() would return the same object that was passed in. getTrusted() would
13822 * return the same object passed in if it was found in the registry under a compatible context or
13823 * throw an exception otherwise. An implementation might only wrap values some of the time based
13824 * on some criteria. getTrusted() might return a value and not throw an exception for special
13825 * constants or objects even if not wrapped. All such implementations fulfill this contract.
13828 * A note on the inheritance model for SCE contexts
13829 * ------------------------------------------------
13830 * I've used inheritance and made RESOURCE_URL wrapped types a subtype of URL wrapped types. This
13831 * is purely an implementation details.
13833 * The contract is simply this:
13835 * getTrusted($sce.RESOURCE_URL, value) succeeding implies that getTrusted($sce.URL, value)
13836 * will also succeed.
13838 * Inheritance happens to capture this in a natural way. In some future, we
13839 * may not use inheritance anymore. That is OK because no code outside of
13840 * sce.js and sceSpecs.js would need to be aware of this detail.
13843 this.$get = ['$parse', '$sniffer', '$sceDelegate', function(
13844 $parse, $sniffer, $sceDelegate) {
13845 // Prereq: Ensure that we're not running in IE8 quirks mode. In that mode, IE allows
13846 // the "expression(javascript expression)" syntax which is insecure.
13847 if (enabled && $sniffer.msie && $sniffer.msieDocumentMode < 8) {
13848 throw $sceMinErr('iequirks',
13849 'Strict Contextual Escaping does not support Internet Explorer version < 9 in quirks ' +
13850 'mode. You can fix this by adding the text <!doctype html> to the top of your HTML ' +
13851 'document. See http://docs.angularjs.org/api/ng.$sce for more information.');
13854 var sce = shallowCopy(SCE_CONTEXTS);
13858 * @name $sce#isEnabled
13861 * @return {Boolean} true if SCE is enabled, false otherwise. If you want to set the value, you
13862 * have to do it at module config time on {@link ng.$sceProvider $sceProvider}.
13865 * Returns a boolean indicating if SCE is enabled.
13867 sce.isEnabled = function () {
13870 sce.trustAs = $sceDelegate.trustAs;
13871 sce.getTrusted = $sceDelegate.getTrusted;
13872 sce.valueOf = $sceDelegate.valueOf;
13875 sce.trustAs = sce.getTrusted = function(type, value) { return value; };
13876 sce.valueOf = identity;
13881 * @name $sce#parseAs
13884 * Converts Angular {@link guide/expression expression} into a function. This is like {@link
13885 * ng.$parse $parse} and is identical when the expression is a literal constant. Otherwise, it
13886 * wraps the expression in a call to {@link ng.$sce#getTrusted $sce.getTrusted(*type*,
13889 * @param {string} type The kind of SCE context in which this result will be used.
13890 * @param {string} expression String expression to compile.
13891 * @returns {function(context, locals)} a function which represents the compiled expression:
13893 * * `context` – `{object}` – an object against which any expressions embedded in the strings
13894 * are evaluated against (typically a scope object).
13895 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
13898 sce.parseAs = function sceParseAs(type, expr) {
13899 var parsed = $parse(expr);
13900 if (parsed.literal && parsed.constant) {
13903 return function sceParseAsTrusted(self, locals) {
13904 return sce.getTrusted(type, parsed(self, locals));
13911 * @name $sce#trustAs
13914 * Delegates to {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}. As such,
13915 * returns an object that is trusted by angular for use in specified strict contextual
13916 * escaping contexts (such as ng-bind-html, ng-include, any src attribute
13917 * interpolation, any dom event binding attribute interpolation such as for onclick, etc.)
13918 * that uses the provided value. See * {@link ng.$sce $sce} for enabling strict contextual
13921 * @param {string} type The kind of context in which this value is safe for use. e.g. url,
13922 * resource_url, html, js and css.
13923 * @param {*} value The value that that should be considered trusted/safe.
13924 * @returns {*} A value that can be used to stand in for the provided `value` in places
13925 * where Angular expects a $sce.trustAs() return value.
13930 * @name $sce#trustAsHtml
13933 * Shorthand method. `$sce.trustAsHtml(value)` →
13934 * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.HTML, value)`}
13936 * @param {*} value The value to trustAs.
13937 * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedHtml
13938 * $sce.getTrustedHtml(value)} to obtain the original value. (privileged directives
13939 * only accept expressions that are either literal constants or are the
13940 * return value of {@link ng.$sce#trustAs $sce.trustAs}.)
13945 * @name $sce#trustAsUrl
13948 * Shorthand method. `$sce.trustAsUrl(value)` →
13949 * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.URL, value)`}
13951 * @param {*} value The value to trustAs.
13952 * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedUrl
13953 * $sce.getTrustedUrl(value)} to obtain the original value. (privileged directives
13954 * only accept expressions that are either literal constants or are the
13955 * return value of {@link ng.$sce#trustAs $sce.trustAs}.)
13960 * @name $sce#trustAsResourceUrl
13963 * Shorthand method. `$sce.trustAsResourceUrl(value)` →
13964 * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.RESOURCE_URL, value)`}
13966 * @param {*} value The value to trustAs.
13967 * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedResourceUrl
13968 * $sce.getTrustedResourceUrl(value)} to obtain the original value. (privileged directives
13969 * only accept expressions that are either literal constants or are the return
13970 * value of {@link ng.$sce#trustAs $sce.trustAs}.)
13975 * @name $sce#trustAsJs
13978 * Shorthand method. `$sce.trustAsJs(value)` →
13979 * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.JS, value)`}
13981 * @param {*} value The value to trustAs.
13982 * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedJs
13983 * $sce.getTrustedJs(value)} to obtain the original value. (privileged directives
13984 * only accept expressions that are either literal constants or are the
13985 * return value of {@link ng.$sce#trustAs $sce.trustAs}.)
13990 * @name $sce#getTrusted
13993 * Delegates to {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted`}. As such,
13994 * takes the result of a {@link ng.$sce#trustAs `$sce.trustAs`}() call and returns the
13995 * originally supplied value if the queried context type is a supertype of the created type.
13996 * If this condition isn't satisfied, throws an exception.
13998 * @param {string} type The kind of context in which this value is to be used.
13999 * @param {*} maybeTrusted The result of a prior {@link ng.$sce#trustAs `$sce.trustAs`}
14001 * @returns {*} The value the was originally provided to
14002 * {@link ng.$sce#trustAs `$sce.trustAs`} if valid in this context.
14003 * Otherwise, throws an exception.
14008 * @name $sce#getTrustedHtml
14011 * Shorthand method. `$sce.getTrustedHtml(value)` →
14012 * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.HTML, value)`}
14014 * @param {*} value The value to pass to `$sce.getTrusted`.
14015 * @returns {*} The return value of `$sce.getTrusted($sce.HTML, value)`
14020 * @name $sce#getTrustedCss
14023 * Shorthand method. `$sce.getTrustedCss(value)` →
14024 * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.CSS, value)`}
14026 * @param {*} value The value to pass to `$sce.getTrusted`.
14027 * @returns {*} The return value of `$sce.getTrusted($sce.CSS, value)`
14032 * @name $sce#getTrustedUrl
14035 * Shorthand method. `$sce.getTrustedUrl(value)` →
14036 * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.URL, value)`}
14038 * @param {*} value The value to pass to `$sce.getTrusted`.
14039 * @returns {*} The return value of `$sce.getTrusted($sce.URL, value)`
14044 * @name $sce#getTrustedResourceUrl
14047 * Shorthand method. `$sce.getTrustedResourceUrl(value)` →
14048 * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.RESOURCE_URL, value)`}
14050 * @param {*} value The value to pass to `$sceDelegate.getTrusted`.
14051 * @returns {*} The return value of `$sce.getTrusted($sce.RESOURCE_URL, value)`
14056 * @name $sce#getTrustedJs
14059 * Shorthand method. `$sce.getTrustedJs(value)` →
14060 * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.JS, value)`}
14062 * @param {*} value The value to pass to `$sce.getTrusted`.
14063 * @returns {*} The return value of `$sce.getTrusted($sce.JS, value)`
14068 * @name $sce#parseAsHtml
14071 * Shorthand method. `$sce.parseAsHtml(expression string)` →
14072 * {@link ng.$sce#parse `$sce.parseAs($sce.HTML, value)`}
14074 * @param {string} expression String expression to compile.
14075 * @returns {function(context, locals)} a function which represents the compiled expression:
14077 * * `context` – `{object}` – an object against which any expressions embedded in the strings
14078 * are evaluated against (typically a scope object).
14079 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
14085 * @name $sce#parseAsCss
14088 * Shorthand method. `$sce.parseAsCss(value)` →
14089 * {@link ng.$sce#parse `$sce.parseAs($sce.CSS, value)`}
14091 * @param {string} expression String expression to compile.
14092 * @returns {function(context, locals)} a function which represents the compiled expression:
14094 * * `context` – `{object}` – an object against which any expressions embedded in the strings
14095 * are evaluated against (typically a scope object).
14096 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
14102 * @name $sce#parseAsUrl
14105 * Shorthand method. `$sce.parseAsUrl(value)` →
14106 * {@link ng.$sce#parse `$sce.parseAs($sce.URL, value)`}
14108 * @param {string} expression String expression to compile.
14109 * @returns {function(context, locals)} a function which represents the compiled expression:
14111 * * `context` – `{object}` – an object against which any expressions embedded in the strings
14112 * are evaluated against (typically a scope object).
14113 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
14119 * @name $sce#parseAsResourceUrl
14122 * Shorthand method. `$sce.parseAsResourceUrl(value)` →
14123 * {@link ng.$sce#parse `$sce.parseAs($sce.RESOURCE_URL, value)`}
14125 * @param {string} expression String expression to compile.
14126 * @returns {function(context, locals)} a function which represents the compiled expression:
14128 * * `context` – `{object}` – an object against which any expressions embedded in the strings
14129 * are evaluated against (typically a scope object).
14130 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
14136 * @name $sce#parseAsJs
14139 * Shorthand method. `$sce.parseAsJs(value)` →
14140 * {@link ng.$sce#parse `$sce.parseAs($sce.JS, value)`}
14142 * @param {string} expression String expression to compile.
14143 * @returns {function(context, locals)} a function which represents the compiled expression:
14145 * * `context` – `{object}` – an object against which any expressions embedded in the strings
14146 * are evaluated against (typically a scope object).
14147 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
14151 // Shorthand delegations.
14152 var parse = sce.parseAs,
14153 getTrusted = sce.getTrusted,
14154 trustAs = sce.trustAs;
14156 forEach(SCE_CONTEXTS, function (enumValue, name) {
14157 var lName = lowercase(name);
14158 sce[camelCase("parse_as_" + lName)] = function (expr) {
14159 return parse(enumValue, expr);
14161 sce[camelCase("get_trusted_" + lName)] = function (value) {
14162 return getTrusted(enumValue, value);
14164 sce[camelCase("trust_as_" + lName)] = function (value) {
14165 return trustAs(enumValue, value);
14174 * !!! This is an undocumented "private" service !!!
14177 * @requires $window
14178 * @requires $document
14180 * @property {boolean} history Does the browser support html5 history api ?
14181 * @property {boolean} hashchange Does the browser support hashchange event ?
14182 * @property {boolean} transitions Does the browser support CSS transition events ?
14183 * @property {boolean} animations Does the browser support CSS animation events ?
14186 * This is very simple implementation of testing browser's features.
14188 function $SnifferProvider() {
14189 this.$get = ['$window', '$document', function($window, $document) {
14190 var eventSupport = {},
14192 int((/android (\d+)/.exec(lowercase(($window.navigator || {}).userAgent)) || [])[1]),
14193 boxee = /Boxee/i.test(($window.navigator || {}).userAgent),
14194 document = $document[0] || {},
14195 documentMode = document.documentMode,
14197 vendorRegex = /^(Moz|webkit|O|ms)(?=[A-Z])/,
14198 bodyStyle = document.body && document.body.style,
14199 transitions = false,
14200 animations = false,
14204 for(var prop in bodyStyle) {
14205 if(match = vendorRegex.exec(prop)) {
14206 vendorPrefix = match[0];
14207 vendorPrefix = vendorPrefix.substr(0, 1).toUpperCase() + vendorPrefix.substr(1);
14212 if(!vendorPrefix) {
14213 vendorPrefix = ('WebkitOpacity' in bodyStyle) && 'webkit';
14216 transitions = !!(('transition' in bodyStyle) || (vendorPrefix + 'Transition' in bodyStyle));
14217 animations = !!(('animation' in bodyStyle) || (vendorPrefix + 'Animation' in bodyStyle));
14219 if (android && (!transitions||!animations)) {
14220 transitions = isString(document.body.style.webkitTransition);
14221 animations = isString(document.body.style.webkitAnimation);
14227 // Android has history.pushState, but it does not update location correctly
14228 // so let's not use the history API at all.
14229 // http://code.google.com/p/android/issues/detail?id=17471
14230 // https://github.com/angular/angular.js/issues/904
14232 // older webkit browser (533.9) on Boxee box has exactly the same problem as Android has
14233 // so let's not use the history API also
14234 // We are purposefully using `!(android < 4)` to cover the case when `android` is undefined
14236 history: !!($window.history && $window.history.pushState && !(android < 4) && !boxee),
14238 hashchange: 'onhashchange' in $window &&
14239 // IE8 compatible mode lies
14240 (!documentMode || documentMode > 7),
14241 hasEvent: function(event) {
14242 // IE9 implements 'input' event it's so fubared that we rather pretend that it doesn't have
14243 // it. In particular the event is not fired when backspace or delete key are pressed or
14244 // when cut operation is performed.
14245 if (event == 'input' && msie == 9) return false;
14247 if (isUndefined(eventSupport[event])) {
14248 var divElm = document.createElement('div');
14249 eventSupport[event] = 'on' + event in divElm;
14252 return eventSupport[event];
14255 vendorPrefix: vendorPrefix,
14256 transitions : transitions,
14257 animations : animations,
14260 msieDocumentMode: documentMode
14265 function $TimeoutProvider() {
14266 this.$get = ['$rootScope', '$browser', '$q', '$exceptionHandler',
14267 function($rootScope, $browser, $q, $exceptionHandler) {
14268 var deferreds = {};
14276 * Angular's wrapper for `window.setTimeout`. The `fn` function is wrapped into a try/catch
14277 * block and delegates any exceptions to
14278 * {@link ng.$exceptionHandler $exceptionHandler} service.
14280 * The return value of registering a timeout function is a promise, which will be resolved when
14281 * the timeout is reached and the timeout function is executed.
14283 * To cancel a timeout request, call `$timeout.cancel(promise)`.
14285 * In tests you can use {@link ngMock.$timeout `$timeout.flush()`} to
14286 * synchronously flush the queue of deferred functions.
14288 * @param {function()} fn A function, whose execution should be delayed.
14289 * @param {number=} [delay=0] Delay in milliseconds.
14290 * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
14291 * will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
14292 * @returns {Promise} Promise that will be resolved when the timeout is reached. The value this
14293 * promise will be resolved with is the return value of the `fn` function.
14296 function timeout(fn, delay, invokeApply) {
14297 var deferred = $q.defer(),
14298 promise = deferred.promise,
14299 skipApply = (isDefined(invokeApply) && !invokeApply),
14302 timeoutId = $browser.defer(function() {
14304 deferred.resolve(fn());
14306 deferred.reject(e);
14307 $exceptionHandler(e);
14310 delete deferreds[promise.$$timeoutId];
14313 if (!skipApply) $rootScope.$apply();
14316 promise.$$timeoutId = timeoutId;
14317 deferreds[timeoutId] = deferred;
14325 * @name $timeout#cancel
14328 * Cancels a task associated with the `promise`. As a result of this, the promise will be
14329 * resolved with a rejection.
14331 * @param {Promise=} promise Promise returned by the `$timeout` function.
14332 * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully
14335 timeout.cancel = function(promise) {
14336 if (promise && promise.$$timeoutId in deferreds) {
14337 deferreds[promise.$$timeoutId].reject('canceled');
14338 delete deferreds[promise.$$timeoutId];
14339 return $browser.defer.cancel(promise.$$timeoutId);
14348 // NOTE: The usage of window and document instead of $window and $document here is
14349 // deliberate. This service depends on the specific behavior of anchor nodes created by the
14350 // browser (resolving and parsing URLs) that is unlikely to be provided by mock objects and
14351 // cause us to break tests. In addition, when the browser resolves a URL for XHR, it
14352 // doesn't know about mocked locations and resolves URLs to the real document - which is
14353 // exactly the behavior needed here. There is little value is mocking these out for this
14355 var urlParsingNode = document.createElement("a");
14356 var originUrl = urlResolve(window.location.href, true);
14361 * Implementation Notes for non-IE browsers
14362 * ----------------------------------------
14363 * Assigning a URL to the href property of an anchor DOM node, even one attached to the DOM,
14364 * results both in the normalizing and parsing of the URL. Normalizing means that a relative
14365 * URL will be resolved into an absolute URL in the context of the application document.
14366 * Parsing means that the anchor node's host, hostname, protocol, port, pathname and related
14367 * properties are all populated to reflect the normalized URL. This approach has wide
14368 * compatibility - Safari 1+, Mozilla 1+, Opera 7+,e etc. See
14369 * http://www.aptana.com/reference/html/api/HTMLAnchorElement.html
14371 * Implementation Notes for IE
14372 * ---------------------------
14373 * IE >= 8 and <= 10 normalizes the URL when assigned to the anchor node similar to the other
14374 * browsers. However, the parsed components will not be set if the URL assigned did not specify
14375 * them. (e.g. if you assign a.href = "foo", then a.protocol, a.host, etc. will be empty.) We
14376 * work around that by performing the parsing in a 2nd step by taking a previously normalized
14377 * URL (e.g. by assigning to a.href) and assigning it a.href again. This correctly populates the
14378 * properties such as protocol, hostname, port, etc.
14380 * IE7 does not normalize the URL when assigned to an anchor node. (Apparently, it does, if one
14381 * uses the inner HTML approach to assign the URL as part of an HTML snippet -
14382 * http://stackoverflow.com/a/472729) However, setting img[src] does normalize the URL.
14383 * Unfortunately, setting img[src] to something like "javascript:foo" on IE throws an exception.
14384 * Since the primary usage for normalizing URLs is to sanitize such URLs, we can't use that
14385 * method and IE < 8 is unsupported.
14388 * http://developer.mozilla.org/en-US/docs/Web/API/HTMLAnchorElement
14389 * http://www.aptana.com/reference/html/api/HTMLAnchorElement.html
14390 * http://url.spec.whatwg.org/#urlutils
14391 * https://github.com/angular/angular.js/pull/2902
14392 * http://james.padolsey.com/javascript/parsing-urls-with-the-dom/
14395 * @param {string} url The URL to be parsed.
14396 * @description Normalizes and parses a URL.
14397 * @returns {object} Returns the normalized URL as a dictionary.
14399 * | member name | Description |
14400 * |---------------|----------------|
14401 * | href | A normalized version of the provided URL if it was not an absolute URL |
14402 * | protocol | The protocol including the trailing colon |
14403 * | host | The host and port (if the port is non-default) of the normalizedUrl |
14404 * | search | The search params, minus the question mark |
14405 * | hash | The hash string, minus the hash symbol
14406 * | hostname | The hostname
14407 * | port | The port, without ":"
14408 * | pathname | The pathname, beginning with "/"
14411 function urlResolve(url, base) {
14415 // Normalize before parse. Refer Implementation Notes on why this is
14416 // done in two steps on IE.
14417 urlParsingNode.setAttribute("href", href);
14418 href = urlParsingNode.href;
14421 urlParsingNode.setAttribute('href', href);
14423 // urlParsingNode provides the UrlUtils interface - http://url.spec.whatwg.org/#urlutils
14425 href: urlParsingNode.href,
14426 protocol: urlParsingNode.protocol ? urlParsingNode.protocol.replace(/:$/, '') : '',
14427 host: urlParsingNode.host,
14428 search: urlParsingNode.search ? urlParsingNode.search.replace(/^\?/, '') : '',
14429 hash: urlParsingNode.hash ? urlParsingNode.hash.replace(/^#/, '') : '',
14430 hostname: urlParsingNode.hostname,
14431 port: urlParsingNode.port,
14432 pathname: (urlParsingNode.pathname.charAt(0) === '/')
14433 ? urlParsingNode.pathname
14434 : '/' + urlParsingNode.pathname
14439 * Parse a request URL and determine whether this is a same-origin request as the application document.
14441 * @param {string|object} requestUrl The url of the request as a string that will be resolved
14442 * or a parsed URL object.
14443 * @returns {boolean} Whether the request is for the same origin as the application document.
14445 function urlIsSameOrigin(requestUrl) {
14446 var parsed = (isString(requestUrl)) ? urlResolve(requestUrl) : requestUrl;
14447 return (parsed.protocol === originUrl.protocol &&
14448 parsed.host === originUrl.host);
14456 * A reference to the browser's `window` object. While `window`
14457 * is globally available in JavaScript, it causes testability problems, because
14458 * it is a global variable. In angular we always refer to it through the
14459 * `$window` service, so it may be overridden, removed or mocked for testing.
14461 * Expressions, like the one defined for the `ngClick` directive in the example
14462 * below, are evaluated with respect to the current scope. Therefore, there is
14463 * no risk of inadvertently coding in a dependency on a global value in such an
14467 <example module="windowExample">
14468 <file name="index.html">
14470 angular.module('windowExample', [])
14471 .controller('ExampleController', ['$scope', '$window', function ($scope, $window) {
14472 $scope.greeting = 'Hello, World!';
14473 $scope.doGreeting = function(greeting) {
14474 $window.alert(greeting);
14478 <div ng-controller="ExampleController">
14479 <input type="text" ng-model="greeting" />
14480 <button ng-click="doGreeting(greeting)">ALERT</button>
14483 <file name="protractor.js" type="protractor">
14484 it('should display the greeting in the input box', function() {
14485 element(by.model('greeting')).sendKeys('Hello, E2E Tests');
14486 // If we click the button it will block the test runner
14487 // element(':button').click();
14492 function $WindowProvider(){
14493 this.$get = valueFn(window);
14496 /* global currencyFilter: true,
14498 filterFilter: true,
14500 limitToFilter: true,
14501 lowercaseFilter: true,
14502 numberFilter: true,
14503 orderByFilter: true,
14504 uppercaseFilter: true,
14509 * @name $filterProvider
14512 * Filters are just functions which transform input to an output. However filters need to be
14513 * Dependency Injected. To achieve this a filter definition consists of a factory function which is
14514 * annotated with dependencies and is responsible for creating a filter function.
14517 * // Filter registration
14518 * function MyModule($provide, $filterProvider) {
14519 * // create a service to demonstrate injection (not always needed)
14520 * $provide.value('greet', function(name){
14521 * return 'Hello ' + name + '!';
14524 * // register a filter factory which uses the
14525 * // greet service to demonstrate DI.
14526 * $filterProvider.register('greet', function(greet){
14527 * // return the filter function which uses the greet service
14528 * // to generate salutation
14529 * return function(text) {
14530 * // filters need to be forgiving so check input validity
14531 * return text && greet(text) || text;
14537 * The filter function is registered with the `$injector` under the filter name suffix with
14541 * it('should be the same instance', inject(
14542 * function($filterProvider) {
14543 * $filterProvider.register('reverse', function(){
14547 * function($filter, reverseFilter) {
14548 * expect($filter('reverse')).toBe(reverseFilter);
14553 * For more information about how angular filters work, and how to create your own filters, see
14554 * {@link guide/filter Filters} in the Angular Developer Guide.
14562 * Filters are used for formatting data displayed to the user.
14564 * The general syntax in templates is as follows:
14566 * {{ expression [| filter_name[:parameter_value] ... ] }}
14568 * @param {String} name Name of the filter function to retrieve
14569 * @return {Function} the filter function
14571 <example name="$filter" module="filterExample">
14572 <file name="index.html">
14573 <div ng-controller="MainCtrl">
14574 <h3>{{ originalText }}</h3>
14575 <h3>{{ filteredText }}</h3>
14579 <file name="script.js">
14580 angular.module('filterExample', [])
14581 .controller('MainCtrl', function($scope, $filter) {
14582 $scope.originalText = 'hello';
14583 $scope.filteredText = $filter('uppercase')($scope.originalText);
14588 $FilterProvider.$inject = ['$provide'];
14589 function $FilterProvider($provide) {
14590 var suffix = 'Filter';
14594 * @name $filterProvider#register
14595 * @param {string|Object} name Name of the filter function, or an object map of filters where
14596 * the keys are the filter names and the values are the filter factories.
14597 * @returns {Object} Registered filter instance, or if a map of filters was provided then a map
14598 * of the registered filter instances.
14600 function register(name, factory) {
14601 if(isObject(name)) {
14603 forEach(name, function(filter, key) {
14604 filters[key] = register(key, filter);
14608 return $provide.factory(name + suffix, factory);
14611 this.register = register;
14613 this.$get = ['$injector', function($injector) {
14614 return function(name) {
14615 return $injector.get(name + suffix);
14619 ////////////////////////////////////////
14622 currencyFilter: false,
14624 filterFilter: false,
14626 limitToFilter: false,
14627 lowercaseFilter: false,
14628 numberFilter: false,
14629 orderByFilter: false,
14630 uppercaseFilter: false,
14633 register('currency', currencyFilter);
14634 register('date', dateFilter);
14635 register('filter', filterFilter);
14636 register('json', jsonFilter);
14637 register('limitTo', limitToFilter);
14638 register('lowercase', lowercaseFilter);
14639 register('number', numberFilter);
14640 register('orderBy', orderByFilter);
14641 register('uppercase', uppercaseFilter);
14650 * Selects a subset of items from `array` and returns it as a new array.
14652 * @param {Array} array The source array.
14653 * @param {string|Object|function()} expression The predicate to be used for selecting items from
14658 * - `string`: The string is evaluated as an expression and the resulting value is used for substring match against
14659 * the contents of the `array`. All strings or objects with string properties in `array` that contain this string
14660 * will be returned. The predicate can be negated by prefixing the string with `!`.
14662 * - `Object`: A pattern object can be used to filter specific properties on objects contained
14663 * by `array`. For example `{name:"M", phone:"1"}` predicate will return an array of items
14664 * which have property `name` containing "M" and property `phone` containing "1". A special
14665 * property name `$` can be used (as in `{$:"text"}`) to accept a match against any
14666 * property of the object. That's equivalent to the simple substring match with a `string`
14667 * as described above. The predicate can be negated by prefixing the string with `!`.
14668 * For Example `{name: "!M"}` predicate will return an array of items which have property `name`
14669 * not containing "M".
14671 * - `function(value)`: A predicate function can be used to write arbitrary filters. The function is
14672 * called for each element of `array`. The final result is an array of those elements that
14673 * the predicate returned true for.
14675 * @param {function(actual, expected)|true|undefined} comparator Comparator which is used in
14676 * determining if the expected value (from the filter expression) and actual value (from
14677 * the object in the array) should be considered a match.
14681 * - `function(actual, expected)`:
14682 * The function will be given the object value and the predicate value to compare and
14683 * should return true if the item should be included in filtered result.
14685 * - `true`: A shorthand for `function(actual, expected) { return angular.equals(expected, actual)}`.
14686 * this is essentially strict comparison of expected and actual.
14688 * - `false|undefined`: A short hand for a function which will look for a substring match in case
14693 <file name="index.html">
14694 <div ng-init="friends = [{name:'John', phone:'555-1276'},
14695 {name:'Mary', phone:'800-BIG-MARY'},
14696 {name:'Mike', phone:'555-4321'},
14697 {name:'Adam', phone:'555-5678'},
14698 {name:'Julie', phone:'555-8765'},
14699 {name:'Juliette', phone:'555-5678'}]"></div>
14701 Search: <input ng-model="searchText">
14702 <table id="searchTextResults">
14703 <tr><th>Name</th><th>Phone</th></tr>
14704 <tr ng-repeat="friend in friends | filter:searchText">
14705 <td>{{friend.name}}</td>
14706 <td>{{friend.phone}}</td>
14710 Any: <input ng-model="search.$"> <br>
14711 Name only <input ng-model="search.name"><br>
14712 Phone only <input ng-model="search.phone"><br>
14713 Equality <input type="checkbox" ng-model="strict"><br>
14714 <table id="searchObjResults">
14715 <tr><th>Name</th><th>Phone</th></tr>
14716 <tr ng-repeat="friendObj in friends | filter:search:strict">
14717 <td>{{friendObj.name}}</td>
14718 <td>{{friendObj.phone}}</td>
14722 <file name="protractor.js" type="protractor">
14723 var expectFriendNames = function(expectedNames, key) {
14724 element.all(by.repeater(key + ' in friends').column(key + '.name')).then(function(arr) {
14725 arr.forEach(function(wd, i) {
14726 expect(wd.getText()).toMatch(expectedNames[i]);
14731 it('should search across all fields when filtering with a string', function() {
14732 var searchText = element(by.model('searchText'));
14733 searchText.clear();
14734 searchText.sendKeys('m');
14735 expectFriendNames(['Mary', 'Mike', 'Adam'], 'friend');
14737 searchText.clear();
14738 searchText.sendKeys('76');
14739 expectFriendNames(['John', 'Julie'], 'friend');
14742 it('should search in specific fields when filtering with a predicate object', function() {
14743 var searchAny = element(by.model('search.$'));
14745 searchAny.sendKeys('i');
14746 expectFriendNames(['Mary', 'Mike', 'Julie', 'Juliette'], 'friendObj');
14748 it('should use a equal comparison when comparator is true', function() {
14749 var searchName = element(by.model('search.name'));
14750 var strict = element(by.model('strict'));
14751 searchName.clear();
14752 searchName.sendKeys('Julie');
14754 expectFriendNames(['Julie'], 'friendObj');
14759 function filterFilter() {
14760 return function(array, expression, comparator) {
14761 if (!isArray(array)) return array;
14763 var comparatorType = typeof(comparator),
14766 predicates.check = function(value) {
14767 for (var j = 0; j < predicates.length; j++) {
14768 if(!predicates[j](value)) {
14775 if (comparatorType !== 'function') {
14776 if (comparatorType === 'boolean' && comparator) {
14777 comparator = function(obj, text) {
14778 return angular.equals(obj, text);
14781 comparator = function(obj, text) {
14782 if (obj && text && typeof obj === 'object' && typeof text === 'object') {
14783 for (var objKey in obj) {
14784 if (objKey.charAt(0) !== '$' && hasOwnProperty.call(obj, objKey) &&
14785 comparator(obj[objKey], text[objKey])) {
14791 text = (''+text).toLowerCase();
14792 return (''+obj).toLowerCase().indexOf(text) > -1;
14797 var search = function(obj, text){
14798 if (typeof text == 'string' && text.charAt(0) === '!') {
14799 return !search(obj, text.substr(1));
14801 switch (typeof obj) {
14805 return comparator(obj, text);
14807 switch (typeof text) {
14809 return comparator(obj, text);
14811 for ( var objKey in obj) {
14812 if (objKey.charAt(0) !== '$' && search(obj[objKey], text)) {
14820 for ( var i = 0; i < obj.length; i++) {
14821 if (search(obj[i], text)) {
14830 switch (typeof expression) {
14834 // Set up expression object and fall through
14835 expression = {$:expression};
14839 for (var key in expression) {
14841 if (typeof expression[path] === 'undefined') return;
14842 predicates.push(function(value) {
14843 return search(path == '$' ? value : (value && value[path]), expression[path]);
14849 predicates.push(expression);
14855 for ( var j = 0; j < array.length; j++) {
14856 var value = array[j];
14857 if (predicates.check(value)) {
14858 filtered.push(value);
14871 * Formats a number as a currency (ie $1,234.56). When no currency symbol is provided, default
14872 * symbol for current locale is used.
14874 * @param {number} amount Input to filter.
14875 * @param {string=} symbol Currency symbol or identifier to be displayed.
14876 * @returns {string} Formatted number.
14880 <example module="currencyExample">
14881 <file name="index.html">
14883 angular.module('currencyExample', [])
14884 .controller('ExampleController', ['$scope', function($scope) {
14885 $scope.amount = 1234.56;
14888 <div ng-controller="ExampleController">
14889 <input type="number" ng-model="amount"> <br>
14890 default currency symbol ($): <span id="currency-default">{{amount | currency}}</span><br>
14891 custom currency identifier (USD$): <span>{{amount | currency:"USD$"}}</span>
14894 <file name="protractor.js" type="protractor">
14895 it('should init with 1234.56', function() {
14896 expect(element(by.id('currency-default')).getText()).toBe('$1,234.56');
14897 expect(element(by.binding('amount | currency:"USD$"')).getText()).toBe('USD$1,234.56');
14899 it('should update', function() {
14900 if (browser.params.browser == 'safari') {
14901 // Safari does not understand the minus key. See
14902 // https://github.com/angular/protractor/issues/481
14905 element(by.model('amount')).clear();
14906 element(by.model('amount')).sendKeys('-1234');
14907 expect(element(by.id('currency-default')).getText()).toBe('($1,234.00)');
14908 expect(element(by.binding('amount | currency:"USD$"')).getText()).toBe('(USD$1,234.00)');
14913 currencyFilter.$inject = ['$locale'];
14914 function currencyFilter($locale) {
14915 var formats = $locale.NUMBER_FORMATS;
14916 return function(amount, currencySymbol){
14917 if (isUndefined(currencySymbol)) currencySymbol = formats.CURRENCY_SYM;
14918 return formatNumber(amount, formats.PATTERNS[1], formats.GROUP_SEP, formats.DECIMAL_SEP, 2).
14919 replace(/\u00A4/g, currencySymbol);
14929 * Formats a number as text.
14931 * If the input is not a number an empty string is returned.
14933 * @param {number|string} number Number to format.
14934 * @param {(number|string)=} fractionSize Number of decimal places to round the number to.
14935 * If this is not provided then the fraction size is computed from the current locale's number
14936 * formatting pattern. In the case of the default locale, it will be 3.
14937 * @returns {string} Number rounded to decimalPlaces and places a “,” after each third digit.
14940 <example module="numberFilterExample">
14941 <file name="index.html">
14943 angular.module('numberFilterExample', [])
14944 .controller('ExampleController', ['$scope', function($scope) {
14945 $scope.val = 1234.56789;
14948 <div ng-controller="ExampleController">
14949 Enter number: <input ng-model='val'><br>
14950 Default formatting: <span id='number-default'>{{val | number}}</span><br>
14951 No fractions: <span>{{val | number:0}}</span><br>
14952 Negative number: <span>{{-val | number:4}}</span>
14955 <file name="protractor.js" type="protractor">
14956 it('should format numbers', function() {
14957 expect(element(by.id('number-default')).getText()).toBe('1,234.568');
14958 expect(element(by.binding('val | number:0')).getText()).toBe('1,235');
14959 expect(element(by.binding('-val | number:4')).getText()).toBe('-1,234.5679');
14962 it('should update', function() {
14963 element(by.model('val')).clear();
14964 element(by.model('val')).sendKeys('3374.333');
14965 expect(element(by.id('number-default')).getText()).toBe('3,374.333');
14966 expect(element(by.binding('val | number:0')).getText()).toBe('3,374');
14967 expect(element(by.binding('-val | number:4')).getText()).toBe('-3,374.3330');
14974 numberFilter.$inject = ['$locale'];
14975 function numberFilter($locale) {
14976 var formats = $locale.NUMBER_FORMATS;
14977 return function(number, fractionSize) {
14978 return formatNumber(number, formats.PATTERNS[0], formats.GROUP_SEP, formats.DECIMAL_SEP,
14983 var DECIMAL_SEP = '.';
14984 function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) {
14985 if (number == null || !isFinite(number) || isObject(number)) return '';
14987 var isNegative = number < 0;
14988 number = Math.abs(number);
14989 var numStr = number + '',
14993 var hasExponent = false;
14994 if (numStr.indexOf('e') !== -1) {
14995 var match = numStr.match(/([\d\.]+)e(-?)(\d+)/);
14996 if (match && match[2] == '-' && match[3] > fractionSize + 1) {
15000 formatedText = numStr;
15001 hasExponent = true;
15005 if (!hasExponent) {
15006 var fractionLen = (numStr.split(DECIMAL_SEP)[1] || '').length;
15008 // determine fractionSize if it is not specified
15009 if (isUndefined(fractionSize)) {
15010 fractionSize = Math.min(Math.max(pattern.minFrac, fractionLen), pattern.maxFrac);
15013 // safely round numbers in JS without hitting imprecisions of floating-point arithmetics
15015 // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round
15016 number = +(Math.round(+(number.toString() + 'e' + fractionSize)).toString() + 'e' + -fractionSize);
15018 if (number === 0) {
15019 isNegative = false;
15022 var fraction = ('' + number).split(DECIMAL_SEP);
15023 var whole = fraction[0];
15024 fraction = fraction[1] || '';
15027 lgroup = pattern.lgSize,
15028 group = pattern.gSize;
15030 if (whole.length >= (lgroup + group)) {
15031 pos = whole.length - lgroup;
15032 for (i = 0; i < pos; i++) {
15033 if ((pos - i)%group === 0 && i !== 0) {
15034 formatedText += groupSep;
15036 formatedText += whole.charAt(i);
15040 for (i = pos; i < whole.length; i++) {
15041 if ((whole.length - i)%lgroup === 0 && i !== 0) {
15042 formatedText += groupSep;
15044 formatedText += whole.charAt(i);
15047 // format fraction part.
15048 while(fraction.length < fractionSize) {
15052 if (fractionSize && fractionSize !== "0") formatedText += decimalSep + fraction.substr(0, fractionSize);
15055 if (fractionSize > 0 && number > -1 && number < 1) {
15056 formatedText = number.toFixed(fractionSize);
15060 parts.push(isNegative ? pattern.negPre : pattern.posPre);
15061 parts.push(formatedText);
15062 parts.push(isNegative ? pattern.negSuf : pattern.posSuf);
15063 return parts.join('');
15066 function padNumber(num, digits, trim) {
15073 while(num.length < digits) num = '0' + num;
15075 num = num.substr(num.length - digits);
15080 function dateGetter(name, size, offset, trim) {
15081 offset = offset || 0;
15082 return function(date) {
15083 var value = date['get' + name]();
15084 if (offset > 0 || value > -offset)
15086 if (value === 0 && offset == -12 ) value = 12;
15087 return padNumber(value, size, trim);
15091 function dateStrGetter(name, shortForm) {
15092 return function(date, formats) {
15093 var value = date['get' + name]();
15094 var get = uppercase(shortForm ? ('SHORT' + name) : name);
15096 return formats[get][value];
15100 function timeZoneGetter(date) {
15101 var zone = -1 * date.getTimezoneOffset();
15102 var paddedZone = (zone >= 0) ? "+" : "";
15104 paddedZone += padNumber(Math[zone > 0 ? 'floor' : 'ceil'](zone / 60), 2) +
15105 padNumber(Math.abs(zone % 60), 2);
15110 function ampmGetter(date, formats) {
15111 return date.getHours() < 12 ? formats.AMPMS[0] : formats.AMPMS[1];
15114 var DATE_FORMATS = {
15115 yyyy: dateGetter('FullYear', 4),
15116 yy: dateGetter('FullYear', 2, 0, true),
15117 y: dateGetter('FullYear', 1),
15118 MMMM: dateStrGetter('Month'),
15119 MMM: dateStrGetter('Month', true),
15120 MM: dateGetter('Month', 2, 1),
15121 M: dateGetter('Month', 1, 1),
15122 dd: dateGetter('Date', 2),
15123 d: dateGetter('Date', 1),
15124 HH: dateGetter('Hours', 2),
15125 H: dateGetter('Hours', 1),
15126 hh: dateGetter('Hours', 2, -12),
15127 h: dateGetter('Hours', 1, -12),
15128 mm: dateGetter('Minutes', 2),
15129 m: dateGetter('Minutes', 1),
15130 ss: dateGetter('Seconds', 2),
15131 s: dateGetter('Seconds', 1),
15132 // while ISO 8601 requires fractions to be prefixed with `.` or `,`
15133 // we can be just safely rely on using `sss` since we currently don't support single or two digit fractions
15134 sss: dateGetter('Milliseconds', 3),
15135 EEEE: dateStrGetter('Day'),
15136 EEE: dateStrGetter('Day', true),
15141 var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z))(.*)/,
15142 NUMBER_STRING = /^\-?\d+$/;
15150 * Formats `date` to a string based on the requested `format`.
15152 * `format` string can be composed of the following elements:
15154 * * `'yyyy'`: 4 digit representation of year (e.g. AD 1 => 0001, AD 2010 => 2010)
15155 * * `'yy'`: 2 digit representation of year, padded (00-99). (e.g. AD 2001 => 01, AD 2010 => 10)
15156 * * `'y'`: 1 digit representation of year, e.g. (AD 1 => 1, AD 199 => 199)
15157 * * `'MMMM'`: Month in year (January-December)
15158 * * `'MMM'`: Month in year (Jan-Dec)
15159 * * `'MM'`: Month in year, padded (01-12)
15160 * * `'M'`: Month in year (1-12)
15161 * * `'dd'`: Day in month, padded (01-31)
15162 * * `'d'`: Day in month (1-31)
15163 * * `'EEEE'`: Day in Week,(Sunday-Saturday)
15164 * * `'EEE'`: Day in Week, (Sun-Sat)
15165 * * `'HH'`: Hour in day, padded (00-23)
15166 * * `'H'`: Hour in day (0-23)
15167 * * `'hh'`: Hour in am/pm, padded (01-12)
15168 * * `'h'`: Hour in am/pm, (1-12)
15169 * * `'mm'`: Minute in hour, padded (00-59)
15170 * * `'m'`: Minute in hour (0-59)
15171 * * `'ss'`: Second in minute, padded (00-59)
15172 * * `'s'`: Second in minute (0-59)
15173 * * `'.sss' or ',sss'`: Millisecond in second, padded (000-999)
15174 * * `'a'`: am/pm marker
15175 * * `'Z'`: 4 digit (+sign) representation of the timezone offset (-1200-+1200)
15177 * `format` string can also be one of the following predefined
15178 * {@link guide/i18n localizable formats}:
15180 * * `'medium'`: equivalent to `'MMM d, y h:mm:ss a'` for en_US locale
15181 * (e.g. Sep 3, 2010 12:05:08 pm)
15182 * * `'short'`: equivalent to `'M/d/yy h:mm a'` for en_US locale (e.g. 9/3/10 12:05 pm)
15183 * * `'fullDate'`: equivalent to `'EEEE, MMMM d,y'` for en_US locale
15184 * (e.g. Friday, September 3, 2010)
15185 * * `'longDate'`: equivalent to `'MMMM d, y'` for en_US locale (e.g. September 3, 2010)
15186 * * `'mediumDate'`: equivalent to `'MMM d, y'` for en_US locale (e.g. Sep 3, 2010)
15187 * * `'shortDate'`: equivalent to `'M/d/yy'` for en_US locale (e.g. 9/3/10)
15188 * * `'mediumTime'`: equivalent to `'h:mm:ss a'` for en_US locale (e.g. 12:05:08 pm)
15189 * * `'shortTime'`: equivalent to `'h:mm a'` for en_US locale (e.g. 12:05 pm)
15191 * `format` string can contain literal values. These need to be escaped by surrounding with single quotes (e.g.
15192 * `"h 'in the morning'"`). In order to output a single quote, escape it - i.e., two single quotes in a sequence
15193 * (e.g. `"h 'o''clock'"`).
15195 * @param {(Date|number|string)} date Date to format either as Date object, milliseconds (string or
15196 * number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.sssZ and its
15197 * shorter versions like yyyy-MM-ddTHH:mmZ, yyyy-MM-dd or yyyyMMddTHHmmssZ). If no timezone is
15198 * specified in the string input, the time is considered to be in the local timezone.
15199 * @param {string=} format Formatting rules (see Description). If not specified,
15200 * `mediumDate` is used.
15201 * @returns {string} Formatted string or the input if input is not recognized as date/millis.
15205 <file name="index.html">
15206 <span ng-non-bindable>{{1288323623006 | date:'medium'}}</span>:
15207 <span>{{1288323623006 | date:'medium'}}</span><br>
15208 <span ng-non-bindable>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span>:
15209 <span>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span><br>
15210 <span ng-non-bindable>{{1288323623006 | date:'MM/dd/yyyy @ h:mma'}}</span>:
15211 <span>{{'1288323623006' | date:'MM/dd/yyyy @ h:mma'}}</span><br>
15212 <span ng-non-bindable>{{1288323623006 | date:"MM/dd/yyyy 'at' h:mma"}}</span>:
15213 <span>{{'1288323623006' | date:"MM/dd/yyyy 'at' h:mma"}}</span><br>
15215 <file name="protractor.js" type="protractor">
15216 it('should format date', function() {
15217 expect(element(by.binding("1288323623006 | date:'medium'")).getText()).
15218 toMatch(/Oct 2\d, 2010 \d{1,2}:\d{2}:\d{2} (AM|PM)/);
15219 expect(element(by.binding("1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'")).getText()).
15220 toMatch(/2010\-10\-2\d \d{2}:\d{2}:\d{2} (\-|\+)?\d{4}/);
15221 expect(element(by.binding("'1288323623006' | date:'MM/dd/yyyy @ h:mma'")).getText()).
15222 toMatch(/10\/2\d\/2010 @ \d{1,2}:\d{2}(AM|PM)/);
15223 expect(element(by.binding("'1288323623006' | date:\"MM/dd/yyyy 'at' h:mma\"")).getText()).
15224 toMatch(/10\/2\d\/2010 at \d{1,2}:\d{2}(AM|PM)/);
15229 dateFilter.$inject = ['$locale'];
15230 function dateFilter($locale) {
15233 var R_ISO8601_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/;
15234 // 1 2 3 4 5 6 7 8 9 10 11
15235 function jsonStringToDate(string) {
15237 if (match = string.match(R_ISO8601_STR)) {
15238 var date = new Date(0),
15241 dateSetter = match[8] ? date.setUTCFullYear : date.setFullYear,
15242 timeSetter = match[8] ? date.setUTCHours : date.setHours;
15245 tzHour = int(match[9] + match[10]);
15246 tzMin = int(match[9] + match[11]);
15248 dateSetter.call(date, int(match[1]), int(match[2]) - 1, int(match[3]));
15249 var h = int(match[4]||0) - tzHour;
15250 var m = int(match[5]||0) - tzMin;
15251 var s = int(match[6]||0);
15252 var ms = Math.round(parseFloat('0.' + (match[7]||0)) * 1000);
15253 timeSetter.call(date, h, m, s, ms);
15260 return function(date, format) {
15265 format = format || 'mediumDate';
15266 format = $locale.DATETIME_FORMATS[format] || format;
15267 if (isString(date)) {
15268 date = NUMBER_STRING.test(date) ? int(date) : jsonStringToDate(date);
15271 if (isNumber(date)) {
15272 date = new Date(date);
15275 if (!isDate(date)) {
15280 match = DATE_FORMATS_SPLIT.exec(format);
15282 parts = concat(parts, match, 1);
15283 format = parts.pop();
15285 parts.push(format);
15290 forEach(parts, function(value){
15291 fn = DATE_FORMATS[value];
15292 text += fn ? fn(date, $locale.DATETIME_FORMATS)
15293 : value.replace(/(^'|'$)/g, '').replace(/''/g, "'");
15307 * Allows you to convert a JavaScript object into JSON string.
15309 * This filter is mostly useful for debugging. When using the double curly {{value}} notation
15310 * the binding is automatically converted to JSON.
15312 * @param {*} object Any JavaScript object (including arrays and primitive types) to filter.
15313 * @returns {string} JSON string.
15318 <file name="index.html">
15319 <pre>{{ {'name':'value'} | json }}</pre>
15321 <file name="protractor.js" type="protractor">
15322 it('should jsonify filtered objects', function() {
15323 expect(element(by.binding("{'name':'value'}")).getText()).toMatch(/\{\n "name": ?"value"\n}/);
15329 function jsonFilter() {
15330 return function(object) {
15331 return toJson(object, true);
15341 * Converts string to lowercase.
15342 * @see angular.lowercase
15344 var lowercaseFilter = valueFn(lowercase);
15352 * Converts string to uppercase.
15353 * @see angular.uppercase
15355 var uppercaseFilter = valueFn(uppercase);
15363 * Creates a new array or string containing only a specified number of elements. The elements
15364 * are taken from either the beginning or the end of the source array or string, as specified by
15365 * the value and sign (positive or negative) of `limit`.
15367 * @param {Array|string} input Source array or string to be limited.
15368 * @param {string|number} limit The length of the returned array or string. If the `limit` number
15369 * is positive, `limit` number of items from the beginning of the source array/string are copied.
15370 * If the number is negative, `limit` number of items from the end of the source array/string
15371 * are copied. The `limit` will be trimmed if it exceeds `array.length`
15372 * @returns {Array|string} A new sub-array or substring of length `limit` or less if input array
15373 * had less than `limit` elements.
15376 <example module="limitToExample">
15377 <file name="index.html">
15379 angular.module('limitToExample', [])
15380 .controller('ExampleController', ['$scope', function($scope) {
15381 $scope.numbers = [1,2,3,4,5,6,7,8,9];
15382 $scope.letters = "abcdefghi";
15383 $scope.numLimit = 3;
15384 $scope.letterLimit = 3;
15387 <div ng-controller="ExampleController">
15388 Limit {{numbers}} to: <input type="integer" ng-model="numLimit">
15389 <p>Output numbers: {{ numbers | limitTo:numLimit }}</p>
15390 Limit {{letters}} to: <input type="integer" ng-model="letterLimit">
15391 <p>Output letters: {{ letters | limitTo:letterLimit }}</p>
15394 <file name="protractor.js" type="protractor">
15395 var numLimitInput = element(by.model('numLimit'));
15396 var letterLimitInput = element(by.model('letterLimit'));
15397 var limitedNumbers = element(by.binding('numbers | limitTo:numLimit'));
15398 var limitedLetters = element(by.binding('letters | limitTo:letterLimit'));
15400 it('should limit the number array to first three items', function() {
15401 expect(numLimitInput.getAttribute('value')).toBe('3');
15402 expect(letterLimitInput.getAttribute('value')).toBe('3');
15403 expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3]');
15404 expect(limitedLetters.getText()).toEqual('Output letters: abc');
15407 it('should update the output when -3 is entered', function() {
15408 numLimitInput.clear();
15409 numLimitInput.sendKeys('-3');
15410 letterLimitInput.clear();
15411 letterLimitInput.sendKeys('-3');
15412 expect(limitedNumbers.getText()).toEqual('Output numbers: [7,8,9]');
15413 expect(limitedLetters.getText()).toEqual('Output letters: ghi');
15416 it('should not exceed the maximum size of input array', function() {
15417 numLimitInput.clear();
15418 numLimitInput.sendKeys('100');
15419 letterLimitInput.clear();
15420 letterLimitInput.sendKeys('100');
15421 expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3,4,5,6,7,8,9]');
15422 expect(limitedLetters.getText()).toEqual('Output letters: abcdefghi');
15427 function limitToFilter(){
15428 return function(input, limit) {
15429 if (!isArray(input) && !isString(input)) return input;
15431 if (Math.abs(Number(limit)) === Infinity) {
15432 limit = Number(limit);
15434 limit = int(limit);
15437 if (isString(input)) {
15438 //NaN check on limit
15440 return limit >= 0 ? input.slice(0, limit) : input.slice(limit, input.length);
15449 // if abs(limit) exceeds maximum length, trim it
15450 if (limit > input.length)
15451 limit = input.length;
15452 else if (limit < -input.length)
15453 limit = -input.length;
15459 i = input.length + limit;
15464 out.push(input[i]);
15477 * Orders a specified `array` by the `expression` predicate. It is ordered alphabetically
15478 * for strings and numerically for numbers. Note: if you notice numbers are not being sorted
15479 * correctly, make sure they are actually being saved as numbers and not strings.
15481 * @param {Array} array The array to sort.
15482 * @param {function(*)|string|Array.<(function(*)|string)>} expression A predicate to be
15483 * used by the comparator to determine the order of elements.
15487 * - `function`: Getter function. The result of this function will be sorted using the
15488 * `<`, `=`, `>` operator.
15489 * - `string`: An Angular expression. The result of this expression is used to compare elements
15490 * (for example `name` to sort by a property called `name` or `name.substr(0, 3)` to sort by
15491 * 3 first characters of a property called `name`). The result of a constant expression
15492 * is interpreted as a property name to be used in comparisons (for example `"special name"`
15493 * to sort object by the value of their `special name` property). An expression can be
15494 * optionally prefixed with `+` or `-` to control ascending or descending sort order
15495 * (for example, `+name` or `-name`).
15496 * - `Array`: An array of function or string predicates. The first predicate in the array
15497 * is used for sorting, but when two items are equivalent, the next predicate is used.
15499 * @param {boolean=} reverse Reverse the order of the array.
15500 * @returns {Array} Sorted copy of the source array.
15503 <example module="orderByExample">
15504 <file name="index.html">
15506 angular.module('orderByExample', [])
15507 .controller('ExampleController', ['$scope', function($scope) {
15509 [{name:'John', phone:'555-1212', age:10},
15510 {name:'Mary', phone:'555-9876', age:19},
15511 {name:'Mike', phone:'555-4321', age:21},
15512 {name:'Adam', phone:'555-5678', age:35},
15513 {name:'Julie', phone:'555-8765', age:29}];
15514 $scope.predicate = '-age';
15517 <div ng-controller="ExampleController">
15518 <pre>Sorting predicate = {{predicate}}; reverse = {{reverse}}</pre>
15520 [ <a href="" ng-click="predicate=''">unsorted</a> ]
15521 <table class="friend">
15523 <th><a href="" ng-click="predicate = 'name'; reverse=false">Name</a>
15524 (<a href="" ng-click="predicate = '-name'; reverse=false">^</a>)</th>
15525 <th><a href="" ng-click="predicate = 'phone'; reverse=!reverse">Phone Number</a></th>
15526 <th><a href="" ng-click="predicate = 'age'; reverse=!reverse">Age</a></th>
15528 <tr ng-repeat="friend in friends | orderBy:predicate:reverse">
15529 <td>{{friend.name}}</td>
15530 <td>{{friend.phone}}</td>
15531 <td>{{friend.age}}</td>
15538 * It's also possible to call the orderBy filter manually, by injecting `$filter`, retrieving the
15539 * filter routine with `$filter('orderBy')`, and calling the returned filter routine with the
15540 * desired parameters.
15545 <example module="orderByExample">
15546 <file name="index.html">
15547 <div ng-controller="ExampleController">
15548 <table class="friend">
15550 <th><a href="" ng-click="reverse=false;order('name', false)">Name</a>
15551 (<a href="" ng-click="order('-name',false)">^</a>)</th>
15552 <th><a href="" ng-click="reverse=!reverse;order('phone', reverse)">Phone Number</a></th>
15553 <th><a href="" ng-click="reverse=!reverse;order('age',reverse)">Age</a></th>
15555 <tr ng-repeat="friend in friends">
15556 <td>{{friend.name}}</td>
15557 <td>{{friend.phone}}</td>
15558 <td>{{friend.age}}</td>
15564 <file name="script.js">
15565 angular.module('orderByExample', [])
15566 .controller('ExampleController', ['$scope', '$filter', function($scope, $filter) {
15567 var orderBy = $filter('orderBy');
15569 { name: 'John', phone: '555-1212', age: 10 },
15570 { name: 'Mary', phone: '555-9876', age: 19 },
15571 { name: 'Mike', phone: '555-4321', age: 21 },
15572 { name: 'Adam', phone: '555-5678', age: 35 },
15573 { name: 'Julie', phone: '555-8765', age: 29 }
15575 $scope.order = function(predicate, reverse) {
15576 $scope.friends = orderBy($scope.friends, predicate, reverse);
15578 $scope.order('-age',false);
15583 orderByFilter.$inject = ['$parse'];
15584 function orderByFilter($parse){
15585 return function(array, sortPredicate, reverseOrder) {
15586 if (!(isArrayLike(array))) return array;
15587 if (!sortPredicate) return array;
15588 sortPredicate = isArray(sortPredicate) ? sortPredicate: [sortPredicate];
15589 sortPredicate = map(sortPredicate, function(predicate){
15590 var descending = false, get = predicate || identity;
15591 if (isString(predicate)) {
15592 if ((predicate.charAt(0) == '+' || predicate.charAt(0) == '-')) {
15593 descending = predicate.charAt(0) == '-';
15594 predicate = predicate.substring(1);
15596 get = $parse(predicate);
15597 if (get.constant) {
15599 return reverseComparator(function(a,b) {
15600 return compare(a[key], b[key]);
15604 return reverseComparator(function(a,b){
15605 return compare(get(a),get(b));
15608 var arrayCopy = [];
15609 for ( var i = 0; i < array.length; i++) { arrayCopy.push(array[i]); }
15610 return arrayCopy.sort(reverseComparator(comparator, reverseOrder));
15612 function comparator(o1, o2){
15613 for ( var i = 0; i < sortPredicate.length; i++) {
15614 var comp = sortPredicate[i](o1, o2);
15615 if (comp !== 0) return comp;
15619 function reverseComparator(comp, descending) {
15620 return toBoolean(descending)
15621 ? function(a,b){return comp(b,a);}
15624 function compare(v1, v2){
15625 var t1 = typeof v1;
15626 var t2 = typeof v2;
15628 if (isDate(v1) && isDate(v2)) {
15632 if (t1 == "string") {
15633 v1 = v1.toLowerCase();
15634 v2 = v2.toLowerCase();
15636 if (v1 === v2) return 0;
15637 return v1 < v2 ? -1 : 1;
15639 return t1 < t2 ? -1 : 1;
15645 function ngDirective(directive) {
15646 if (isFunction(directive)) {
15651 directive.restrict = directive.restrict || 'AC';
15652 return valueFn(directive);
15661 * Modifies the default behavior of the html A tag so that the default action is prevented when
15662 * the href attribute is empty.
15664 * This change permits the easy creation of action links with the `ngClick` directive
15665 * without changing the location or causing page reloads, e.g.:
15666 * `<a href="" ng-click="list.addItem()">Add Item</a>`
15668 var htmlAnchorDirective = valueFn({
15670 compile: function(element, attr) {
15674 // turn <a href ng-click="..">link</a> into a stylable link in IE
15675 // but only if it doesn't have name attribute, in which case it's an anchor
15676 if (!attr.href && !attr.name) {
15677 attr.$set('href', '');
15680 // add a comment node to anchors to workaround IE bug that causes element content to be reset
15681 // to new attribute content if attribute is updated with value containing @ and element also
15682 // contains value with @
15684 element.append(document.createComment('IE fix'));
15687 if (!attr.href && !attr.xlinkHref && !attr.name) {
15688 return function(scope, element) {
15689 // SVGAElement does not use the href attribute, but rather the 'xlinkHref' attribute.
15690 var href = toString.call(element.prop('href')) === '[object SVGAnimatedString]' ?
15691 'xlink:href' : 'href';
15692 element.on('click', function(event){
15693 // if we have no href url, then don't navigate anywhere.
15694 if (!element.attr(href)) {
15695 event.preventDefault();
15710 * Using Angular markup like `{{hash}}` in an href attribute will
15711 * make the link go to the wrong URL if the user clicks it before
15712 * Angular has a chance to replace the `{{hash}}` markup with its
15713 * value. Until Angular replaces the markup the link will be broken
15714 * and will most likely return a 404 error.
15716 * The `ngHref` directive solves this problem.
15718 * The wrong way to write it:
15720 * <a href="http://www.gravatar.com/avatar/{{hash}}"/>
15723 * The correct way to write it:
15725 * <a ng-href="http://www.gravatar.com/avatar/{{hash}}"/>
15729 * @param {template} ngHref any string which can contain `{{}}` markup.
15732 * This example shows various combinations of `href`, `ng-href` and `ng-click` attributes
15733 * in links and their different behaviors:
15735 <file name="index.html">
15736 <input ng-model="value" /><br />
15737 <a id="link-1" href ng-click="value = 1">link 1</a> (link, don't reload)<br />
15738 <a id="link-2" href="" ng-click="value = 2">link 2</a> (link, don't reload)<br />
15739 <a id="link-3" ng-href="/{{'123'}}">link 3</a> (link, reload!)<br />
15740 <a id="link-4" href="" name="xx" ng-click="value = 4">anchor</a> (link, don't reload)<br />
15741 <a id="link-5" name="xxx" ng-click="value = 5">anchor</a> (no link)<br />
15742 <a id="link-6" ng-href="{{value}}">link</a> (link, change location)
15744 <file name="protractor.js" type="protractor">
15745 it('should execute ng-click but not reload when href without value', function() {
15746 element(by.id('link-1')).click();
15747 expect(element(by.model('value')).getAttribute('value')).toEqual('1');
15748 expect(element(by.id('link-1')).getAttribute('href')).toBe('');
15751 it('should execute ng-click but not reload when href empty string', function() {
15752 element(by.id('link-2')).click();
15753 expect(element(by.model('value')).getAttribute('value')).toEqual('2');
15754 expect(element(by.id('link-2')).getAttribute('href')).toBe('');
15757 it('should execute ng-click and change url when ng-href specified', function() {
15758 expect(element(by.id('link-3')).getAttribute('href')).toMatch(/\/123$/);
15760 element(by.id('link-3')).click();
15762 // At this point, we navigate away from an Angular page, so we need
15763 // to use browser.driver to get the base webdriver.
15765 browser.wait(function() {
15766 return browser.driver.getCurrentUrl().then(function(url) {
15767 return url.match(/\/123$/);
15769 }, 5000, 'page should navigate to /123');
15772 xit('should execute ng-click but not reload when href empty string and name specified', function() {
15773 element(by.id('link-4')).click();
15774 expect(element(by.model('value')).getAttribute('value')).toEqual('4');
15775 expect(element(by.id('link-4')).getAttribute('href')).toBe('');
15778 it('should execute ng-click but not reload when no href but name specified', function() {
15779 element(by.id('link-5')).click();
15780 expect(element(by.model('value')).getAttribute('value')).toEqual('5');
15781 expect(element(by.id('link-5')).getAttribute('href')).toBe(null);
15784 it('should only change url when only ng-href', function() {
15785 element(by.model('value')).clear();
15786 element(by.model('value')).sendKeys('6');
15787 expect(element(by.id('link-6')).getAttribute('href')).toMatch(/\/6$/);
15789 element(by.id('link-6')).click();
15791 // At this point, we navigate away from an Angular page, so we need
15792 // to use browser.driver to get the base webdriver.
15793 browser.wait(function() {
15794 return browser.driver.getCurrentUrl().then(function(url) {
15795 return url.match(/\/6$/);
15797 }, 5000, 'page should navigate to /6');
15810 * Using Angular markup like `{{hash}}` in a `src` attribute doesn't
15811 * work right: The browser will fetch from the URL with the literal
15812 * text `{{hash}}` until Angular replaces the expression inside
15813 * `{{hash}}`. The `ngSrc` directive solves this problem.
15815 * The buggy way to write it:
15817 * <img src="http://www.gravatar.com/avatar/{{hash}}"/>
15820 * The correct way to write it:
15822 * <img ng-src="http://www.gravatar.com/avatar/{{hash}}"/>
15826 * @param {template} ngSrc any string which can contain `{{}}` markup.
15836 * Using Angular markup like `{{hash}}` in a `srcset` attribute doesn't
15837 * work right: The browser will fetch from the URL with the literal
15838 * text `{{hash}}` until Angular replaces the expression inside
15839 * `{{hash}}`. The `ngSrcset` directive solves this problem.
15841 * The buggy way to write it:
15843 * <img srcset="http://www.gravatar.com/avatar/{{hash}} 2x"/>
15846 * The correct way to write it:
15848 * <img ng-srcset="http://www.gravatar.com/avatar/{{hash}} 2x"/>
15852 * @param {template} ngSrcset any string which can contain `{{}}` markup.
15863 * We shouldn't do this, because it will make the button enabled on Chrome/Firefox but not on IE8 and older IEs:
15865 * <div ng-init="scope = { isDisabled: false }">
15866 * <button disabled="{{scope.isDisabled}}">Disabled</button>
15870 * The HTML specification does not require browsers to preserve the values of boolean attributes
15871 * such as disabled. (Their presence means true and their absence means false.)
15872 * If we put an Angular interpolation expression into such an attribute then the
15873 * binding information would be lost when the browser removes the attribute.
15874 * The `ngDisabled` directive solves this problem for the `disabled` attribute.
15875 * This complementary directive is not removed by the browser and so provides
15876 * a permanent reliable place to store the binding information.
15880 <file name="index.html">
15881 Click me to toggle: <input type="checkbox" ng-model="checked"><br/>
15882 <button ng-model="button" ng-disabled="checked">Button</button>
15884 <file name="protractor.js" type="protractor">
15885 it('should toggle button', function() {
15886 expect(element(by.css('button')).getAttribute('disabled')).toBeFalsy();
15887 element(by.model('checked')).click();
15888 expect(element(by.css('button')).getAttribute('disabled')).toBeTruthy();
15894 * @param {expression} ngDisabled If the {@link guide/expression expression} is truthy,
15895 * then special attribute "disabled" will be set on the element
15906 * The HTML specification does not require browsers to preserve the values of boolean attributes
15907 * such as checked. (Their presence means true and their absence means false.)
15908 * If we put an Angular interpolation expression into such an attribute then the
15909 * binding information would be lost when the browser removes the attribute.
15910 * The `ngChecked` directive solves this problem for the `checked` attribute.
15911 * This complementary directive is not removed by the browser and so provides
15912 * a permanent reliable place to store the binding information.
15915 <file name="index.html">
15916 Check me to check both: <input type="checkbox" ng-model="master"><br/>
15917 <input id="checkSlave" type="checkbox" ng-checked="master">
15919 <file name="protractor.js" type="protractor">
15920 it('should check both checkBoxes', function() {
15921 expect(element(by.id('checkSlave')).getAttribute('checked')).toBeFalsy();
15922 element(by.model('master')).click();
15923 expect(element(by.id('checkSlave')).getAttribute('checked')).toBeTruthy();
15929 * @param {expression} ngChecked If the {@link guide/expression expression} is truthy,
15930 * then special attribute "checked" will be set on the element
15941 * The HTML specification does not require browsers to preserve the values of boolean attributes
15942 * such as readonly. (Their presence means true and their absence means false.)
15943 * If we put an Angular interpolation expression into such an attribute then the
15944 * binding information would be lost when the browser removes the attribute.
15945 * The `ngReadonly` directive solves this problem for the `readonly` attribute.
15946 * This complementary directive is not removed by the browser and so provides
15947 * a permanent reliable place to store the binding information.
15950 <file name="index.html">
15951 Check me to make text readonly: <input type="checkbox" ng-model="checked"><br/>
15952 <input type="text" ng-readonly="checked" value="I'm Angular"/>
15954 <file name="protractor.js" type="protractor">
15955 it('should toggle readonly attr', function() {
15956 expect(element(by.css('[type="text"]')).getAttribute('readonly')).toBeFalsy();
15957 element(by.model('checked')).click();
15958 expect(element(by.css('[type="text"]')).getAttribute('readonly')).toBeTruthy();
15964 * @param {expression} ngReadonly If the {@link guide/expression expression} is truthy,
15965 * then special attribute "readonly" will be set on the element
15976 * The HTML specification does not require browsers to preserve the values of boolean attributes
15977 * such as selected. (Their presence means true and their absence means false.)
15978 * If we put an Angular interpolation expression into such an attribute then the
15979 * binding information would be lost when the browser removes the attribute.
15980 * The `ngSelected` directive solves this problem for the `selected` attribute.
15981 * This complementary directive is not removed by the browser and so provides
15982 * a permanent reliable place to store the binding information.
15986 <file name="index.html">
15987 Check me to select: <input type="checkbox" ng-model="selected"><br/>
15989 <option>Hello!</option>
15990 <option id="greet" ng-selected="selected">Greetings!</option>
15993 <file name="protractor.js" type="protractor">
15994 it('should select Greetings!', function() {
15995 expect(element(by.id('greet')).getAttribute('selected')).toBeFalsy();
15996 element(by.model('selected')).click();
15997 expect(element(by.id('greet')).getAttribute('selected')).toBeTruthy();
16003 * @param {expression} ngSelected If the {@link guide/expression expression} is truthy,
16004 * then special attribute "selected" will be set on the element
16014 * The HTML specification does not require browsers to preserve the values of boolean attributes
16015 * such as open. (Their presence means true and their absence means false.)
16016 * If we put an Angular interpolation expression into such an attribute then the
16017 * binding information would be lost when the browser removes the attribute.
16018 * The `ngOpen` directive solves this problem for the `open` attribute.
16019 * This complementary directive is not removed by the browser and so provides
16020 * a permanent reliable place to store the binding information.
16023 <file name="index.html">
16024 Check me check multiple: <input type="checkbox" ng-model="open"><br/>
16025 <details id="details" ng-open="open">
16026 <summary>Show/Hide me</summary>
16029 <file name="protractor.js" type="protractor">
16030 it('should toggle open', function() {
16031 expect(element(by.id('details')).getAttribute('open')).toBeFalsy();
16032 element(by.model('open')).click();
16033 expect(element(by.id('details')).getAttribute('open')).toBeTruthy();
16039 * @param {expression} ngOpen If the {@link guide/expression expression} is truthy,
16040 * then special attribute "open" will be set on the element
16043 var ngAttributeAliasDirectives = {};
16046 // boolean attrs are evaluated
16047 forEach(BOOLEAN_ATTR, function(propName, attrName) {
16048 // binding to multiple is not supported
16049 if (propName == "multiple") return;
16051 var normalized = directiveNormalize('ng-' + attrName);
16052 ngAttributeAliasDirectives[normalized] = function() {
16055 link: function(scope, element, attr) {
16056 scope.$watch(attr[normalized], function ngBooleanAttrWatchAction(value) {
16057 attr.$set(attrName, !!value);
16065 // ng-src, ng-srcset, ng-href are interpolated
16066 forEach(['src', 'srcset', 'href'], function(attrName) {
16067 var normalized = directiveNormalize('ng-' + attrName);
16068 ngAttributeAliasDirectives[normalized] = function() {
16070 priority: 99, // it needs to run after the attributes are interpolated
16071 link: function(scope, element, attr) {
16072 var propName = attrName,
16075 if (attrName === 'href' &&
16076 toString.call(element.prop('href')) === '[object SVGAnimatedString]') {
16077 name = 'xlinkHref';
16078 attr.$attr[name] = 'xlink:href';
16082 attr.$observe(normalized, function(value) {
16084 if (attrName === 'href') {
16085 attr.$set(name, null);
16090 attr.$set(name, value);
16092 // on IE, if "ng:src" directive declaration is used and "src" attribute doesn't exist
16093 // then calling element.setAttribute('src', 'foo') doesn't do anything, so we need
16094 // to set the property as well to achieve the desired effect.
16095 // we use attr[attrName] value since $set can sanitize the url.
16096 if (msie && propName) element.prop(propName, attr[name]);
16103 /* global -nullFormCtrl */
16104 var nullFormCtrl = {
16106 $removeControl: noop,
16107 $setValidity: noop,
16114 * @name form.FormController
16116 * @property {boolean} $pristine True if user has not interacted with the form yet.
16117 * @property {boolean} $dirty True if user has already interacted with the form.
16118 * @property {boolean} $valid True if all of the containing forms and controls are valid.
16119 * @property {boolean} $invalid True if at least one containing control or form is invalid.
16121 * @property {Object} $error Is an object hash, containing references to all invalid controls or
16124 * - keys are validation tokens (error names),
16125 * - values are arrays of controls or forms that are invalid for given error name.
16128 * Built-in validation tokens:
16141 * `FormController` keeps track of all its controls and nested forms as well as the state of them,
16142 * such as being valid/invalid or dirty/pristine.
16144 * Each {@link ng.directive:form form} directive creates an instance
16145 * of `FormController`.
16148 //asks for $scope to fool the BC controller module
16149 FormController.$inject = ['$element', '$attrs', '$scope', '$animate'];
16150 function FormController(element, attrs, $scope, $animate) {
16152 parentForm = element.parent().controller('form') || nullFormCtrl,
16153 invalidCount = 0, // used to easily determine if we are valid
16154 errors = form.$error = {},
16158 form.$name = attrs.name || attrs.ngForm;
16159 form.$dirty = false;
16160 form.$pristine = true;
16161 form.$valid = true;
16162 form.$invalid = false;
16164 parentForm.$addControl(form);
16166 // Setup initial state of the control
16167 element.addClass(PRISTINE_CLASS);
16168 toggleValidCss(true);
16170 // convenience method for easy toggling of classes
16171 function toggleValidCss(isValid, validationErrorKey) {
16172 validationErrorKey = validationErrorKey ? '-' + snake_case(validationErrorKey, '-') : '';
16173 $animate.setClass(element,
16174 (isValid ? VALID_CLASS : INVALID_CLASS) + validationErrorKey,
16175 (isValid ? INVALID_CLASS : VALID_CLASS) + validationErrorKey);
16180 * @name form.FormController#$addControl
16183 * Register a control with the form.
16185 * Input elements using ngModelController do this automatically when they are linked.
16187 form.$addControl = function(control) {
16188 // Breaking change - before, inputs whose name was "hasOwnProperty" were quietly ignored
16189 // and not added to the scope. Now we throw an error.
16190 assertNotHasOwnProperty(control.$name, 'input');
16191 controls.push(control);
16193 if (control.$name) {
16194 form[control.$name] = control;
16200 * @name form.FormController#$removeControl
16203 * Deregister a control from the form.
16205 * Input elements using ngModelController do this automatically when they are destroyed.
16207 form.$removeControl = function(control) {
16208 if (control.$name && form[control.$name] === control) {
16209 delete form[control.$name];
16211 forEach(errors, function(queue, validationToken) {
16212 form.$setValidity(validationToken, true, control);
16215 arrayRemove(controls, control);
16220 * @name form.FormController#$setValidity
16223 * Sets the validity of a form control.
16225 * This method will also propagate to parent forms.
16227 form.$setValidity = function(validationToken, isValid, control) {
16228 var queue = errors[validationToken];
16232 arrayRemove(queue, control);
16233 if (!queue.length) {
16235 if (!invalidCount) {
16236 toggleValidCss(isValid);
16237 form.$valid = true;
16238 form.$invalid = false;
16240 errors[validationToken] = false;
16241 toggleValidCss(true, validationToken);
16242 parentForm.$setValidity(validationToken, true, form);
16247 if (!invalidCount) {
16248 toggleValidCss(isValid);
16251 if (includes(queue, control)) return;
16253 errors[validationToken] = queue = [];
16255 toggleValidCss(false, validationToken);
16256 parentForm.$setValidity(validationToken, false, form);
16258 queue.push(control);
16260 form.$valid = false;
16261 form.$invalid = true;
16267 * @name form.FormController#$setDirty
16270 * Sets the form to a dirty state.
16272 * This method can be called to add the 'ng-dirty' class and set the form to a dirty
16273 * state (ng-dirty class). This method will also propagate to parent forms.
16275 form.$setDirty = function() {
16276 $animate.removeClass(element, PRISTINE_CLASS);
16277 $animate.addClass(element, DIRTY_CLASS);
16278 form.$dirty = true;
16279 form.$pristine = false;
16280 parentForm.$setDirty();
16285 * @name form.FormController#$setPristine
16288 * Sets the form to its pristine state.
16290 * This method can be called to remove the 'ng-dirty' class and set the form to its pristine
16291 * state (ng-pristine class). This method will also propagate to all the controls contained
16294 * Setting a form back to a pristine state is often useful when we want to 'reuse' a form after
16295 * saving or resetting it.
16297 form.$setPristine = function () {
16298 $animate.removeClass(element, DIRTY_CLASS);
16299 $animate.addClass(element, PRISTINE_CLASS);
16300 form.$dirty = false;
16301 form.$pristine = true;
16302 forEach(controls, function(control) {
16303 control.$setPristine();
16315 * Nestable alias of {@link ng.directive:form `form`} directive. HTML
16316 * does not allow nesting of form elements. It is useful to nest forms, for example if the validity of a
16317 * sub-group of controls needs to be determined.
16319 * Note: the purpose of `ngForm` is to group controls,
16320 * but not to be a replacement for the `<form>` tag with all of its capabilities
16321 * (e.g. posting to the server, ...).
16323 * @param {string=} ngForm|name Name of the form. If specified, the form controller will be published into
16324 * related scope, under this name.
16334 * Directive that instantiates
16335 * {@link form.FormController FormController}.
16337 * If the `name` attribute is specified, the form controller is published onto the current scope under
16340 * # Alias: {@link ng.directive:ngForm `ngForm`}
16342 * In Angular forms can be nested. This means that the outer form is valid when all of the child
16343 * forms are valid as well. However, browsers do not allow nesting of `<form>` elements, so
16344 * Angular provides the {@link ng.directive:ngForm `ngForm`} directive which behaves identically to
16345 * `<form>` but can be nested. This allows you to have nested forms, which is very useful when
16346 * using Angular validation directives in forms that are dynamically generated using the
16347 * {@link ng.directive:ngRepeat `ngRepeat`} directive. Since you cannot dynamically generate the `name`
16348 * attribute of input elements using interpolation, you have to wrap each set of repeated inputs in an
16349 * `ngForm` directive and nest these in an outer `form` element.
16353 * - `ng-valid` is set if the form is valid.
16354 * - `ng-invalid` is set if the form is invalid.
16355 * - `ng-pristine` is set if the form is pristine.
16356 * - `ng-dirty` is set if the form is dirty.
16358 * Keep in mind that ngAnimate can detect each of these classes when added and removed.
16361 * # Submitting a form and preventing the default action
16363 * Since the role of forms in client-side Angular applications is different than in classical
16364 * roundtrip apps, it is desirable for the browser not to translate the form submission into a full
16365 * page reload that sends the data to the server. Instead some javascript logic should be triggered
16366 * to handle the form submission in an application-specific way.
16368 * For this reason, Angular prevents the default action (form submission to the server) unless the
16369 * `<form>` element has an `action` attribute specified.
16371 * You can use one of the following two ways to specify what javascript method should be called when
16372 * a form is submitted:
16374 * - {@link ng.directive:ngSubmit ngSubmit} directive on the form element
16375 * - {@link ng.directive:ngClick ngClick} directive on the first
16376 * button or input field of type submit (input[type=submit])
16378 * To prevent double execution of the handler, use only one of the {@link ng.directive:ngSubmit ngSubmit}
16379 * or {@link ng.directive:ngClick ngClick} directives.
16380 * This is because of the following form submission rules in the HTML specification:
16382 * - If a form has only one input field then hitting enter in this field triggers form submit
16384 * - if a form has 2+ input fields and no buttons or input[type=submit] then hitting enter
16385 * doesn't trigger submit
16386 * - if a form has one or more input fields and one or more buttons or input[type=submit] then
16387 * hitting enter in any of the input fields will trigger the click handler on the *first* button or
16388 * input[type=submit] (`ngClick`) *and* a submit handler on the enclosing form (`ngSubmit`)
16391 * ## Animation Hooks
16393 * Animations in ngForm are triggered when any of the associated CSS classes are added and removed.
16394 * These classes are: `.ng-pristine`, `.ng-dirty`, `.ng-invalid` and `.ng-valid` as well as any
16395 * other validations that are performed within the form. Animations in ngForm are similar to how
16396 * they work in ngClass and animations can be hooked into using CSS transitions, keyframes as well
16397 * as JS animations.
16399 * The following example shows a simple way to utilize CSS transitions to style a form element
16400 * that has been rendered as invalid after it has been validated:
16403 * //be sure to include ngAnimate as a module to hook into more
16404 * //advanced animations
16406 * transition:0.5s linear all;
16407 * background: white;
16409 * .my-form.ng-invalid {
16416 <example deps="angular-animate.js" animations="true" fixBase="true" module="formExample">
16417 <file name="index.html">
16419 angular.module('formExample', [])
16420 .controller('FormController', ['$scope', function($scope) {
16421 $scope.userType = 'guest';
16426 -webkit-transition:all linear 0.5s;
16427 transition:all linear 0.5s;
16428 background: transparent;
16430 .my-form.ng-invalid {
16434 <form name="myForm" ng-controller="FormController" class="my-form">
16435 userType: <input name="input" ng-model="userType" required>
16436 <span class="error" ng-show="myForm.input.$error.required">Required!</span><br>
16437 <tt>userType = {{userType}}</tt><br>
16438 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br>
16439 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br>
16440 <tt>myForm.$valid = {{myForm.$valid}}</tt><br>
16441 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br>
16444 <file name="protractor.js" type="protractor">
16445 it('should initialize to model', function() {
16446 var userType = element(by.binding('userType'));
16447 var valid = element(by.binding('myForm.input.$valid'));
16449 expect(userType.getText()).toContain('guest');
16450 expect(valid.getText()).toContain('true');
16453 it('should be invalid if empty', function() {
16454 var userType = element(by.binding('userType'));
16455 var valid = element(by.binding('myForm.input.$valid'));
16456 var userInput = element(by.model('userType'));
16459 userInput.sendKeys('');
16461 expect(userType.getText()).toEqual('userType =');
16462 expect(valid.getText()).toContain('false');
16467 * @param {string=} name Name of the form. If specified, the form controller will be published into
16468 * related scope, under this name.
16470 var formDirectiveFactory = function(isNgForm) {
16471 return ['$timeout', function($timeout) {
16472 var formDirective = {
16474 restrict: isNgForm ? 'EAC' : 'E',
16475 controller: FormController,
16476 compile: function() {
16478 pre: function(scope, formElement, attr, controller) {
16479 if (!attr.action) {
16480 // we can't use jq events because if a form is destroyed during submission the default
16481 // action is not prevented. see #1238
16483 // IE 9 is not affected because it doesn't fire a submit event and try to do a full
16484 // page reload if the form was destroyed by submission of the form via a click handler
16485 // on a button in the form. Looks like an IE9 specific bug.
16486 var preventDefaultListener = function(event) {
16487 event.preventDefault
16488 ? event.preventDefault()
16489 : event.returnValue = false; // IE
16492 addEventListenerFn(formElement[0], 'submit', preventDefaultListener);
16494 // unregister the preventDefault listener so that we don't not leak memory but in a
16495 // way that will achieve the prevention of the default action.
16496 formElement.on('$destroy', function() {
16497 $timeout(function() {
16498 removeEventListenerFn(formElement[0], 'submit', preventDefaultListener);
16503 var parentFormCtrl = formElement.parent().controller('form'),
16504 alias = attr.name || attr.ngForm;
16507 setter(scope, alias, controller, alias);
16509 if (parentFormCtrl) {
16510 formElement.on('$destroy', function() {
16511 parentFormCtrl.$removeControl(controller);
16513 setter(scope, alias, undefined, alias);
16515 extend(controller, nullFormCtrl); //stop propagating child destruction handlers upwards
16523 return formDirective;
16527 var formDirective = formDirectiveFactory();
16528 var ngFormDirective = formDirectiveFactory(true);
16530 /* global VALID_CLASS: true,
16531 INVALID_CLASS: true,
16532 PRISTINE_CLASS: true,
16536 var URL_REGEXP = /^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/;
16537 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;
16538 var NUMBER_REGEXP = /^\s*(\-|\+)?(\d+|(\d*(\.\d*)))\s*$/;
16544 * @name input[text]
16547 * Standard HTML text input with angular data binding, inherited by most of the `input` elements.
16549 * *NOTE* Not every feature offered is available for all input types.
16551 * @param {string} ngModel Assignable angular expression to data-bind to.
16552 * @param {string=} name Property name of the form under which the control is published.
16553 * @param {string=} required Adds `required` validation error key if the value is not entered.
16554 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
16555 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
16556 * `required` when you want to data-bind to the `required` attribute.
16557 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
16559 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
16561 * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the
16562 * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for
16563 * patterns defined as scope expressions.
16564 * @param {string=} ngChange Angular expression to be executed when input changes due to user
16565 * interaction with the input element.
16566 * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
16567 * This parameter is ignored for input[type=password] controls, which will never trim the
16571 <example name="text-input-directive" module="textInputExample">
16572 <file name="index.html">
16574 angular.module('textInputExample', [])
16575 .controller('ExampleController', ['$scope', function($scope) {
16576 $scope.text = 'guest';
16577 $scope.word = /^\s*\w*\s*$/;
16580 <form name="myForm" ng-controller="ExampleController">
16581 Single word: <input type="text" name="input" ng-model="text"
16582 ng-pattern="word" required ng-trim="false">
16583 <span class="error" ng-show="myForm.input.$error.required">
16585 <span class="error" ng-show="myForm.input.$error.pattern">
16586 Single word only!</span>
16588 <tt>text = {{text}}</tt><br/>
16589 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
16590 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
16591 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
16592 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
16595 <file name="protractor.js" type="protractor">
16596 var text = element(by.binding('text'));
16597 var valid = element(by.binding('myForm.input.$valid'));
16598 var input = element(by.model('text'));
16600 it('should initialize to model', function() {
16601 expect(text.getText()).toContain('guest');
16602 expect(valid.getText()).toContain('true');
16605 it('should be invalid if empty', function() {
16607 input.sendKeys('');
16609 expect(text.getText()).toEqual('text =');
16610 expect(valid.getText()).toContain('false');
16613 it('should be invalid if multi word', function() {
16615 input.sendKeys('hello world');
16617 expect(valid.getText()).toContain('false');
16622 'text': textInputType,
16627 * @name input[number]
16630 * Text input with number validation and transformation. Sets the `number` validation
16631 * error if not a valid number.
16633 * @param {string} ngModel Assignable angular expression to data-bind to.
16634 * @param {string=} name Property name of the form under which the control is published.
16635 * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
16636 * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
16637 * @param {string=} required Sets `required` validation error key if the value is not entered.
16638 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
16639 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
16640 * `required` when you want to data-bind to the `required` attribute.
16641 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
16643 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
16645 * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the
16646 * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for
16647 * patterns defined as scope expressions.
16648 * @param {string=} ngChange Angular expression to be executed when input changes due to user
16649 * interaction with the input element.
16652 <example name="number-input-directive" module="numberExample">
16653 <file name="index.html">
16655 angular.module('numberExample', [])
16656 .controller('ExampleController', ['$scope', function($scope) {
16660 <form name="myForm" ng-controller="ExampleController">
16661 Number: <input type="number" name="input" ng-model="value"
16662 min="0" max="99" required>
16663 <span class="error" ng-show="myForm.input.$error.required">
16665 <span class="error" ng-show="myForm.input.$error.number">
16666 Not valid number!</span>
16667 <tt>value = {{value}}</tt><br/>
16668 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
16669 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
16670 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
16671 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
16674 <file name="protractor.js" type="protractor">
16675 var value = element(by.binding('value'));
16676 var valid = element(by.binding('myForm.input.$valid'));
16677 var input = element(by.model('value'));
16679 it('should initialize to model', function() {
16680 expect(value.getText()).toContain('12');
16681 expect(valid.getText()).toContain('true');
16684 it('should be invalid if empty', function() {
16686 input.sendKeys('');
16687 expect(value.getText()).toEqual('value =');
16688 expect(valid.getText()).toContain('false');
16691 it('should be invalid if over max', function() {
16693 input.sendKeys('123');
16694 expect(value.getText()).toEqual('value =');
16695 expect(valid.getText()).toContain('false');
16700 'number': numberInputType,
16708 * Text input with URL validation. Sets the `url` validation error key if the content is not a
16711 * @param {string} ngModel Assignable angular expression to data-bind to.
16712 * @param {string=} name Property name of the form under which the control is published.
16713 * @param {string=} required Sets `required` validation error key if the value is not entered.
16714 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
16715 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
16716 * `required` when you want to data-bind to the `required` attribute.
16717 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
16719 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
16721 * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the
16722 * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for
16723 * patterns defined as scope expressions.
16724 * @param {string=} ngChange Angular expression to be executed when input changes due to user
16725 * interaction with the input element.
16728 <example name="url-input-directive" module="urlExample">
16729 <file name="index.html">
16731 angular.module('urlExample', [])
16732 .controller('ExampleController', ['$scope', function($scope) {
16733 $scope.text = 'http://google.com';
16736 <form name="myForm" ng-controller="ExampleController">
16737 URL: <input type="url" name="input" ng-model="text" required>
16738 <span class="error" ng-show="myForm.input.$error.required">
16740 <span class="error" ng-show="myForm.input.$error.url">
16741 Not valid url!</span>
16742 <tt>text = {{text}}</tt><br/>
16743 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
16744 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
16745 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
16746 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
16747 <tt>myForm.$error.url = {{!!myForm.$error.url}}</tt><br/>
16750 <file name="protractor.js" type="protractor">
16751 var text = element(by.binding('text'));
16752 var valid = element(by.binding('myForm.input.$valid'));
16753 var input = element(by.model('text'));
16755 it('should initialize to model', function() {
16756 expect(text.getText()).toContain('http://google.com');
16757 expect(valid.getText()).toContain('true');
16760 it('should be invalid if empty', function() {
16762 input.sendKeys('');
16764 expect(text.getText()).toEqual('text =');
16765 expect(valid.getText()).toContain('false');
16768 it('should be invalid if not url', function() {
16770 input.sendKeys('box');
16772 expect(valid.getText()).toContain('false');
16777 'url': urlInputType,
16782 * @name input[email]
16785 * Text input with email validation. Sets the `email` validation error key if not a valid email
16788 * @param {string} ngModel Assignable angular expression to data-bind to.
16789 * @param {string=} name Property name of the form under which the control is published.
16790 * @param {string=} required Sets `required` validation error key if the value is not entered.
16791 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
16792 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
16793 * `required` when you want to data-bind to the `required` attribute.
16794 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
16796 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
16798 * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the
16799 * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for
16800 * patterns defined as scope expressions.
16801 * @param {string=} ngChange Angular expression to be executed when input changes due to user
16802 * interaction with the input element.
16805 <example name="email-input-directive" module="emailExample">
16806 <file name="index.html">
16808 angular.module('emailExample', [])
16809 .controller('ExampleController', ['$scope', function($scope) {
16810 $scope.text = 'me@example.com';
16813 <form name="myForm" ng-controller="ExampleController">
16814 Email: <input type="email" name="input" ng-model="text" required>
16815 <span class="error" ng-show="myForm.input.$error.required">
16817 <span class="error" ng-show="myForm.input.$error.email">
16818 Not valid email!</span>
16819 <tt>text = {{text}}</tt><br/>
16820 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
16821 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
16822 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
16823 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
16824 <tt>myForm.$error.email = {{!!myForm.$error.email}}</tt><br/>
16827 <file name="protractor.js" type="protractor">
16828 var text = element(by.binding('text'));
16829 var valid = element(by.binding('myForm.input.$valid'));
16830 var input = element(by.model('text'));
16832 it('should initialize to model', function() {
16833 expect(text.getText()).toContain('me@example.com');
16834 expect(valid.getText()).toContain('true');
16837 it('should be invalid if empty', function() {
16839 input.sendKeys('');
16840 expect(text.getText()).toEqual('text =');
16841 expect(valid.getText()).toContain('false');
16844 it('should be invalid if not email', function() {
16846 input.sendKeys('xxx');
16848 expect(valid.getText()).toContain('false');
16853 'email': emailInputType,
16858 * @name input[radio]
16861 * HTML radio button.
16863 * @param {string} ngModel Assignable angular expression to data-bind to.
16864 * @param {string} value The value to which the expression should be set when selected.
16865 * @param {string=} name Property name of the form under which the control is published.
16866 * @param {string=} ngChange Angular expression to be executed when input changes due to user
16867 * interaction with the input element.
16868 * @param {string} ngValue Angular expression which sets the value to which the expression should
16869 * be set when selected.
16872 <example name="radio-input-directive" module="radioExample">
16873 <file name="index.html">
16875 angular.module('radioExample', [])
16876 .controller('ExampleController', ['$scope', function($scope) {
16877 $scope.color = 'blue';
16878 $scope.specialValue = {
16884 <form name="myForm" ng-controller="ExampleController">
16885 <input type="radio" ng-model="color" value="red"> Red <br/>
16886 <input type="radio" ng-model="color" ng-value="specialValue"> Green <br/>
16887 <input type="radio" ng-model="color" value="blue"> Blue <br/>
16888 <tt>color = {{color | json}}</tt><br/>
16890 Note that `ng-value="specialValue"` sets radio item's value to be the value of `$scope.specialValue`.
16892 <file name="protractor.js" type="protractor">
16893 it('should change state', function() {
16894 var color = element(by.binding('color'));
16896 expect(color.getText()).toContain('blue');
16898 element.all(by.model('color')).get(0).click();
16900 expect(color.getText()).toContain('red');
16905 'radio': radioInputType,
16910 * @name input[checkbox]
16915 * @param {string} ngModel Assignable angular expression to data-bind to.
16916 * @param {string=} name Property name of the form under which the control is published.
16917 * @param {string=} ngTrueValue The value to which the expression should be set when selected.
16918 * @param {string=} ngFalseValue The value to which the expression should be set when not selected.
16919 * @param {string=} ngChange Angular expression to be executed when input changes due to user
16920 * interaction with the input element.
16923 <example name="checkbox-input-directive" module="checkboxExample">
16924 <file name="index.html">
16926 angular.module('checkboxExample', [])
16927 .controller('ExampleController', ['$scope', function($scope) {
16928 $scope.value1 = true;
16929 $scope.value2 = 'YES'
16932 <form name="myForm" ng-controller="ExampleController">
16933 Value1: <input type="checkbox" ng-model="value1"> <br/>
16934 Value2: <input type="checkbox" ng-model="value2"
16935 ng-true-value="YES" ng-false-value="NO"> <br/>
16936 <tt>value1 = {{value1}}</tt><br/>
16937 <tt>value2 = {{value2}}</tt><br/>
16940 <file name="protractor.js" type="protractor">
16941 it('should change state', function() {
16942 var value1 = element(by.binding('value1'));
16943 var value2 = element(by.binding('value2'));
16945 expect(value1.getText()).toContain('true');
16946 expect(value2.getText()).toContain('YES');
16948 element(by.model('value1')).click();
16949 element(by.model('value2')).click();
16951 expect(value1.getText()).toContain('false');
16952 expect(value2.getText()).toContain('NO');
16957 'checkbox': checkboxInputType,
16966 // A helper function to call $setValidity and return the value / undefined,
16967 // a pattern that is repeated a lot in the input validation logic.
16968 function validate(ctrl, validatorName, validity, value){
16969 ctrl.$setValidity(validatorName, validity);
16970 return validity ? value : undefined;
16973 function testFlags(validity, flags) {
16976 for (i=0; i<flags.length; ++i) {
16978 if (validity[flag]) {
16986 // Pass validity so that behaviour can be mocked easier.
16987 function addNativeHtml5Validators(ctrl, validatorName, badFlags, ignoreFlags, validity) {
16988 if (isObject(validity)) {
16989 ctrl.$$hasNativeValidators = true;
16990 var validator = function(value) {
16991 // Don't overwrite previous validation, don't consider valueMissing to apply (ng-required can
16992 // perform the required validation)
16993 if (!ctrl.$error[validatorName] &&
16994 !testFlags(validity, ignoreFlags) &&
16995 testFlags(validity, badFlags)) {
16996 ctrl.$setValidity(validatorName, false);
17001 ctrl.$parsers.push(validator);
17005 function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
17006 var validity = element.prop(VALIDITY_STATE_PROPERTY);
17007 var placeholder = element[0].placeholder, noevent = {};
17008 var type = lowercase(element[0].type);
17009 ctrl.$$validityState = validity;
17011 // In composition mode, users are still inputing intermediate text buffer,
17012 // hold the listener until composition is done.
17013 // More about composition events: https://developer.mozilla.org/en-US/docs/Web/API/CompositionEvent
17014 if (!$sniffer.android) {
17015 var composing = false;
17017 element.on('compositionstart', function(data) {
17021 element.on('compositionend', function() {
17027 var listener = function(ev) {
17028 if (composing) return;
17029 var value = element.val();
17031 // IE (11 and under) seem to emit an 'input' event if the placeholder value changes.
17032 // We don't want to dirty the value when this happens, so we abort here. Unfortunately,
17033 // IE also sends input events for other non-input-related things, (such as focusing on a
17034 // form control), so this change is not entirely enough to solve this.
17035 if (msie && (ev || noevent).type === 'input' && element[0].placeholder !== placeholder) {
17036 placeholder = element[0].placeholder;
17040 // By default we will trim the value
17041 // If the attribute ng-trim exists we will avoid trimming
17042 // If input type is 'password', the value is never trimmed
17043 if (type !== 'password' && (toBoolean(attr.ngTrim || 'T'))) {
17044 value = trim(value);
17047 // If a control is suffering from bad input, browsers discard its value, so it may be
17048 // necessary to revalidate even if the control's value is the same empty value twice in
17050 var revalidate = validity && ctrl.$$hasNativeValidators;
17051 if (ctrl.$viewValue !== value || (value === '' && revalidate)) {
17052 if (scope.$root.$$phase) {
17053 ctrl.$setViewValue(value);
17055 scope.$apply(function() {
17056 ctrl.$setViewValue(value);
17062 // if the browser does support "input" event, we are fine - except on IE9 which doesn't fire the
17063 // input event on backspace, delete or cut
17064 if ($sniffer.hasEvent('input')) {
17065 element.on('input', listener);
17069 var deferListener = function() {
17071 timeout = $browser.defer(function() {
17078 element.on('keydown', function(event) {
17079 var key = event.keyCode;
17082 // command modifiers arrows
17083 if (key === 91 || (15 < key && key < 19) || (37 <= key && key <= 40)) return;
17088 // if user modifies input value using context menu in IE, we need "paste" and "cut" events to catch it
17089 if ($sniffer.hasEvent('paste')) {
17090 element.on('paste cut', deferListener);
17094 // if user paste into input using mouse on older browser
17095 // or form autocomplete on newer browser, we need "change" event to catch it
17096 element.on('change', listener);
17098 ctrl.$render = function() {
17099 element.val(ctrl.$isEmpty(ctrl.$viewValue) ? '' : ctrl.$viewValue);
17102 // pattern validator
17103 var pattern = attr.ngPattern,
17108 var validateRegex = function(regexp, value) {
17109 return validate(ctrl, 'pattern', ctrl.$isEmpty(value) || regexp.test(value), value);
17111 match = pattern.match(/^\/(.*)\/([gim]*)$/);
17113 pattern = new RegExp(match[1], match[2]);
17114 patternValidator = function(value) {
17115 return validateRegex(pattern, value);
17118 patternValidator = function(value) {
17119 var patternObj = scope.$eval(pattern);
17121 if (!patternObj || !patternObj.test) {
17122 throw minErr('ngPattern')('noregexp',
17123 'Expected {0} to be a RegExp but was {1}. Element: {2}', pattern,
17124 patternObj, startingTag(element));
17126 return validateRegex(patternObj, value);
17130 ctrl.$formatters.push(patternValidator);
17131 ctrl.$parsers.push(patternValidator);
17134 // min length validator
17135 if (attr.ngMinlength) {
17136 var minlength = int(attr.ngMinlength);
17137 var minLengthValidator = function(value) {
17138 return validate(ctrl, 'minlength', ctrl.$isEmpty(value) || value.length >= minlength, value);
17141 ctrl.$parsers.push(minLengthValidator);
17142 ctrl.$formatters.push(minLengthValidator);
17145 // max length validator
17146 if (attr.ngMaxlength) {
17147 var maxlength = int(attr.ngMaxlength);
17148 var maxLengthValidator = function(value) {
17149 return validate(ctrl, 'maxlength', ctrl.$isEmpty(value) || value.length <= maxlength, value);
17152 ctrl.$parsers.push(maxLengthValidator);
17153 ctrl.$formatters.push(maxLengthValidator);
17157 var numberBadFlags = ['badInput'];
17159 function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) {
17160 textInputType(scope, element, attr, ctrl, $sniffer, $browser);
17162 ctrl.$parsers.push(function(value) {
17163 var empty = ctrl.$isEmpty(value);
17164 if (empty || NUMBER_REGEXP.test(value)) {
17165 ctrl.$setValidity('number', true);
17166 return value === '' ? null : (empty ? value : parseFloat(value));
17168 ctrl.$setValidity('number', false);
17173 addNativeHtml5Validators(ctrl, 'number', numberBadFlags, null, ctrl.$$validityState);
17175 ctrl.$formatters.push(function(value) {
17176 return ctrl.$isEmpty(value) ? '' : '' + value;
17180 var minValidator = function(value) {
17181 var min = parseFloat(attr.min);
17182 return validate(ctrl, 'min', ctrl.$isEmpty(value) || value >= min, value);
17185 ctrl.$parsers.push(minValidator);
17186 ctrl.$formatters.push(minValidator);
17190 var maxValidator = function(value) {
17191 var max = parseFloat(attr.max);
17192 return validate(ctrl, 'max', ctrl.$isEmpty(value) || value <= max, value);
17195 ctrl.$parsers.push(maxValidator);
17196 ctrl.$formatters.push(maxValidator);
17199 ctrl.$formatters.push(function(value) {
17200 return validate(ctrl, 'number', ctrl.$isEmpty(value) || isNumber(value), value);
17204 function urlInputType(scope, element, attr, ctrl, $sniffer, $browser) {
17205 textInputType(scope, element, attr, ctrl, $sniffer, $browser);
17207 var urlValidator = function(value) {
17208 return validate(ctrl, 'url', ctrl.$isEmpty(value) || URL_REGEXP.test(value), value);
17211 ctrl.$formatters.push(urlValidator);
17212 ctrl.$parsers.push(urlValidator);
17215 function emailInputType(scope, element, attr, ctrl, $sniffer, $browser) {
17216 textInputType(scope, element, attr, ctrl, $sniffer, $browser);
17218 var emailValidator = function(value) {
17219 return validate(ctrl, 'email', ctrl.$isEmpty(value) || EMAIL_REGEXP.test(value), value);
17222 ctrl.$formatters.push(emailValidator);
17223 ctrl.$parsers.push(emailValidator);
17226 function radioInputType(scope, element, attr, ctrl) {
17227 // make the name unique, if not defined
17228 if (isUndefined(attr.name)) {
17229 element.attr('name', nextUid());
17232 element.on('click', function() {
17233 if (element[0].checked) {
17234 scope.$apply(function() {
17235 ctrl.$setViewValue(attr.value);
17240 ctrl.$render = function() {
17241 var value = attr.value;
17242 element[0].checked = (value == ctrl.$viewValue);
17245 attr.$observe('value', ctrl.$render);
17248 function checkboxInputType(scope, element, attr, ctrl) {
17249 var trueValue = attr.ngTrueValue,
17250 falseValue = attr.ngFalseValue;
17252 if (!isString(trueValue)) trueValue = true;
17253 if (!isString(falseValue)) falseValue = false;
17255 element.on('click', function() {
17256 scope.$apply(function() {
17257 ctrl.$setViewValue(element[0].checked);
17261 ctrl.$render = function() {
17262 element[0].checked = ctrl.$viewValue;
17265 // Override the standard `$isEmpty` because a value of `false` means empty in a checkbox.
17266 ctrl.$isEmpty = function(value) {
17267 return value !== trueValue;
17270 ctrl.$formatters.push(function(value) {
17271 return value === trueValue;
17274 ctrl.$parsers.push(function(value) {
17275 return value ? trueValue : falseValue;
17286 * HTML textarea element control with angular data-binding. The data-binding and validation
17287 * properties of this element are exactly the same as those of the
17288 * {@link ng.directive:input input element}.
17290 * @param {string} ngModel Assignable angular expression to data-bind to.
17291 * @param {string=} name Property name of the form under which the control is published.
17292 * @param {string=} required Sets `required` validation error key if the value is not entered.
17293 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
17294 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
17295 * `required` when you want to data-bind to the `required` attribute.
17296 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
17298 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
17300 * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the
17301 * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for
17302 * patterns defined as scope expressions.
17303 * @param {string=} ngChange Angular expression to be executed when input changes due to user
17304 * interaction with the input element.
17305 * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
17315 * HTML input element control with angular data-binding. Input control follows HTML5 input types
17316 * and polyfills the HTML5 validation behavior for older browsers.
17318 * *NOTE* Not every feature offered is available for all input types.
17320 * @param {string} ngModel Assignable angular expression to data-bind to.
17321 * @param {string=} name Property name of the form under which the control is published.
17322 * @param {string=} required Sets `required` validation error key if the value is not entered.
17323 * @param {boolean=} ngRequired Sets `required` attribute if set to true
17324 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
17326 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
17328 * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the
17329 * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for
17330 * patterns defined as scope expressions.
17331 * @param {string=} ngChange Angular expression to be executed when input changes due to user
17332 * interaction with the input element.
17333 * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
17334 * This parameter is ignored for input[type=password] controls, which will never trim the
17338 <example name="input-directive" module="inputExample">
17339 <file name="index.html">
17341 angular.module('inputExample', [])
17342 .controller('ExampleController', ['$scope', function($scope) {
17343 $scope.user = {name: 'guest', last: 'visitor'};
17346 <div ng-controller="ExampleController">
17347 <form name="myForm">
17348 User name: <input type="text" name="userName" ng-model="user.name" required>
17349 <span class="error" ng-show="myForm.userName.$error.required">
17350 Required!</span><br>
17351 Last name: <input type="text" name="lastName" ng-model="user.last"
17352 ng-minlength="3" ng-maxlength="10">
17353 <span class="error" ng-show="myForm.lastName.$error.minlength">
17355 <span class="error" ng-show="myForm.lastName.$error.maxlength">
17356 Too long!</span><br>
17359 <tt>user = {{user}}</tt><br/>
17360 <tt>myForm.userName.$valid = {{myForm.userName.$valid}}</tt><br>
17361 <tt>myForm.userName.$error = {{myForm.userName.$error}}</tt><br>
17362 <tt>myForm.lastName.$valid = {{myForm.lastName.$valid}}</tt><br>
17363 <tt>myForm.lastName.$error = {{myForm.lastName.$error}}</tt><br>
17364 <tt>myForm.$valid = {{myForm.$valid}}</tt><br>
17365 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br>
17366 <tt>myForm.$error.minlength = {{!!myForm.$error.minlength}}</tt><br>
17367 <tt>myForm.$error.maxlength = {{!!myForm.$error.maxlength}}</tt><br>
17370 <file name="protractor.js" type="protractor">
17371 var user = element(by.binding('{{user}}'));
17372 var userNameValid = element(by.binding('myForm.userName.$valid'));
17373 var lastNameValid = element(by.binding('myForm.lastName.$valid'));
17374 var lastNameError = element(by.binding('myForm.lastName.$error'));
17375 var formValid = element(by.binding('myForm.$valid'));
17376 var userNameInput = element(by.model('user.name'));
17377 var userLastInput = element(by.model('user.last'));
17379 it('should initialize to model', function() {
17380 expect(user.getText()).toContain('{"name":"guest","last":"visitor"}');
17381 expect(userNameValid.getText()).toContain('true');
17382 expect(formValid.getText()).toContain('true');
17385 it('should be invalid if empty when required', function() {
17386 userNameInput.clear();
17387 userNameInput.sendKeys('');
17389 expect(user.getText()).toContain('{"last":"visitor"}');
17390 expect(userNameValid.getText()).toContain('false');
17391 expect(formValid.getText()).toContain('false');
17394 it('should be valid if empty when min length is set', function() {
17395 userLastInput.clear();
17396 userLastInput.sendKeys('');
17398 expect(user.getText()).toContain('{"name":"guest","last":""}');
17399 expect(lastNameValid.getText()).toContain('true');
17400 expect(formValid.getText()).toContain('true');
17403 it('should be invalid if less than required min length', function() {
17404 userLastInput.clear();
17405 userLastInput.sendKeys('xx');
17407 expect(user.getText()).toContain('{"name":"guest"}');
17408 expect(lastNameValid.getText()).toContain('false');
17409 expect(lastNameError.getText()).toContain('minlength');
17410 expect(formValid.getText()).toContain('false');
17413 it('should be invalid if longer than max length', function() {
17414 userLastInput.clear();
17415 userLastInput.sendKeys('some ridiculously long name');
17417 expect(user.getText()).toContain('{"name":"guest"}');
17418 expect(lastNameValid.getText()).toContain('false');
17419 expect(lastNameError.getText()).toContain('maxlength');
17420 expect(formValid.getText()).toContain('false');
17425 var inputDirective = ['$browser', '$sniffer', function($browser, $sniffer) {
17428 require: '?ngModel',
17429 link: function(scope, element, attr, ctrl) {
17431 (inputType[lowercase(attr.type)] || inputType.text)(scope, element, attr, ctrl, $sniffer,
17438 var VALID_CLASS = 'ng-valid',
17439 INVALID_CLASS = 'ng-invalid',
17440 PRISTINE_CLASS = 'ng-pristine',
17441 DIRTY_CLASS = 'ng-dirty';
17445 * @name ngModel.NgModelController
17447 * @property {string} $viewValue Actual string value in the view.
17448 * @property {*} $modelValue The value in the model, that the control is bound to.
17449 * @property {Array.<Function>} $parsers Array of functions to execute, as a pipeline, whenever
17450 the control reads value from the DOM. Each function is called, in turn, passing the value
17451 through to the next. The last return value is used to populate the model.
17452 Used to sanitize / convert the value as well as validation. For validation,
17453 the parsers should update the validity state using
17454 {@link ngModel.NgModelController#$setValidity $setValidity()},
17455 and return `undefined` for invalid values.
17458 * @property {Array.<Function>} $formatters Array of functions to execute, as a pipeline, whenever
17459 the model value changes. Each function is called, in turn, passing the value through to the
17460 next. Used to format / convert values for display in the control and validation.
17462 * function formatter(value) {
17464 * return value.toUpperCase();
17467 * ngModel.$formatters.push(formatter);
17470 * @property {Array.<Function>} $viewChangeListeners Array of functions to execute whenever the
17471 * view value has changed. It is called with no arguments, and its return value is ignored.
17472 * This can be used in place of additional $watches against the model value.
17474 * @property {Object} $error An object hash with all errors as keys.
17476 * @property {boolean} $pristine True if user has not interacted with the control yet.
17477 * @property {boolean} $dirty True if user has already interacted with the control.
17478 * @property {boolean} $valid True if there is no error.
17479 * @property {boolean} $invalid True if at least one error on the control.
17483 * `NgModelController` provides API for the `ng-model` directive. The controller contains
17484 * services for data-binding, validation, CSS updates, and value formatting and parsing. It
17485 * purposefully does not contain any logic which deals with DOM rendering or listening to
17486 * DOM events. Such DOM related logic should be provided by other directives which make use of
17487 * `NgModelController` for data-binding.
17489 * ## Custom Control Example
17490 * This example shows how to use `NgModelController` with a custom control to achieve
17491 * data-binding. Notice how different directives (`contenteditable`, `ng-model`, and `required`)
17492 * collaborate together to achieve the desired result.
17494 * Note that `contenteditable` is an HTML5 attribute, which tells the browser to let the element
17495 * contents be edited in place by the user. This will not work on older browsers.
17497 * We are using the {@link ng.service:$sce $sce} service here and include the {@link ngSanitize $sanitize}
17498 * module to automatically remove "bad" content like inline event listener (e.g. `<span onclick="...">`).
17499 * However, as we are using `$sce` the model can still decide to to provide unsafe content if it marks
17500 * that content using the `$sce` service.
17502 * <example name="NgModelController" module="customControl" deps="angular-sanitize.js">
17503 <file name="style.css">
17504 [contenteditable] {
17505 border: 1px solid black;
17506 background-color: white;
17511 border: 1px solid red;
17515 <file name="script.js">
17516 angular.module('customControl', ['ngSanitize']).
17517 directive('contenteditable', ['$sce', function($sce) {
17519 restrict: 'A', // only activate on element attribute
17520 require: '?ngModel', // get a hold of NgModelController
17521 link: function(scope, element, attrs, ngModel) {
17522 if(!ngModel) return; // do nothing if no ng-model
17524 // Specify how UI should be updated
17525 ngModel.$render = function() {
17526 element.html($sce.getTrustedHtml(ngModel.$viewValue || ''));
17529 // Listen for change events to enable binding
17530 element.on('blur keyup change', function() {
17531 scope.$apply(read);
17533 read(); // initialize
17535 // Write data to the model
17537 var html = element.html();
17538 // When we clear the content editable the browser leaves a <br> behind
17539 // If strip-br attribute is provided then we strip this out
17540 if( attrs.stripBr && html == '<br>' ) {
17543 ngModel.$setViewValue(html);
17549 <file name="index.html">
17550 <form name="myForm">
17551 <div contenteditable
17552 name="myWidget" ng-model="userContent"
17554 required>Change me!</div>
17555 <span ng-show="myForm.myWidget.$error.required">Required!</span>
17557 <textarea ng-model="userContent"></textarea>
17560 <file name="protractor.js" type="protractor">
17561 it('should data-bind and become invalid', function() {
17562 if (browser.params.browser == 'safari' || browser.params.browser == 'firefox') {
17563 // SafariDriver can't handle contenteditable
17564 // and Firefox driver can't clear contenteditables very well
17567 var contentEditable = element(by.css('[contenteditable]'));
17568 var content = 'Change me!';
17570 expect(contentEditable.getText()).toEqual(content);
17572 contentEditable.clear();
17573 contentEditable.sendKeys(protractor.Key.BACK_SPACE);
17574 expect(contentEditable.getText()).toEqual('');
17575 expect(contentEditable.getAttribute('class')).toMatch(/ng-invalid-required/);
17582 var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$parse', '$animate',
17583 function($scope, $exceptionHandler, $attr, $element, $parse, $animate) {
17584 this.$viewValue = Number.NaN;
17585 this.$modelValue = Number.NaN;
17586 this.$parsers = [];
17587 this.$formatters = [];
17588 this.$viewChangeListeners = [];
17589 this.$pristine = true;
17590 this.$dirty = false;
17591 this.$valid = true;
17592 this.$invalid = false;
17593 this.$name = $attr.name;
17595 var ngModelGet = $parse($attr.ngModel),
17596 ngModelSet = ngModelGet.assign;
17599 throw minErr('ngModel')('nonassign', "Expression '{0}' is non-assignable. Element: {1}",
17600 $attr.ngModel, startingTag($element));
17605 * @name ngModel.NgModelController#$render
17608 * Called when the view needs to be updated. It is expected that the user of the ng-model
17609 * directive will implement this method.
17611 this.$render = noop;
17615 * @name ngModel.NgModelController#$isEmpty
17618 * This is called when we need to determine if the value of the input is empty.
17620 * For instance, the required directive does this to work out if the input has data or not.
17621 * The default `$isEmpty` function checks whether the value is `undefined`, `''`, `null` or `NaN`.
17623 * You can override this for input directives whose concept of being empty is different to the
17624 * default. The `checkboxInputType` directive does this because in its case a value of `false`
17627 * @param {*} value Reference to check.
17628 * @returns {boolean} True if `value` is empty.
17630 this.$isEmpty = function(value) {
17631 return isUndefined(value) || value === '' || value === null || value !== value;
17634 var parentForm = $element.inheritedData('$formController') || nullFormCtrl,
17635 invalidCount = 0, // used to easily determine if we are valid
17636 $error = this.$error = {}; // keep invalid keys here
17639 // Setup initial state of the control
17640 $element.addClass(PRISTINE_CLASS);
17641 toggleValidCss(true);
17643 // convenience method for easy toggling of classes
17644 function toggleValidCss(isValid, validationErrorKey) {
17645 validationErrorKey = validationErrorKey ? '-' + snake_case(validationErrorKey, '-') : '';
17646 $animate.removeClass($element, (isValid ? INVALID_CLASS : VALID_CLASS) + validationErrorKey);
17647 $animate.addClass($element, (isValid ? VALID_CLASS : INVALID_CLASS) + validationErrorKey);
17652 * @name ngModel.NgModelController#$setValidity
17655 * Change the validity state, and notifies the form when the control changes validity. (i.e. it
17656 * does not notify form if given validator is already marked as invalid).
17658 * This method should be called by validators - i.e. the parser or formatter functions.
17660 * @param {string} validationErrorKey Name of the validator. the `validationErrorKey` will assign
17661 * to `$error[validationErrorKey]=!isValid` so that it is available for data-binding.
17662 * The `validationErrorKey` should be in camelCase and will get converted into dash-case
17663 * for class name. Example: `myError` will result in `ng-valid-my-error` and `ng-invalid-my-error`
17664 * class and can be bound to as `{{someForm.someControl.$error.myError}}` .
17665 * @param {boolean} isValid Whether the current state is valid (true) or invalid (false).
17667 this.$setValidity = function(validationErrorKey, isValid) {
17668 // Purposeful use of ! here to cast isValid to boolean in case it is undefined
17670 if ($error[validationErrorKey] === !isValid) return;
17674 if ($error[validationErrorKey]) invalidCount--;
17675 if (!invalidCount) {
17676 toggleValidCss(true);
17677 this.$valid = true;
17678 this.$invalid = false;
17681 toggleValidCss(false);
17682 this.$invalid = true;
17683 this.$valid = false;
17687 $error[validationErrorKey] = !isValid;
17688 toggleValidCss(isValid, validationErrorKey);
17690 parentForm.$setValidity(validationErrorKey, isValid, this);
17695 * @name ngModel.NgModelController#$setPristine
17698 * Sets the control to its pristine state.
17700 * This method can be called to remove the 'ng-dirty' class and set the control to its pristine
17701 * state (ng-pristine class).
17703 this.$setPristine = function () {
17704 this.$dirty = false;
17705 this.$pristine = true;
17706 $animate.removeClass($element, DIRTY_CLASS);
17707 $animate.addClass($element, PRISTINE_CLASS);
17712 * @name ngModel.NgModelController#$setViewValue
17715 * Update the view value.
17717 * This method should be called when the view value changes, typically from within a DOM event handler.
17718 * For example {@link ng.directive:input input} and
17719 * {@link ng.directive:select select} directives call it.
17721 * It will update the $viewValue, then pass this value through each of the functions in `$parsers`,
17722 * which includes any validators. The value that comes out of this `$parsers` pipeline, be applied to
17723 * `$modelValue` and the **expression** specified in the `ng-model` attribute.
17725 * Lastly, all the registered change listeners, in the `$viewChangeListeners` list, are called.
17727 * Note that calling this function does not trigger a `$digest`.
17729 * @param {string} value Value from the view.
17731 this.$setViewValue = function(value) {
17732 this.$viewValue = value;
17735 if (this.$pristine) {
17736 this.$dirty = true;
17737 this.$pristine = false;
17738 $animate.removeClass($element, PRISTINE_CLASS);
17739 $animate.addClass($element, DIRTY_CLASS);
17740 parentForm.$setDirty();
17743 forEach(this.$parsers, function(fn) {
17747 if (this.$modelValue !== value) {
17748 this.$modelValue = value;
17749 ngModelSet($scope, value);
17750 forEach(this.$viewChangeListeners, function(listener) {
17754 $exceptionHandler(e);
17763 $scope.$watch(function ngModelWatch() {
17764 var value = ngModelGet($scope);
17766 // if scope model value and ngModel value are out of sync
17767 if (ctrl.$modelValue !== value) {
17769 var formatters = ctrl.$formatters,
17770 idx = formatters.length;
17772 ctrl.$modelValue = value;
17774 value = formatters[idx](value);
17777 if (ctrl.$viewValue !== value) {
17778 ctrl.$viewValue = value;
17795 * The `ngModel` directive binds an `input`,`select`, `textarea` (or custom form control) to a
17796 * property on the scope using {@link ngModel.NgModelController NgModelController},
17797 * which is created and exposed by this directive.
17799 * `ngModel` is responsible for:
17801 * - Binding the view into the model, which other directives such as `input`, `textarea` or `select`
17803 * - Providing validation behavior (i.e. required, number, email, url).
17804 * - Keeping the state of the control (valid/invalid, dirty/pristine, validation errors).
17805 * - Setting related css classes on the element (`ng-valid`, `ng-invalid`, `ng-dirty`, `ng-pristine`) including animations.
17806 * - Registering the control with its parent {@link ng.directive:form form}.
17808 * Note: `ngModel` will try to bind to the property given by evaluating the expression on the
17809 * current scope. If the property doesn't already exist on this scope, it will be created
17810 * implicitly and added to the scope.
17812 * For best practices on using `ngModel`, see:
17814 * - [https://github.com/angular/angular.js/wiki/Understanding-Scopes]
17816 * For basic examples, how to use `ngModel`, see:
17818 * - {@link ng.directive:input input}
17819 * - {@link input[text] text}
17820 * - {@link input[checkbox] checkbox}
17821 * - {@link input[radio] radio}
17822 * - {@link input[number] number}
17823 * - {@link input[email] email}
17824 * - {@link input[url] url}
17825 * - {@link ng.directive:select select}
17826 * - {@link ng.directive:textarea textarea}
17829 * The following CSS classes are added and removed on the associated input/select/textarea element
17830 * depending on the validity of the model.
17832 * - `ng-valid` is set if the model is valid.
17833 * - `ng-invalid` is set if the model is invalid.
17834 * - `ng-pristine` is set if the model is pristine.
17835 * - `ng-dirty` is set if the model is dirty.
17837 * Keep in mind that ngAnimate can detect each of these classes when added and removed.
17839 * ## Animation Hooks
17841 * Animations within models are triggered when any of the associated CSS classes are added and removed
17842 * on the input element which is attached to the model. These classes are: `.ng-pristine`, `.ng-dirty`,
17843 * `.ng-invalid` and `.ng-valid` as well as any other validations that are performed on the model itself.
17844 * The animations that are triggered within ngModel are similar to how they work in ngClass and
17845 * animations can be hooked into using CSS transitions, keyframes as well as JS animations.
17847 * The following example shows a simple way to utilize CSS transitions to style an input element
17848 * that has been rendered as invalid after it has been validated:
17851 * //be sure to include ngAnimate as a module to hook into more
17852 * //advanced animations
17854 * transition:0.5s linear all;
17855 * background: white;
17857 * .my-input.ng-invalid {
17864 * <example deps="angular-animate.js" animations="true" fixBase="true" module="inputExample">
17865 <file name="index.html">
17867 angular.module('inputExample', [])
17868 .controller('ExampleController', ['$scope', function($scope) {
17874 -webkit-transition:all linear 0.5s;
17875 transition:all linear 0.5s;
17876 background: transparent;
17878 .my-input.ng-invalid {
17883 Update input to see transitions when valid/invalid.
17884 Integer is a valid value.
17885 <form name="testForm" ng-controller="ExampleController">
17886 <input ng-model="val" ng-pattern="/^\d+$/" name="anim" class="my-input" />
17891 var ngModelDirective = function() {
17893 require: ['ngModel', '^?form'],
17894 controller: NgModelController,
17895 link: function(scope, element, attr, ctrls) {
17896 // notify others, especially parent forms
17898 var modelCtrl = ctrls[0],
17899 formCtrl = ctrls[1] || nullFormCtrl;
17901 formCtrl.$addControl(modelCtrl);
17903 scope.$on('$destroy', function() {
17904 formCtrl.$removeControl(modelCtrl);
17916 * Evaluate the given expression when the user changes the input.
17917 * The expression is evaluated immediately, unlike the JavaScript onchange event
17918 * which only triggers at the end of a change (usually, when the user leaves the
17919 * form element or presses the return key).
17920 * The expression is not evaluated when the value change is coming from the model.
17922 * Note, this directive requires `ngModel` to be present.
17925 * @param {expression} ngChange {@link guide/expression Expression} to evaluate upon change
17929 * <example name="ngChange-directive" module="changeExample">
17930 * <file name="index.html">
17932 * angular.module('changeExample', [])
17933 * .controller('ExampleController', ['$scope', function($scope) {
17934 * $scope.counter = 0;
17935 * $scope.change = function() {
17936 * $scope.counter++;
17940 * <div ng-controller="ExampleController">
17941 * <input type="checkbox" ng-model="confirmed" ng-change="change()" id="ng-change-example1" />
17942 * <input type="checkbox" ng-model="confirmed" id="ng-change-example2" />
17943 * <label for="ng-change-example2">Confirmed</label><br />
17944 * <tt>debug = {{confirmed}}</tt><br/>
17945 * <tt>counter = {{counter}}</tt><br/>
17948 * <file name="protractor.js" type="protractor">
17949 * var counter = element(by.binding('counter'));
17950 * var debug = element(by.binding('confirmed'));
17952 * it('should evaluate the expression if changing from view', function() {
17953 * expect(counter.getText()).toContain('0');
17955 * element(by.id('ng-change-example1')).click();
17957 * expect(counter.getText()).toContain('1');
17958 * expect(debug.getText()).toContain('true');
17961 * it('should not evaluate the expression if changing from model', function() {
17962 * element(by.id('ng-change-example2')).click();
17964 * expect(counter.getText()).toContain('0');
17965 * expect(debug.getText()).toContain('true');
17970 var ngChangeDirective = valueFn({
17971 require: 'ngModel',
17972 link: function(scope, element, attr, ctrl) {
17973 ctrl.$viewChangeListeners.push(function() {
17974 scope.$eval(attr.ngChange);
17980 var requiredDirective = function() {
17982 require: '?ngModel',
17983 link: function(scope, elm, attr, ctrl) {
17985 attr.required = true; // force truthy in case we are on non input element
17987 var validator = function(value) {
17988 if (attr.required && ctrl.$isEmpty(value)) {
17989 ctrl.$setValidity('required', false);
17992 ctrl.$setValidity('required', true);
17997 ctrl.$formatters.push(validator);
17998 ctrl.$parsers.unshift(validator);
18000 attr.$observe('required', function() {
18001 validator(ctrl.$viewValue);
18013 * Text input that converts between a delimited string and an array of strings. The delimiter
18014 * can be a fixed string (by default a comma) or a regular expression.
18017 * @param {string=} ngList optional delimiter that should be used to split the value. If
18018 * specified in form `/something/` then the value will be converted into a regular expression.
18021 <example name="ngList-directive" module="listExample">
18022 <file name="index.html">
18024 angular.module('listExample', [])
18025 .controller('ExampleController', ['$scope', function($scope) {
18026 $scope.names = ['igor', 'misko', 'vojta'];
18029 <form name="myForm" ng-controller="ExampleController">
18030 List: <input name="namesInput" ng-model="names" ng-list required>
18031 <span class="error" ng-show="myForm.namesInput.$error.required">
18034 <tt>names = {{names}}</tt><br/>
18035 <tt>myForm.namesInput.$valid = {{myForm.namesInput.$valid}}</tt><br/>
18036 <tt>myForm.namesInput.$error = {{myForm.namesInput.$error}}</tt><br/>
18037 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
18038 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
18041 <file name="protractor.js" type="protractor">
18042 var listInput = element(by.model('names'));
18043 var names = element(by.binding('{{names}}'));
18044 var valid = element(by.binding('myForm.namesInput.$valid'));
18045 var error = element(by.css('span.error'));
18047 it('should initialize to model', function() {
18048 expect(names.getText()).toContain('["igor","misko","vojta"]');
18049 expect(valid.getText()).toContain('true');
18050 expect(error.getCssValue('display')).toBe('none');
18053 it('should be invalid if empty', function() {
18055 listInput.sendKeys('');
18057 expect(names.getText()).toContain('');
18058 expect(valid.getText()).toContain('false');
18059 expect(error.getCssValue('display')).not.toBe('none'); });
18063 var ngListDirective = function() {
18065 require: 'ngModel',
18066 link: function(scope, element, attr, ctrl) {
18067 var match = /\/(.*)\//.exec(attr.ngList),
18068 separator = match && new RegExp(match[1]) || attr.ngList || ',';
18070 var parse = function(viewValue) {
18071 // If the viewValue is invalid (say required but empty) it will be `undefined`
18072 if (isUndefined(viewValue)) return;
18077 forEach(viewValue.split(separator), function(value) {
18078 if (value) list.push(trim(value));
18085 ctrl.$parsers.push(parse);
18086 ctrl.$formatters.push(function(value) {
18087 if (isArray(value)) {
18088 return value.join(', ');
18094 // Override the standard $isEmpty because an empty array means the input is empty.
18095 ctrl.$isEmpty = function(value) {
18096 return !value || !value.length;
18103 var CONSTANT_VALUE_REGEXP = /^(true|false|\d+)$/;
18109 * Binds the given expression to the value of `input[select]` or `input[radio]`, so
18110 * that when the element is selected, the `ngModel` of that element is set to the
18113 * `ngValue` is useful when dynamically generating lists of radio buttons using `ng-repeat`, as
18117 * @param {string=} ngValue angular expression, whose value will be bound to the `value` attribute
18118 * of the `input` element
18121 <example name="ngValue-directive" module="valueExample">
18122 <file name="index.html">
18124 angular.module('valueExample', [])
18125 .controller('ExampleController', ['$scope', function($scope) {
18126 $scope.names = ['pizza', 'unicorns', 'robots'];
18127 $scope.my = { favorite: 'unicorns' };
18130 <form ng-controller="ExampleController">
18131 <h2>Which is your favorite?</h2>
18132 <label ng-repeat="name in names" for="{{name}}">
18134 <input type="radio"
18135 ng-model="my.favorite"
18140 <div>You chose {{my.favorite}}</div>
18143 <file name="protractor.js" type="protractor">
18144 var favorite = element(by.binding('my.favorite'));
18146 it('should initialize to model', function() {
18147 expect(favorite.getText()).toContain('unicorns');
18149 it('should bind the values to the inputs', function() {
18150 element.all(by.model('my.favorite')).get(0).click();
18151 expect(favorite.getText()).toContain('pizza');
18156 var ngValueDirective = function() {
18159 compile: function(tpl, tplAttr) {
18160 if (CONSTANT_VALUE_REGEXP.test(tplAttr.ngValue)) {
18161 return function ngValueConstantLink(scope, elm, attr) {
18162 attr.$set('value', scope.$eval(attr.ngValue));
18165 return function ngValueLink(scope, elm, attr) {
18166 scope.$watch(attr.ngValue, function valueWatchAction(value) {
18167 attr.$set('value', value);
18181 * The `ngBind` attribute tells Angular to replace the text content of the specified HTML element
18182 * with the value of a given expression, and to update the text content when the value of that
18183 * expression changes.
18185 * Typically, you don't use `ngBind` directly, but instead you use the double curly markup like
18186 * `{{ expression }}` which is similar but less verbose.
18188 * It is preferable to use `ngBind` instead of `{{ expression }}` if a template is momentarily
18189 * displayed by the browser in its raw state before Angular compiles it. Since `ngBind` is an
18190 * element attribute, it makes the bindings invisible to the user while the page is loading.
18192 * An alternative solution to this problem would be using the
18193 * {@link ng.directive:ngCloak ngCloak} directive.
18197 * @param {expression} ngBind {@link guide/expression Expression} to evaluate.
18200 * Enter a name in the Live Preview text box; the greeting below the text box changes instantly.
18201 <example module="bindExample">
18202 <file name="index.html">
18204 angular.module('bindExample', [])
18205 .controller('ExampleController', ['$scope', function($scope) {
18206 $scope.name = 'Whirled';
18209 <div ng-controller="ExampleController">
18210 Enter name: <input type="text" ng-model="name"><br>
18211 Hello <span ng-bind="name"></span>!
18214 <file name="protractor.js" type="protractor">
18215 it('should check ng-bind', function() {
18216 var nameInput = element(by.model('name'));
18218 expect(element(by.binding('name')).getText()).toBe('Whirled');
18220 nameInput.sendKeys('world');
18221 expect(element(by.binding('name')).getText()).toBe('world');
18226 var ngBindDirective = ngDirective({
18227 compile: function(templateElement) {
18228 templateElement.addClass('ng-binding');
18229 return function (scope, element, attr) {
18230 element.data('$binding', attr.ngBind);
18231 scope.$watch(attr.ngBind, function ngBindWatchAction(value) {
18232 // We are purposefully using == here rather than === because we want to
18233 // catch when value is "null or undefined"
18235 element.text(value == undefined ? '' : value);
18244 * @name ngBindTemplate
18247 * The `ngBindTemplate` directive specifies that the element
18248 * text content should be replaced with the interpolation of the template
18249 * in the `ngBindTemplate` attribute.
18250 * Unlike `ngBind`, the `ngBindTemplate` can contain multiple `{{` `}}`
18251 * expressions. This directive is needed since some HTML elements
18252 * (such as TITLE and OPTION) cannot contain SPAN elements.
18255 * @param {string} ngBindTemplate template of form
18256 * <tt>{{</tt> <tt>expression</tt> <tt>}}</tt> to eval.
18259 * Try it here: enter text in text box and watch the greeting change.
18260 <example module="bindExample">
18261 <file name="index.html">
18263 angular.module('bindExample', [])
18264 .controller('ExampleController', ['$scope', function ($scope) {
18265 $scope.salutation = 'Hello';
18266 $scope.name = 'World';
18269 <div ng-controller="ExampleController">
18270 Salutation: <input type="text" ng-model="salutation"><br>
18271 Name: <input type="text" ng-model="name"><br>
18272 <pre ng-bind-template="{{salutation}} {{name}}!"></pre>
18275 <file name="protractor.js" type="protractor">
18276 it('should check ng-bind', function() {
18277 var salutationElem = element(by.binding('salutation'));
18278 var salutationInput = element(by.model('salutation'));
18279 var nameInput = element(by.model('name'));
18281 expect(salutationElem.getText()).toBe('Hello World!');
18283 salutationInput.clear();
18284 salutationInput.sendKeys('Greetings');
18286 nameInput.sendKeys('user');
18288 expect(salutationElem.getText()).toBe('Greetings user!');
18293 var ngBindTemplateDirective = ['$interpolate', function($interpolate) {
18294 return function(scope, element, attr) {
18295 // TODO: move this to scenario runner
18296 var interpolateFn = $interpolate(element.attr(attr.$attr.ngBindTemplate));
18297 element.addClass('ng-binding').data('$binding', interpolateFn);
18298 attr.$observe('ngBindTemplate', function(value) {
18299 element.text(value);
18310 * Creates a binding that will innerHTML the result of evaluating the `expression` into the current
18311 * element in a secure way. By default, the innerHTML-ed content will be sanitized using the {@link
18312 * ngSanitize.$sanitize $sanitize} service. To utilize this functionality, ensure that `$sanitize`
18313 * is available, for example, by including {@link ngSanitize} in your module's dependencies (not in
18314 * core Angular.) You may also bypass sanitization for values you know are safe. To do so, bind to
18315 * an explicitly trusted value via {@link ng.$sce#trustAsHtml $sce.trustAsHtml}. See the example
18316 * under {@link ng.$sce#Example Strict Contextual Escaping (SCE)}.
18318 * Note: If a `$sanitize` service is unavailable and the bound value isn't explicitly trusted, you
18319 * will have an exception (instead of an exploit.)
18322 * @param {expression} ngBindHtml {@link guide/expression Expression} to evaluate.
18326 <example module="bindHtmlExample" deps="angular-sanitize.js">
18327 <file name="index.html">
18328 <div ng-controller="ExampleController">
18329 <p ng-bind-html="myHTML"></p>
18333 <file name="script.js">
18334 angular.module('bindHtmlExample', ['ngSanitize'])
18335 .controller('ExampleController', ['$scope', function($scope) {
18337 'I am an <code>HTML</code>string with ' +
18338 '<a href="#">links!</a> and other <em>stuff</em>';
18342 <file name="protractor.js" type="protractor">
18343 it('should check ng-bind-html', function() {
18344 expect(element(by.binding('myHTML')).getText()).toBe(
18345 'I am an HTMLstring with links! and other stuff');
18350 var ngBindHtmlDirective = ['$sce', '$parse', function($sce, $parse) {
18352 compile: function (tElement) {
18353 tElement.addClass('ng-binding');
18355 return function (scope, element, attr) {
18356 element.data('$binding', attr.ngBindHtml);
18358 var parsed = $parse(attr.ngBindHtml);
18360 function getStringValue() {
18361 return (parsed(scope) || '').toString();
18364 scope.$watch(getStringValue, function ngBindHtmlWatchAction(value) {
18365 element.html($sce.getTrustedHtml(parsed(scope)) || '');
18372 function classDirective(name, selector) {
18373 name = 'ngClass' + name;
18374 return ['$animate', function($animate) {
18377 link: function(scope, element, attr) {
18380 scope.$watch(attr[name], ngClassWatchAction, true);
18382 attr.$observe('class', function(value) {
18383 ngClassWatchAction(scope.$eval(attr[name]));
18387 if (name !== 'ngClass') {
18388 scope.$watch('$index', function($index, old$index) {
18389 // jshint bitwise: false
18390 var mod = $index & 1;
18391 if (mod !== (old$index & 1)) {
18392 var classes = arrayClasses(scope.$eval(attr[name]));
18394 addClasses(classes) :
18395 removeClasses(classes);
18400 function addClasses(classes) {
18401 var newClasses = digestClassCounts(classes, 1);
18402 attr.$addClass(newClasses);
18405 function removeClasses(classes) {
18406 var newClasses = digestClassCounts(classes, -1);
18407 attr.$removeClass(newClasses);
18410 function digestClassCounts (classes, count) {
18411 var classCounts = element.data('$classCounts') || {};
18412 var classesToUpdate = [];
18413 forEach(classes, function (className) {
18414 if (count > 0 || classCounts[className]) {
18415 classCounts[className] = (classCounts[className] || 0) + count;
18416 if (classCounts[className] === +(count > 0)) {
18417 classesToUpdate.push(className);
18421 element.data('$classCounts', classCounts);
18422 return classesToUpdate.join(' ');
18425 function updateClasses (oldClasses, newClasses) {
18426 var toAdd = arrayDifference(newClasses, oldClasses);
18427 var toRemove = arrayDifference(oldClasses, newClasses);
18428 toRemove = digestClassCounts(toRemove, -1);
18429 toAdd = digestClassCounts(toAdd, 1);
18431 if (toAdd.length === 0) {
18432 $animate.removeClass(element, toRemove);
18433 } else if (toRemove.length === 0) {
18434 $animate.addClass(element, toAdd);
18436 $animate.setClass(element, toAdd, toRemove);
18440 function ngClassWatchAction(newVal) {
18441 if (selector === true || scope.$index % 2 === selector) {
18442 var newClasses = arrayClasses(newVal || []);
18444 addClasses(newClasses);
18445 } else if (!equals(newVal,oldVal)) {
18446 var oldClasses = arrayClasses(oldVal);
18447 updateClasses(oldClasses, newClasses);
18450 oldVal = shallowCopy(newVal);
18455 function arrayDifference(tokens1, tokens2) {
18459 for(var i = 0; i < tokens1.length; i++) {
18460 var token = tokens1[i];
18461 for(var j = 0; j < tokens2.length; j++) {
18462 if(token == tokens2[j]) continue outer;
18464 values.push(token);
18469 function arrayClasses (classVal) {
18470 if (isArray(classVal)) {
18472 } else if (isString(classVal)) {
18473 return classVal.split(' ');
18474 } else if (isObject(classVal)) {
18475 var classes = [], i = 0;
18476 forEach(classVal, function(v, k) {
18478 classes = classes.concat(k.split(' '));
18494 * The `ngClass` directive allows you to dynamically set CSS classes on an HTML element by databinding
18495 * an expression that represents all classes to be added.
18497 * The directive operates in three different ways, depending on which of three types the expression
18500 * 1. If the expression evaluates to a string, the string should be one or more space-delimited class
18503 * 2. If the expression evaluates to an array, each element of the array should be a string that is
18504 * one or more space-delimited class names.
18506 * 3. If the expression evaluates to an object, then for each key-value pair of the
18507 * object with a truthy value the corresponding key is used as a class name.
18509 * The directive won't add duplicate classes if a particular class was already set.
18511 * When the expression changes, the previously added classes are removed and only then the
18512 * new classes are added.
18515 * add - happens just before the class is applied to the element
18516 * remove - happens just before the class is removed from the element
18519 * @param {expression} ngClass {@link guide/expression Expression} to eval. The result
18520 * of the evaluation can be a string representing space delimited class
18521 * names, an array, or a map of class names to boolean values. In the case of a map, the
18522 * names of the properties whose values are truthy will be added as css classes to the
18525 * @example Example that demonstrates basic bindings via ngClass directive.
18527 <file name="index.html">
18528 <p ng-class="{strike: deleted, bold: important, red: error}">Map Syntax Example</p>
18529 <input type="checkbox" ng-model="deleted"> deleted (apply "strike" class)<br>
18530 <input type="checkbox" ng-model="important"> important (apply "bold" class)<br>
18531 <input type="checkbox" ng-model="error"> error (apply "red" class)
18533 <p ng-class="style">Using String Syntax</p>
18534 <input type="text" ng-model="style" placeholder="Type: bold strike red">
18536 <p ng-class="[style1, style2, style3]">Using Array Syntax</p>
18537 <input ng-model="style1" placeholder="Type: bold, strike or red"><br>
18538 <input ng-model="style2" placeholder="Type: bold, strike or red"><br>
18539 <input ng-model="style3" placeholder="Type: bold, strike or red"><br>
18541 <file name="style.css">
18543 text-decoration: line-through;
18552 <file name="protractor.js" type="protractor">
18553 var ps = element.all(by.css('p'));
18555 it('should let you toggle the class', function() {
18557 expect(ps.first().getAttribute('class')).not.toMatch(/bold/);
18558 expect(ps.first().getAttribute('class')).not.toMatch(/red/);
18560 element(by.model('important')).click();
18561 expect(ps.first().getAttribute('class')).toMatch(/bold/);
18563 element(by.model('error')).click();
18564 expect(ps.first().getAttribute('class')).toMatch(/red/);
18567 it('should let you toggle string example', function() {
18568 expect(ps.get(1).getAttribute('class')).toBe('');
18569 element(by.model('style')).clear();
18570 element(by.model('style')).sendKeys('red');
18571 expect(ps.get(1).getAttribute('class')).toBe('red');
18574 it('array example should have 3 classes', function() {
18575 expect(ps.last().getAttribute('class')).toBe('');
18576 element(by.model('style1')).sendKeys('bold');
18577 element(by.model('style2')).sendKeys('strike');
18578 element(by.model('style3')).sendKeys('red');
18579 expect(ps.last().getAttribute('class')).toBe('bold strike red');
18586 The example below demonstrates how to perform animations using ngClass.
18588 <example module="ngAnimate" deps="angular-animate.js" animations="true">
18589 <file name="index.html">
18590 <input id="setbtn" type="button" value="set" ng-click="myVar='my-class'">
18591 <input id="clearbtn" type="button" value="clear" ng-click="myVar=''">
18593 <span class="base-class" ng-class="myVar">Sample Text</span>
18595 <file name="style.css">
18597 -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
18598 transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
18601 .base-class.my-class {
18606 <file name="protractor.js" type="protractor">
18607 it('should check ng-class', function() {
18608 expect(element(by.css('.base-class')).getAttribute('class')).not.
18609 toMatch(/my-class/);
18611 element(by.id('setbtn')).click();
18613 expect(element(by.css('.base-class')).getAttribute('class')).
18614 toMatch(/my-class/);
18616 element(by.id('clearbtn')).click();
18618 expect(element(by.css('.base-class')).getAttribute('class')).not.
18619 toMatch(/my-class/);
18625 ## ngClass and pre-existing CSS3 Transitions/Animations
18626 The ngClass directive still supports CSS3 Transitions/Animations even if they do not follow the ngAnimate CSS naming structure.
18627 Upon animation ngAnimate will apply supplementary CSS classes to track the start and end of an animation, but this will not hinder
18628 any pre-existing CSS transitions already on the element. To get an idea of what happens during a class-based animation, be sure
18629 to view the step by step details of {@link ngAnimate.$animate#addclass $animate.addClass} and
18630 {@link ngAnimate.$animate#removeclass $animate.removeClass}.
18632 var ngClassDirective = classDirective('', true);
18640 * The `ngClassOdd` and `ngClassEven` directives work exactly as
18641 * {@link ng.directive:ngClass ngClass}, except they work in
18642 * conjunction with `ngRepeat` and take effect only on odd (even) rows.
18644 * This directive can be applied only within the scope of an
18645 * {@link ng.directive:ngRepeat ngRepeat}.
18648 * @param {expression} ngClassOdd {@link guide/expression Expression} to eval. The result
18649 * of the evaluation can be a string representing space delimited class names or an array.
18653 <file name="index.html">
18654 <ol ng-init="names=['John', 'Mary', 'Cate', 'Suz']">
18655 <li ng-repeat="name in names">
18656 <span ng-class-odd="'odd'" ng-class-even="'even'">
18662 <file name="style.css">
18670 <file name="protractor.js" type="protractor">
18671 it('should check ng-class-odd and ng-class-even', function() {
18672 expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')).
18674 expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')).
18680 var ngClassOddDirective = classDirective('Odd', 0);
18684 * @name ngClassEven
18688 * The `ngClassOdd` and `ngClassEven` directives work exactly as
18689 * {@link ng.directive:ngClass ngClass}, except they work in
18690 * conjunction with `ngRepeat` and take effect only on odd (even) rows.
18692 * This directive can be applied only within the scope of an
18693 * {@link ng.directive:ngRepeat ngRepeat}.
18696 * @param {expression} ngClassEven {@link guide/expression Expression} to eval. The
18697 * result of the evaluation can be a string representing space delimited class names or an array.
18701 <file name="index.html">
18702 <ol ng-init="names=['John', 'Mary', 'Cate', 'Suz']">
18703 <li ng-repeat="name in names">
18704 <span ng-class-odd="'odd'" ng-class-even="'even'">
18705 {{name}}
18710 <file name="style.css">
18718 <file name="protractor.js" type="protractor">
18719 it('should check ng-class-odd and ng-class-even', function() {
18720 expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')).
18722 expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')).
18728 var ngClassEvenDirective = classDirective('Even', 1);
18736 * The `ngCloak` directive is used to prevent the Angular html template from being briefly
18737 * displayed by the browser in its raw (uncompiled) form while your application is loading. Use this
18738 * directive to avoid the undesirable flicker effect caused by the html template display.
18740 * The directive can be applied to the `<body>` element, but the preferred usage is to apply
18741 * multiple `ngCloak` directives to small portions of the page to permit progressive rendering
18742 * of the browser view.
18744 * `ngCloak` works in cooperation with the following css rule embedded within `angular.js` and
18745 * `angular.min.js`.
18746 * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
18749 * [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
18750 * display: none !important;
18754 * When this css rule is loaded by the browser, all html elements (including their children) that
18755 * are tagged with the `ngCloak` directive are hidden. When Angular encounters this directive
18756 * during the compilation of the template it deletes the `ngCloak` element attribute, making
18757 * the compiled element visible.
18759 * For the best result, the `angular.js` script must be loaded in the head section of the html
18760 * document; alternatively, the css rule above must be included in the external stylesheet of the
18763 * Legacy browsers, like IE7, do not provide attribute selector support (added in CSS 2.1) so they
18764 * cannot match the `[ng\:cloak]` selector. To work around this limitation, you must add the css
18765 * class `ng-cloak` in addition to the `ngCloak` directive as shown in the example below.
18771 <file name="index.html">
18772 <div id="template1" ng-cloak>{{ 'hello' }}</div>
18773 <div id="template2" ng-cloak class="ng-cloak">{{ 'hello IE7' }}</div>
18775 <file name="protractor.js" type="protractor">
18776 it('should remove the template directive and css class', function() {
18777 expect($('#template1').getAttribute('ng-cloak')).
18779 expect($('#template2').getAttribute('ng-cloak')).
18786 var ngCloakDirective = ngDirective({
18787 compile: function(element, attr) {
18788 attr.$set('ngCloak', undefined);
18789 element.removeClass('ng-cloak');
18795 * @name ngController
18798 * The `ngController` directive attaches a controller class to the view. This is a key aspect of how angular
18799 * supports the principles behind the Model-View-Controller design pattern.
18801 * MVC components in angular:
18803 * * Model — Models are the properties of a scope; scopes are attached to the DOM where scope properties
18804 * are accessed through bindings.
18805 * * View — The template (HTML with data bindings) that is rendered into the View.
18806 * * Controller — The `ngController` directive specifies a Controller class; the class contains business
18807 * logic behind the application to decorate the scope with functions and values
18809 * Note that you can also attach controllers to the DOM by declaring it in a route definition
18810 * via the {@link ngRoute.$route $route} service. A common mistake is to declare the controller
18811 * again using `ng-controller` in the template itself. This will cause the controller to be attached
18812 * and executed twice.
18816 * @param {expression} ngController Name of a globally accessible constructor function or an
18817 * {@link guide/expression expression} that on the current scope evaluates to a
18818 * constructor function. The controller instance can be published into a scope property
18819 * by specifying `as propertyName`.
18822 * Here is a simple form for editing user contact information. Adding, removing, clearing, and
18823 * greeting are methods declared on the controller (see source tab). These methods can
18824 * easily be called from the angular markup. Any changes to the data are automatically reflected
18825 * in the View without the need for a manual update.
18827 * Two different declaration styles are included below:
18829 * * one binds methods and properties directly onto the controller using `this`:
18830 * `ng-controller="SettingsController1 as settings"`
18831 * * one injects `$scope` into the controller:
18832 * `ng-controller="SettingsController2"`
18834 * The second option is more common in the Angular community, and is generally used in boilerplates
18835 * and in this guide. However, there are advantages to binding properties directly to the controller
18836 * and avoiding scope.
18838 * * Using `controller as` makes it obvious which controller you are accessing in the template when
18839 * multiple controllers apply to an element.
18840 * * If you are writing your controllers as classes you have easier access to the properties and
18841 * methods, which will appear on the scope, from inside the controller code.
18842 * * Since there is always a `.` in the bindings, you don't have to worry about prototypal
18843 * inheritance masking primitives.
18845 * This example demonstrates the `controller as` syntax.
18847 * <example name="ngControllerAs" module="controllerAsExample">
18848 * <file name="index.html">
18849 * <div id="ctrl-as-exmpl" ng-controller="SettingsController1 as settings">
18850 * Name: <input type="text" ng-model="settings.name"/>
18851 * [ <a href="" ng-click="settings.greet()">greet</a> ]<br/>
18854 * <li ng-repeat="contact in settings.contacts">
18855 * <select ng-model="contact.type">
18856 * <option>phone</option>
18857 * <option>email</option>
18859 * <input type="text" ng-model="contact.value"/>
18860 * [ <a href="" ng-click="settings.clearContact(contact)">clear</a>
18861 * | <a href="" ng-click="settings.removeContact(contact)">X</a> ]
18863 * <li>[ <a href="" ng-click="settings.addContact()">add</a> ]</li>
18867 * <file name="app.js">
18868 * angular.module('controllerAsExample', [])
18869 * .controller('SettingsController1', SettingsController1);
18871 * function SettingsController1() {
18872 * this.name = "John Smith";
18873 * this.contacts = [
18874 * {type: 'phone', value: '408 555 1212'},
18875 * {type: 'email', value: 'john.smith@example.org'} ];
18878 * SettingsController1.prototype.greet = function() {
18879 * alert(this.name);
18882 * SettingsController1.prototype.addContact = function() {
18883 * this.contacts.push({type: 'email', value: 'yourname@example.org'});
18886 * SettingsController1.prototype.removeContact = function(contactToRemove) {
18887 * var index = this.contacts.indexOf(contactToRemove);
18888 * this.contacts.splice(index, 1);
18891 * SettingsController1.prototype.clearContact = function(contact) {
18892 * contact.type = 'phone';
18893 * contact.value = '';
18896 * <file name="protractor.js" type="protractor">
18897 * it('should check controller as', function() {
18898 * var container = element(by.id('ctrl-as-exmpl'));
18899 * expect(container.element(by.model('settings.name'))
18900 * .getAttribute('value')).toBe('John Smith');
18902 * var firstRepeat =
18903 * container.element(by.repeater('contact in settings.contacts').row(0));
18904 * var secondRepeat =
18905 * container.element(by.repeater('contact in settings.contacts').row(1));
18907 * expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
18908 * .toBe('408 555 1212');
18910 * expect(secondRepeat.element(by.model('contact.value')).getAttribute('value'))
18911 * .toBe('john.smith@example.org');
18913 * firstRepeat.element(by.linkText('clear')).click();
18915 * expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
18918 * container.element(by.linkText('add')).click();
18920 * expect(container.element(by.repeater('contact in settings.contacts').row(2))
18921 * .element(by.model('contact.value'))
18922 * .getAttribute('value'))
18923 * .toBe('yourname@example.org');
18928 * This example demonstrates the "attach to `$scope`" style of controller.
18930 * <example name="ngController" module="controllerExample">
18931 * <file name="index.html">
18932 * <div id="ctrl-exmpl" ng-controller="SettingsController2">
18933 * Name: <input type="text" ng-model="name"/>
18934 * [ <a href="" ng-click="greet()">greet</a> ]<br/>
18937 * <li ng-repeat="contact in contacts">
18938 * <select ng-model="contact.type">
18939 * <option>phone</option>
18940 * <option>email</option>
18942 * <input type="text" ng-model="contact.value"/>
18943 * [ <a href="" ng-click="clearContact(contact)">clear</a>
18944 * | <a href="" ng-click="removeContact(contact)">X</a> ]
18946 * <li>[ <a href="" ng-click="addContact()">add</a> ]</li>
18950 * <file name="app.js">
18951 * angular.module('controllerExample', [])
18952 * .controller('SettingsController2', ['$scope', SettingsController2]);
18954 * function SettingsController2($scope) {
18955 * $scope.name = "John Smith";
18956 * $scope.contacts = [
18957 * {type:'phone', value:'408 555 1212'},
18958 * {type:'email', value:'john.smith@example.org'} ];
18960 * $scope.greet = function() {
18961 * alert($scope.name);
18964 * $scope.addContact = function() {
18965 * $scope.contacts.push({type:'email', value:'yourname@example.org'});
18968 * $scope.removeContact = function(contactToRemove) {
18969 * var index = $scope.contacts.indexOf(contactToRemove);
18970 * $scope.contacts.splice(index, 1);
18973 * $scope.clearContact = function(contact) {
18974 * contact.type = 'phone';
18975 * contact.value = '';
18979 * <file name="protractor.js" type="protractor">
18980 * it('should check controller', function() {
18981 * var container = element(by.id('ctrl-exmpl'));
18983 * expect(container.element(by.model('name'))
18984 * .getAttribute('value')).toBe('John Smith');
18986 * var firstRepeat =
18987 * container.element(by.repeater('contact in contacts').row(0));
18988 * var secondRepeat =
18989 * container.element(by.repeater('contact in contacts').row(1));
18991 * expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
18992 * .toBe('408 555 1212');
18993 * expect(secondRepeat.element(by.model('contact.value')).getAttribute('value'))
18994 * .toBe('john.smith@example.org');
18996 * firstRepeat.element(by.linkText('clear')).click();
18998 * expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
19001 * container.element(by.linkText('add')).click();
19003 * expect(container.element(by.repeater('contact in contacts').row(2))
19004 * .element(by.model('contact.value'))
19005 * .getAttribute('value'))
19006 * .toBe('yourname@example.org');
19012 var ngControllerDirective = [function() {
19026 * Enables [CSP (Content Security Policy)](https://developer.mozilla.org/en/Security/CSP) support.
19028 * This is necessary when developing things like Google Chrome Extensions.
19030 * CSP forbids apps to use `eval` or `Function(string)` generated functions (among other things).
19031 * For Angular to be CSP compatible there are only two things that we need to do differently:
19033 * - don't use `Function` constructor to generate optimized value getters
19034 * - don't inject custom stylesheet into the document
19036 * AngularJS uses `Function(string)` generated functions as a speed optimization. Applying the `ngCsp`
19037 * directive will cause Angular to use CSP compatibility mode. When this mode is on AngularJS will
19038 * evaluate all expressions up to 30% slower than in non-CSP mode, but no security violations will
19041 * CSP forbids JavaScript to inline stylesheet rules. In non CSP mode Angular automatically
19042 * includes some CSS rules (e.g. {@link ng.directive:ngCloak ngCloak}).
19043 * To make those directives work in CSP mode, include the `angular-csp.css` manually.
19045 * Angular tries to autodetect if CSP is active and automatically turn on the CSP-safe mode. This
19046 * autodetection however triggers a CSP error to be logged in the console:
19049 * Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of
19050 * script in the following Content Security Policy directive: "default-src 'self'". Note that
19051 * 'script-src' was not explicitly set, so 'default-src' is used as a fallback.
19054 * This error is harmless but annoying. To prevent the error from showing up, put the `ngCsp`
19055 * directive on the root element of the application or on the `angular.js` script tag, whichever
19056 * appears first in the html document.
19058 * *Note: This directive is only available in the `ng-csp` and `data-ng-csp` attribute form.*
19061 * This example shows how to apply the `ngCsp` directive to the `html` tag.
19064 <html ng-app ng-csp>
19071 // ngCsp is not implemented as a proper directive any more, because we need it be processed while we
19072 // bootstrap the system (before $parse is instantiated), for this reason we just have
19073 // the csp.isActive() fn that looks for ng-csp attribute anywhere in the current doc
19080 * The ngClick directive allows you to specify custom behavior when
19081 * an element is clicked.
19085 * @param {expression} ngClick {@link guide/expression Expression} to evaluate upon
19086 * click. ({@link guide/expression#-event- Event object is available as `$event`})
19090 <file name="index.html">
19091 <button ng-click="count = count + 1" ng-init="count=0">
19098 <file name="protractor.js" type="protractor">
19099 it('should check ng-click', function() {
19100 expect(element(by.binding('count')).getText()).toMatch('0');
19101 element(by.css('button')).click();
19102 expect(element(by.binding('count')).getText()).toMatch('1');
19108 * A directive that allows creation of custom onclick handlers that are defined as angular
19109 * expressions and are compiled and executed within the current scope.
19111 * Events that are handled via these handler are always configured not to propagate further.
19113 var ngEventDirectives = {};
19115 // For events that might fire synchronously during DOM manipulation
19116 // we need to execute their event handlers asynchronously using $evalAsync,
19117 // so that they are not executed in an inconsistent state.
19118 var forceAsyncEvents = {
19123 'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste'.split(' '),
19124 function(eventName) {
19125 var directiveName = directiveNormalize('ng-' + eventName);
19126 ngEventDirectives[directiveName] = ['$parse', '$rootScope', function($parse, $rootScope) {
19128 compile: function($element, attr) {
19129 var fn = $parse(attr[directiveName]);
19130 return function ngEventHandler(scope, element) {
19131 element.on(eventName, function(event) {
19132 var callback = function() {
19133 fn(scope, {$event:event});
19135 if (forceAsyncEvents[eventName] && $rootScope.$$phase) {
19136 scope.$evalAsync(callback);
19138 scope.$apply(callback);
19153 * The `ngDblclick` directive allows you to specify custom behavior on a dblclick event.
19157 * @param {expression} ngDblclick {@link guide/expression Expression} to evaluate upon
19158 * a dblclick. (The Event object is available as `$event`)
19162 <file name="index.html">
19163 <button ng-dblclick="count = count + 1" ng-init="count=0">
19164 Increment (on double click)
19174 * @name ngMousedown
19177 * The ngMousedown directive allows you to specify custom behavior on mousedown event.
19181 * @param {expression} ngMousedown {@link guide/expression Expression} to evaluate upon
19182 * mousedown. ({@link guide/expression#-event- Event object is available as `$event`})
19186 <file name="index.html">
19187 <button ng-mousedown="count = count + 1" ng-init="count=0">
19188 Increment (on mouse down)
19201 * Specify custom behavior on mouseup event.
19205 * @param {expression} ngMouseup {@link guide/expression Expression} to evaluate upon
19206 * mouseup. ({@link guide/expression#-event- Event object is available as `$event`})
19210 <file name="index.html">
19211 <button ng-mouseup="count = count + 1" ng-init="count=0">
19212 Increment (on mouse up)
19221 * @name ngMouseover
19224 * Specify custom behavior on mouseover event.
19228 * @param {expression} ngMouseover {@link guide/expression Expression} to evaluate upon
19229 * mouseover. ({@link guide/expression#-event- Event object is available as `$event`})
19233 <file name="index.html">
19234 <button ng-mouseover="count = count + 1" ng-init="count=0">
19235 Increment (when mouse is over)
19245 * @name ngMouseenter
19248 * Specify custom behavior on mouseenter event.
19252 * @param {expression} ngMouseenter {@link guide/expression Expression} to evaluate upon
19253 * mouseenter. ({@link guide/expression#-event- Event object is available as `$event`})
19257 <file name="index.html">
19258 <button ng-mouseenter="count = count + 1" ng-init="count=0">
19259 Increment (when mouse enters)
19269 * @name ngMouseleave
19272 * Specify custom behavior on mouseleave event.
19276 * @param {expression} ngMouseleave {@link guide/expression Expression} to evaluate upon
19277 * mouseleave. ({@link guide/expression#-event- Event object is available as `$event`})
19281 <file name="index.html">
19282 <button ng-mouseleave="count = count + 1" ng-init="count=0">
19283 Increment (when mouse leaves)
19293 * @name ngMousemove
19296 * Specify custom behavior on mousemove event.
19300 * @param {expression} ngMousemove {@link guide/expression Expression} to evaluate upon
19301 * mousemove. ({@link guide/expression#-event- Event object is available as `$event`})
19305 <file name="index.html">
19306 <button ng-mousemove="count = count + 1" ng-init="count=0">
19307 Increment (when mouse moves)
19320 * Specify custom behavior on keydown event.
19324 * @param {expression} ngKeydown {@link guide/expression Expression} to evaluate upon
19325 * keydown. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
19329 <file name="index.html">
19330 <input ng-keydown="count = count + 1" ng-init="count=0">
19331 key down count: {{count}}
19342 * Specify custom behavior on keyup event.
19346 * @param {expression} ngKeyup {@link guide/expression Expression} to evaluate upon
19347 * keyup. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
19351 <file name="index.html">
19352 <p>Typing in the input box below updates the key count</p>
19353 <input ng-keyup="count = count + 1" ng-init="count=0"> key up count: {{count}}
19355 <p>Typing in the input box below updates the keycode</p>
19356 <input ng-keyup="event=$event">
19357 <p>event keyCode: {{ event.keyCode }}</p>
19358 <p>event altKey: {{ event.altKey }}</p>
19369 * Specify custom behavior on keypress event.
19372 * @param {expression} ngKeypress {@link guide/expression Expression} to evaluate upon
19373 * keypress. ({@link guide/expression#-event- Event object is available as `$event`}
19374 * and can be interrogated for keyCode, altKey, etc.)
19378 <file name="index.html">
19379 <input ng-keypress="count = count + 1" ng-init="count=0">
19380 key press count: {{count}}
19391 * Enables binding angular expressions to onsubmit events.
19393 * Additionally it prevents the default action (which for form means sending the request to the
19394 * server and reloading the current page), but only if the form does not contain `action`,
19395 * `data-action`, or `x-action` attributes.
19397 * <div class="alert alert-warning">
19398 * **Warning:** Be careful not to cause "double-submission" by using both the `ngClick` and
19399 * `ngSubmit` handlers together. See the
19400 * {@link form#submitting-a-form-and-preventing-the-default-action `form` directive documentation}
19401 * for a detailed discussion of when `ngSubmit` may be triggered.
19406 * @param {expression} ngSubmit {@link guide/expression Expression} to eval.
19407 * ({@link guide/expression#-event- Event object is available as `$event`})
19410 <example module="submitExample">
19411 <file name="index.html">
19413 angular.module('submitExample', [])
19414 .controller('ExampleController', ['$scope', function($scope) {
19416 $scope.text = 'hello';
19417 $scope.submit = function() {
19419 $scope.list.push(this.text);
19425 <form ng-submit="submit()" ng-controller="ExampleController">
19426 Enter text and hit enter:
19427 <input type="text" ng-model="text" name="text" />
19428 <input type="submit" id="submit" value="Submit" />
19429 <pre>list={{list}}</pre>
19432 <file name="protractor.js" type="protractor">
19433 it('should check ng-submit', function() {
19434 expect(element(by.binding('list')).getText()).toBe('list=[]');
19435 element(by.css('#submit')).click();
19436 expect(element(by.binding('list')).getText()).toContain('hello');
19437 expect(element(by.model('text')).getAttribute('value')).toBe('');
19439 it('should ignore empty strings', function() {
19440 expect(element(by.binding('list')).getText()).toBe('list=[]');
19441 element(by.css('#submit')).click();
19442 element(by.css('#submit')).click();
19443 expect(element(by.binding('list')).getText()).toContain('hello');
19454 * Specify custom behavior on focus event.
19456 * Note: As the `focus` event is executed synchronously when calling `input.focus()`
19457 * AngularJS executes the expression using `scope.$evalAsync` if the event is fired
19458 * during an `$apply` to ensure a consistent state.
19460 * @element window, input, select, textarea, a
19462 * @param {expression} ngFocus {@link guide/expression Expression} to evaluate upon
19463 * focus. ({@link guide/expression#-event- Event object is available as `$event`})
19466 * See {@link ng.directive:ngClick ngClick}
19474 * Specify custom behavior on blur event.
19476 * A [blur event](https://developer.mozilla.org/en-US/docs/Web/Events/blur) fires when
19477 * an element has lost focus.
19479 * Note: As the `blur` event is executed synchronously also during DOM manipulations
19480 * (e.g. removing a focussed input),
19481 * AngularJS executes the expression using `scope.$evalAsync` if the event is fired
19482 * during an `$apply` to ensure a consistent state.
19484 * @element window, input, select, textarea, a
19486 * @param {expression} ngBlur {@link guide/expression Expression} to evaluate upon
19487 * blur. ({@link guide/expression#-event- Event object is available as `$event`})
19490 * See {@link ng.directive:ngClick ngClick}
19498 * Specify custom behavior on copy event.
19500 * @element window, input, select, textarea, a
19502 * @param {expression} ngCopy {@link guide/expression Expression} to evaluate upon
19503 * copy. ({@link guide/expression#-event- Event object is available as `$event`})
19507 <file name="index.html">
19508 <input ng-copy="copied=true" ng-init="copied=false; value='copy me'" ng-model="value">
19519 * Specify custom behavior on cut event.
19521 * @element window, input, select, textarea, a
19523 * @param {expression} ngCut {@link guide/expression Expression} to evaluate upon
19524 * cut. ({@link guide/expression#-event- Event object is available as `$event`})
19528 <file name="index.html">
19529 <input ng-cut="cut=true" ng-init="cut=false; value='cut me'" ng-model="value">
19540 * Specify custom behavior on paste event.
19542 * @element window, input, select, textarea, a
19544 * @param {expression} ngPaste {@link guide/expression Expression} to evaluate upon
19545 * paste. ({@link guide/expression#-event- Event object is available as `$event`})
19549 <file name="index.html">
19550 <input ng-paste="paste=true" ng-init="paste=false" placeholder='paste here'>
19562 * The `ngIf` directive removes or recreates a portion of the DOM tree based on an
19563 * {expression}. If the expression assigned to `ngIf` evaluates to a false
19564 * value then the element is removed from the DOM, otherwise a clone of the
19565 * element is reinserted into the DOM.
19567 * `ngIf` differs from `ngShow` and `ngHide` in that `ngIf` completely removes and recreates the
19568 * element in the DOM rather than changing its visibility via the `display` css property. A common
19569 * case when this difference is significant is when using css selectors that rely on an element's
19570 * position within the DOM, such as the `:first-child` or `:last-child` pseudo-classes.
19572 * Note that when an element is removed using `ngIf` its scope is destroyed and a new scope
19573 * is created when the element is restored. The scope created within `ngIf` inherits from
19574 * its parent scope using
19575 * [prototypal inheritance](https://github.com/angular/angular.js/wiki/The-Nuances-of-Scope-Prototypal-Inheritance).
19576 * An important implication of this is if `ngModel` is used within `ngIf` to bind to
19577 * a javascript primitive defined in the parent scope. In this case any modifications made to the
19578 * variable within the child scope will override (hide) the value in the parent scope.
19580 * Also, `ngIf` recreates elements using their compiled state. An example of this behavior
19581 * is if an element's class attribute is directly modified after it's compiled, using something like
19582 * jQuery's `.addClass()` method, and the element is later removed. When `ngIf` recreates the element
19583 * the added class will be lost because the original compiled state is used to regenerate the element.
19585 * Additionally, you can provide animations via the `ngAnimate` module to animate the `enter`
19586 * and `leave` effects.
19589 * enter - happens just after the ngIf contents change and a new DOM element is created and injected into the ngIf container
19590 * leave - happens just before the ngIf contents are removed from the DOM
19595 * @param {expression} ngIf If the {@link guide/expression expression} is falsy then
19596 * the element is removed from the DOM tree. If it is truthy a copy of the compiled
19597 * element is added to the DOM tree.
19600 <example module="ngAnimate" deps="angular-animate.js" animations="true">
19601 <file name="index.html">
19602 Click me: <input type="checkbox" ng-model="checked" ng-init="checked=true" /><br/>
19604 <span ng-if="checked" class="animate-if">
19605 I'm removed when the checkbox is unchecked.
19608 <file name="animations.css">
19611 border:1px solid black;
19615 .animate-if.ng-enter, .animate-if.ng-leave {
19616 -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
19617 transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
19620 .animate-if.ng-enter,
19621 .animate-if.ng-leave.ng-leave-active {
19625 .animate-if.ng-leave,
19626 .animate-if.ng-enter.ng-enter-active {
19632 var ngIfDirective = ['$animate', function($animate) {
19634 transclude: 'element',
19639 link: function ($scope, $element, $attr, ctrl, $transclude) {
19640 var block, childScope, previousElements;
19641 $scope.$watch($attr.ngIf, function ngIfWatchAction(value) {
19643 if (toBoolean(value)) {
19645 childScope = $scope.$new();
19646 $transclude(childScope, function (clone) {
19647 clone[clone.length++] = document.createComment(' end ngIf: ' + $attr.ngIf + ' ');
19648 // Note: We only need the first/last node of the cloned nodes.
19649 // However, we need to keep the reference to the jqlite wrapper as it might be changed later
19650 // by a directive with templateUrl when its template arrives.
19654 $animate.enter(clone, $element.parent(), $element);
19658 if(previousElements) {
19659 previousElements.remove();
19660 previousElements = null;
19663 childScope.$destroy();
19667 previousElements = getBlockElements(block.clone);
19668 $animate.leave(previousElements, function() {
19669 previousElements = null;
19685 * Fetches, compiles and includes an external HTML fragment.
19687 * By default, the template URL is restricted to the same domain and protocol as the
19688 * application document. This is done by calling {@link ng.$sce#getTrustedResourceUrl
19689 * $sce.getTrustedResourceUrl} on it. To load templates from other domains or protocols
19690 * you may either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist them} or
19691 * [wrap them](ng.$sce#trustAsResourceUrl) as trusted values. Refer to Angular's {@link
19692 * ng.$sce Strict Contextual Escaping}.
19694 * In addition, the browser's
19695 * [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest)
19696 * and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/)
19697 * policy may further restrict whether the template is successfully loaded.
19698 * For example, `ngInclude` won't work for cross-domain requests on all browsers and for `file://`
19699 * access on some browsers.
19702 * enter - animation is used to bring new content into the browser.
19703 * leave - animation is used to animate existing content away.
19705 * The enter and leave animation occur concurrently.
19710 * @param {string} ngInclude|src angular expression evaluating to URL. If the source is a string constant,
19711 * make sure you wrap it in **single** quotes, e.g. `src="'myPartialTemplate.html'"`.
19712 * @param {string=} onload Expression to evaluate when a new partial is loaded.
19714 * @param {string=} autoscroll Whether `ngInclude` should call {@link ng.$anchorScroll
19715 * $anchorScroll} to scroll the viewport after the content is loaded.
19717 * - If the attribute is not set, disable scrolling.
19718 * - If the attribute is set without value, enable scrolling.
19719 * - Otherwise enable scrolling only if the expression evaluates to truthy value.
19722 <example module="includeExample" deps="angular-animate.js" animations="true">
19723 <file name="index.html">
19724 <div ng-controller="ExampleController">
19725 <select ng-model="template" ng-options="t.name for t in templates">
19726 <option value="">(blank)</option>
19728 url of the template: <tt>{{template.url}}</tt>
19730 <div class="slide-animate-container">
19731 <div class="slide-animate" ng-include="template.url"></div>
19735 <file name="script.js">
19736 angular.module('includeExample', ['ngAnimate'])
19737 .controller('ExampleController', ['$scope', function($scope) {
19739 [ { name: 'template1.html', url: 'template1.html'},
19740 { name: 'template2.html', url: 'template2.html'} ];
19741 $scope.template = $scope.templates[0];
19744 <file name="template1.html">
19745 Content of template1.html
19747 <file name="template2.html">
19748 Content of template2.html
19750 <file name="animations.css">
19751 .slide-animate-container {
19754 border:1px solid black;
19763 .slide-animate.ng-enter, .slide-animate.ng-leave {
19764 -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
19765 transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
19776 .slide-animate.ng-enter {
19779 .slide-animate.ng-enter.ng-enter-active {
19783 .slide-animate.ng-leave {
19786 .slide-animate.ng-leave.ng-leave-active {
19790 <file name="protractor.js" type="protractor">
19791 var templateSelect = element(by.model('template'));
19792 var includeElem = element(by.css('[ng-include]'));
19794 it('should load template1.html', function() {
19795 expect(includeElem.getText()).toMatch(/Content of template1.html/);
19798 it('should load template2.html', function() {
19799 if (browser.params.browser == 'firefox') {
19800 // Firefox can't handle using selects
19801 // See https://github.com/angular/protractor/issues/480
19804 templateSelect.click();
19805 templateSelect.all(by.css('option')).get(2).click();
19806 expect(includeElem.getText()).toMatch(/Content of template2.html/);
19809 it('should change to blank', function() {
19810 if (browser.params.browser == 'firefox') {
19811 // Firefox can't handle using selects
19814 templateSelect.click();
19815 templateSelect.all(by.css('option')).get(0).click();
19816 expect(includeElem.isPresent()).toBe(false);
19825 * @name ngInclude#$includeContentRequested
19826 * @eventType emit on the scope ngInclude was declared in
19828 * Emitted every time the ngInclude content is requested.
19834 * @name ngInclude#$includeContentLoaded
19835 * @eventType emit on the current ngInclude scope
19837 * Emitted every time the ngInclude content is reloaded.
19839 var ngIncludeDirective = ['$http', '$templateCache', '$anchorScroll', '$animate', '$sce',
19840 function($http, $templateCache, $anchorScroll, $animate, $sce) {
19845 transclude: 'element',
19846 controller: angular.noop,
19847 compile: function(element, attr) {
19848 var srcExp = attr.ngInclude || attr.src,
19849 onloadExp = attr.onload || '',
19850 autoScrollExp = attr.autoscroll;
19852 return function(scope, $element, $attr, ctrl, $transclude) {
19853 var changeCounter = 0,
19858 var cleanupLastIncludeContent = function() {
19859 if(previousElement) {
19860 previousElement.remove();
19861 previousElement = null;
19864 currentScope.$destroy();
19865 currentScope = null;
19867 if(currentElement) {
19868 $animate.leave(currentElement, function() {
19869 previousElement = null;
19871 previousElement = currentElement;
19872 currentElement = null;
19876 scope.$watch($sce.parseAsResourceUrl(srcExp), function ngIncludeWatchAction(src) {
19877 var afterAnimation = function() {
19878 if (isDefined(autoScrollExp) && (!autoScrollExp || scope.$eval(autoScrollExp))) {
19882 var thisChangeId = ++changeCounter;
19885 $http.get(src, {cache: $templateCache}).success(function(response) {
19886 if (thisChangeId !== changeCounter) return;
19887 var newScope = scope.$new();
19888 ctrl.template = response;
19890 // Note: This will also link all children of ng-include that were contained in the original
19891 // html. If that content contains controllers, ... they could pollute/change the scope.
19892 // However, using ng-include on an element with additional content does not make sense...
19893 // Note: We can't remove them in the cloneAttchFn of $transclude as that
19894 // function is called before linking the content, which would apply child
19895 // directives to non existing elements.
19896 var clone = $transclude(newScope, function(clone) {
19897 cleanupLastIncludeContent();
19898 $animate.enter(clone, null, $element, afterAnimation);
19901 currentScope = newScope;
19902 currentElement = clone;
19904 currentScope.$emit('$includeContentLoaded');
19905 scope.$eval(onloadExp);
19906 }).error(function() {
19907 if (thisChangeId === changeCounter) cleanupLastIncludeContent();
19909 scope.$emit('$includeContentRequested');
19911 cleanupLastIncludeContent();
19912 ctrl.template = null;
19920 // This directive is called during the $transclude call of the first `ngInclude` directive.
19921 // It will replace and compile the content of the element with the loaded template.
19922 // We need this directive so that the element content is already filled when
19923 // the link function of another directive on the same element as ngInclude
19925 var ngIncludeFillContentDirective = ['$compile',
19926 function($compile) {
19930 require: 'ngInclude',
19931 link: function(scope, $element, $attr, ctrl) {
19932 $element.html(ctrl.template);
19933 $compile($element.contents())(scope);
19944 * The `ngInit` directive allows you to evaluate an expression in the
19947 * <div class="alert alert-error">
19948 * The only appropriate use of `ngInit` is for aliasing special properties of
19949 * {@link ng.directive:ngRepeat `ngRepeat`}, as seen in the demo below. Besides this case, you
19950 * should use {@link guide/controller controllers} rather than `ngInit`
19951 * to initialize values on a scope.
19953 * <div class="alert alert-warning">
19954 * **Note**: If you have assignment in `ngInit` along with {@link ng.$filter `$filter`}, make
19955 * sure you have parenthesis for correct precedence:
19956 * <pre class="prettyprint">
19957 * <div ng-init="test1 = (data | orderBy:'name')"></div>
19964 * @param {expression} ngInit {@link guide/expression Expression} to eval.
19967 <example module="initExample">
19968 <file name="index.html">
19970 angular.module('initExample', [])
19971 .controller('ExampleController', ['$scope', function($scope) {
19972 $scope.list = [['a', 'b'], ['c', 'd']];
19975 <div ng-controller="ExampleController">
19976 <div ng-repeat="innerList in list" ng-init="outerIndex = $index">
19977 <div ng-repeat="value in innerList" ng-init="innerIndex = $index">
19978 <span class="example-init">list[ {{outerIndex}} ][ {{innerIndex}} ] = {{value}};</span>
19983 <file name="protractor.js" type="protractor">
19984 it('should alias index positions', function() {
19985 var elements = element.all(by.css('.example-init'));
19986 expect(elements.get(0).getText()).toBe('list[ 0 ][ 0 ] = a;');
19987 expect(elements.get(1).getText()).toBe('list[ 0 ][ 1 ] = b;');
19988 expect(elements.get(2).getText()).toBe('list[ 1 ][ 0 ] = c;');
19989 expect(elements.get(3).getText()).toBe('list[ 1 ][ 1 ] = d;');
19994 var ngInitDirective = ngDirective({
19996 compile: function() {
19998 pre: function(scope, element, attrs) {
19999 scope.$eval(attrs.ngInit);
20007 * @name ngNonBindable
20012 * The `ngNonBindable` directive tells Angular not to compile or bind the contents of the current
20013 * DOM element. This is useful if the element contains what appears to be Angular directives and
20014 * bindings but which should be ignored by Angular. This could be the case if you have a site that
20015 * displays snippets of code, for instance.
20020 * In this example there are two locations where a simple interpolation binding (`{{}}`) is present,
20021 * but the one wrapped in `ngNonBindable` is left alone.
20025 <file name="index.html">
20026 <div>Normal: {{1 + 2}}</div>
20027 <div ng-non-bindable>Ignored: {{1 + 2}}</div>
20029 <file name="protractor.js" type="protractor">
20030 it('should check ng-non-bindable', function() {
20031 expect(element(by.binding('1 + 2')).getText()).toContain('3');
20032 expect(element.all(by.css('div')).last().getText()).toMatch(/1 \+ 2/);
20037 var ngNonBindableDirective = ngDirective({ terminal: true, priority: 1000 });
20041 * @name ngPluralize
20045 * `ngPluralize` is a directive that displays messages according to en-US localization rules.
20046 * These rules are bundled with angular.js, but can be overridden
20047 * (see {@link guide/i18n Angular i18n} dev guide). You configure ngPluralize directive
20048 * by specifying the mappings between
20049 * [plural categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html)
20050 * and the strings to be displayed.
20052 * # Plural categories and explicit number rules
20054 * [plural categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html)
20055 * in Angular's default en-US locale: "one" and "other".
20057 * While a plural category may match many numbers (for example, in en-US locale, "other" can match
20058 * any number that is not 1), an explicit number rule can only match one number. For example, the
20059 * explicit number rule for "3" matches the number 3. There are examples of plural categories
20060 * and explicit number rules throughout the rest of this documentation.
20062 * # Configuring ngPluralize
20063 * You configure ngPluralize by providing 2 attributes: `count` and `when`.
20064 * You can also provide an optional attribute, `offset`.
20066 * The value of the `count` attribute can be either a string or an {@link guide/expression
20067 * Angular expression}; these are evaluated on the current scope for its bound value.
20069 * The `when` attribute specifies the mappings between plural categories and the actual
20070 * string to be displayed. The value of the attribute should be a JSON object.
20072 * The following example shows how to configure ngPluralize:
20075 * <ng-pluralize count="personCount"
20076 when="{'0': 'Nobody is viewing.',
20077 * 'one': '1 person is viewing.',
20078 * 'other': '{} people are viewing.'}">
20082 * In the example, `"0: Nobody is viewing."` is an explicit number rule. If you did not
20083 * specify this rule, 0 would be matched to the "other" category and "0 people are viewing"
20084 * would be shown instead of "Nobody is viewing". You can specify an explicit number rule for
20085 * other numbers, for example 12, so that instead of showing "12 people are viewing", you can
20086 * show "a dozen people are viewing".
20088 * You can use a set of closed braces (`{}`) as a placeholder for the number that you want substituted
20089 * into pluralized strings. In the previous example, Angular will replace `{}` with
20090 * <span ng-non-bindable>`{{personCount}}`</span>. The closed braces `{}` is a placeholder
20091 * for <span ng-non-bindable>{{numberExpression}}</span>.
20093 * # Configuring ngPluralize with offset
20094 * The `offset` attribute allows further customization of pluralized text, which can result in
20095 * a better user experience. For example, instead of the message "4 people are viewing this document",
20096 * you might display "John, Kate and 2 others are viewing this document".
20097 * The offset attribute allows you to offset a number by any desired value.
20098 * Let's take a look at an example:
20101 * <ng-pluralize count="personCount" offset=2
20102 * when="{'0': 'Nobody is viewing.',
20103 * '1': '{{person1}} is viewing.',
20104 * '2': '{{person1}} and {{person2}} are viewing.',
20105 * 'one': '{{person1}}, {{person2}} and one other person are viewing.',
20106 * 'other': '{{person1}}, {{person2}} and {} other people are viewing.'}">
20110 * Notice that we are still using two plural categories(one, other), but we added
20111 * three explicit number rules 0, 1 and 2.
20112 * When one person, perhaps John, views the document, "John is viewing" will be shown.
20113 * When three people view the document, no explicit number rule is found, so
20114 * an offset of 2 is taken off 3, and Angular uses 1 to decide the plural category.
20115 * In this case, plural category 'one' is matched and "John, Mary and one other person are viewing"
20118 * Note that when you specify offsets, you must provide explicit number rules for
20119 * numbers from 0 up to and including the offset. If you use an offset of 3, for example,
20120 * you must provide explicit number rules for 0, 1, 2 and 3. You must also provide plural strings for
20121 * plural categories "one" and "other".
20123 * @param {string|expression} count The variable to be bound to.
20124 * @param {string} when The mapping between plural category to its corresponding strings.
20125 * @param {number=} offset Offset to deduct from the total number.
20128 <example module="pluralizeExample">
20129 <file name="index.html">
20131 angular.module('pluralizeExample', [])
20132 .controller('ExampleController', ['$scope', function($scope) {
20133 $scope.person1 = 'Igor';
20134 $scope.person2 = 'Misko';
20135 $scope.personCount = 1;
20138 <div ng-controller="ExampleController">
20139 Person 1:<input type="text" ng-model="person1" value="Igor" /><br/>
20140 Person 2:<input type="text" ng-model="person2" value="Misko" /><br/>
20141 Number of People:<input type="text" ng-model="personCount" value="1" /><br/>
20143 <!--- Example with simple pluralization rules for en locale --->
20145 <ng-pluralize count="personCount"
20146 when="{'0': 'Nobody is viewing.',
20147 'one': '1 person is viewing.',
20148 'other': '{} people are viewing.'}">
20149 </ng-pluralize><br>
20151 <!--- Example with offset --->
20153 <ng-pluralize count="personCount" offset=2
20154 when="{'0': 'Nobody is viewing.',
20155 '1': '{{person1}} is viewing.',
20156 '2': '{{person1}} and {{person2}} are viewing.',
20157 'one': '{{person1}}, {{person2}} and one other person are viewing.',
20158 'other': '{{person1}}, {{person2}} and {} other people are viewing.'}">
20162 <file name="protractor.js" type="protractor">
20163 it('should show correct pluralized string', function() {
20164 var withoutOffset = element.all(by.css('ng-pluralize')).get(0);
20165 var withOffset = element.all(by.css('ng-pluralize')).get(1);
20166 var countInput = element(by.model('personCount'));
20168 expect(withoutOffset.getText()).toEqual('1 person is viewing.');
20169 expect(withOffset.getText()).toEqual('Igor is viewing.');
20171 countInput.clear();
20172 countInput.sendKeys('0');
20174 expect(withoutOffset.getText()).toEqual('Nobody is viewing.');
20175 expect(withOffset.getText()).toEqual('Nobody is viewing.');
20177 countInput.clear();
20178 countInput.sendKeys('2');
20180 expect(withoutOffset.getText()).toEqual('2 people are viewing.');
20181 expect(withOffset.getText()).toEqual('Igor and Misko are viewing.');
20183 countInput.clear();
20184 countInput.sendKeys('3');
20186 expect(withoutOffset.getText()).toEqual('3 people are viewing.');
20187 expect(withOffset.getText()).toEqual('Igor, Misko and one other person are viewing.');
20189 countInput.clear();
20190 countInput.sendKeys('4');
20192 expect(withoutOffset.getText()).toEqual('4 people are viewing.');
20193 expect(withOffset.getText()).toEqual('Igor, Misko and 2 other people are viewing.');
20195 it('should show data-bound names', function() {
20196 var withOffset = element.all(by.css('ng-pluralize')).get(1);
20197 var personCount = element(by.model('personCount'));
20198 var person1 = element(by.model('person1'));
20199 var person2 = element(by.model('person2'));
20200 personCount.clear();
20201 personCount.sendKeys('4');
20203 person1.sendKeys('Di');
20205 person2.sendKeys('Vojta');
20206 expect(withOffset.getText()).toEqual('Di, Vojta and 2 other people are viewing.');
20211 var ngPluralizeDirective = ['$locale', '$interpolate', function($locale, $interpolate) {
20215 link: function(scope, element, attr) {
20216 var numberExp = attr.count,
20217 whenExp = attr.$attr.when && element.attr(attr.$attr.when), // we have {{}} in attrs
20218 offset = attr.offset || 0,
20219 whens = scope.$eval(whenExp) || {},
20221 startSymbol = $interpolate.startSymbol(),
20222 endSymbol = $interpolate.endSymbol(),
20223 isWhen = /^when(Minus)?(.+)$/;
20225 forEach(attr, function(expression, attributeName) {
20226 if (isWhen.test(attributeName)) {
20227 whens[lowercase(attributeName.replace('when', '').replace('Minus', '-'))] =
20228 element.attr(attr.$attr[attributeName]);
20231 forEach(whens, function(expression, key) {
20233 $interpolate(expression.replace(BRACE, startSymbol + numberExp + '-' +
20234 offset + endSymbol));
20237 scope.$watch(function ngPluralizeWatch() {
20238 var value = parseFloat(scope.$eval(numberExp));
20240 if (!isNaN(value)) {
20241 //if explicit number rule such as 1, 2, 3... is defined, just use it. Otherwise,
20242 //check it against pluralization rules in $locale service
20243 if (!(value in whens)) value = $locale.pluralCat(value - offset);
20244 return whensExpFns[value](scope, element, true);
20248 }, function ngPluralizeWatchAction(newVal) {
20249 element.text(newVal);
20260 * The `ngRepeat` directive instantiates a template once per item from a collection. Each template
20261 * instance gets its own scope, where the given loop variable is set to the current collection item,
20262 * and `$index` is set to the item index or key.
20264 * Special properties are exposed on the local scope of each template instance, including:
20266 * | Variable | Type | Details |
20267 * |-----------|-----------------|-----------------------------------------------------------------------------|
20268 * | `$index` | {@type number} | iterator offset of the repeated element (0..length-1) |
20269 * | `$first` | {@type boolean} | true if the repeated element is first in the iterator. |
20270 * | `$middle` | {@type boolean} | true if the repeated element is between the first and last in the iterator. |
20271 * | `$last` | {@type boolean} | true if the repeated element is last in the iterator. |
20272 * | `$even` | {@type boolean} | true if the iterator position `$index` is even (otherwise false). |
20273 * | `$odd` | {@type boolean} | true if the iterator position `$index` is odd (otherwise false). |
20275 * Creating aliases for these properties is possible with {@link ng.directive:ngInit `ngInit`}.
20276 * This may be useful when, for instance, nesting ngRepeats.
20278 * # Special repeat start and end points
20279 * To repeat a series of elements instead of just one parent element, ngRepeat (as well as other ng directives) supports extending
20280 * the range of the repeater by defining explicit start and end points by using **ng-repeat-start** and **ng-repeat-end** respectively.
20281 * 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)
20282 * up to and including the ending HTML tag where **ng-repeat-end** is placed.
20284 * The example below makes use of this feature:
20286 * <header ng-repeat-start="item in items">
20287 * Header {{ item }}
20289 * <div class="body">
20292 * <footer ng-repeat-end>
20293 * Footer {{ item }}
20297 * And with an input of {@type ['A','B']} for the items variable in the example above, the output will evaluate to:
20302 * <div class="body">
20311 * <div class="body">
20319 * The custom start and end points for ngRepeat also support all other HTML directive syntax flavors provided in AngularJS (such
20320 * as **data-ng-repeat-start**, **x-ng-repeat-start** and **ng:repeat-start**).
20323 * **.enter** - when a new item is added to the list or when an item is revealed after a filter
20325 * **.leave** - when an item is removed from the list or when an item is filtered out
20327 * **.move** - when an adjacent item is filtered out causing a reorder or when the item contents are reordered
20332 * @param {repeat_expression} ngRepeat The expression indicating how to enumerate a collection. These
20333 * formats are currently supported:
20335 * * `variable in expression` – where variable is the user defined loop variable and `expression`
20336 * is a scope expression giving the collection to enumerate.
20338 * For example: `album in artist.albums`.
20340 * * `(key, value) in expression` – where `key` and `value` can be any user defined identifiers,
20341 * and `expression` is the scope expression giving the collection to enumerate.
20343 * For example: `(name, age) in {'adam':10, 'amalie':12}`.
20345 * * `variable in expression track by tracking_expression` – You can also provide an optional tracking function
20346 * which can be used to associate the objects in the collection with the DOM elements. If no tracking function
20347 * is specified the ng-repeat associates elements by identity in the collection. It is an error to have
20348 * more than one tracking function to resolve to the same key. (This would mean that two distinct objects are
20349 * mapped to the same DOM element, which is not possible.) Filters should be applied to the expression,
20350 * before specifying a tracking expression.
20352 * For example: `item in items` is equivalent to `item in items track by $id(item)`. This implies that the DOM elements
20353 * will be associated by item identity in the array.
20355 * For example: `item in items track by $id(item)`. A built in `$id()` function can be used to assign a unique
20356 * `$$hashKey` property to each item in the array. This property is then used as a key to associated DOM elements
20357 * with the corresponding item in the array by identity. Moving the same object in array would move the DOM
20358 * element in the same way in the DOM.
20360 * For example: `item in items track by item.id` is a typical pattern when the items come from the database. In this
20361 * case the object identity does not matter. Two objects are considered equivalent as long as their `id`
20362 * property is same.
20364 * For example: `item in items | filter:searchText track by item.id` is a pattern that might be used to apply a filter
20365 * to items in conjunction with a tracking expression.
20368 * This example initializes the scope to a list of names and
20369 * then uses `ngRepeat` to display every person:
20370 <example module="ngAnimate" deps="angular-animate.js" animations="true">
20371 <file name="index.html">
20372 <div ng-init="friends = [
20373 {name:'John', age:25, gender:'boy'},
20374 {name:'Jessie', age:30, gender:'girl'},
20375 {name:'Johanna', age:28, gender:'girl'},
20376 {name:'Joy', age:15, gender:'girl'},
20377 {name:'Mary', age:28, gender:'girl'},
20378 {name:'Peter', age:95, gender:'boy'},
20379 {name:'Sebastian', age:50, gender:'boy'},
20380 {name:'Erika', age:27, gender:'girl'},
20381 {name:'Patrick', age:40, gender:'boy'},
20382 {name:'Samantha', age:60, gender:'girl'}
20384 I have {{friends.length}} friends. They are:
20385 <input type="search" ng-model="q" placeholder="filter friends..." />
20386 <ul class="example-animate-container">
20387 <li class="animate-repeat" ng-repeat="friend in friends | filter:q">
20388 [{{$index + 1}}] {{friend.name}} who is {{friend.age}} years old.
20393 <file name="animations.css">
20394 .example-animate-container {
20396 border:1px solid black;
20405 box-sizing:border-box;
20408 .animate-repeat.ng-move,
20409 .animate-repeat.ng-enter,
20410 .animate-repeat.ng-leave {
20411 -webkit-transition:all linear 0.5s;
20412 transition:all linear 0.5s;
20415 .animate-repeat.ng-leave.ng-leave-active,
20416 .animate-repeat.ng-move,
20417 .animate-repeat.ng-enter {
20422 .animate-repeat.ng-leave,
20423 .animate-repeat.ng-move.ng-move-active,
20424 .animate-repeat.ng-enter.ng-enter-active {
20429 <file name="protractor.js" type="protractor">
20430 var friends = element.all(by.repeater('friend in friends'));
20432 it('should render initial data set', function() {
20433 expect(friends.count()).toBe(10);
20434 expect(friends.get(0).getText()).toEqual('[1] John who is 25 years old.');
20435 expect(friends.get(1).getText()).toEqual('[2] Jessie who is 30 years old.');
20436 expect(friends.last().getText()).toEqual('[10] Samantha who is 60 years old.');
20437 expect(element(by.binding('friends.length')).getText())
20438 .toMatch("I have 10 friends. They are:");
20441 it('should update repeater when filter predicate changes', function() {
20442 expect(friends.count()).toBe(10);
20444 element(by.model('q')).sendKeys('ma');
20446 expect(friends.count()).toBe(2);
20447 expect(friends.get(0).getText()).toEqual('[1] Mary who is 28 years old.');
20448 expect(friends.last().getText()).toEqual('[2] Samantha who is 60 years old.');
20453 var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
20454 var NG_REMOVED = '$$NG_REMOVED';
20455 var ngRepeatMinErr = minErr('ngRepeat');
20457 transclude: 'element',
20461 link: function($scope, $element, $attr, ctrl, $transclude){
20462 var expression = $attr.ngRepeat;
20463 var match = expression.match(/^\s*([\s\S]+?)\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?\s*$/),
20464 trackByExp, trackByExpGetter, trackByIdExpFn, trackByIdArrayFn, trackByIdObjFn,
20465 lhs, rhs, valueIdentifier, keyIdentifier,
20466 hashFnLocals = {$id: hashKey};
20469 throw ngRepeatMinErr('iexp', "Expected expression in form of '_item_ in _collection_[ track by _id_]' but got '{0}'.",
20475 trackByExp = match[3];
20478 trackByExpGetter = $parse(trackByExp);
20479 trackByIdExpFn = function(key, value, index) {
20480 // assign key, value, and $index to the locals so that they can be used in hash functions
20481 if (keyIdentifier) hashFnLocals[keyIdentifier] = key;
20482 hashFnLocals[valueIdentifier] = value;
20483 hashFnLocals.$index = index;
20484 return trackByExpGetter($scope, hashFnLocals);
20487 trackByIdArrayFn = function(key, value) {
20488 return hashKey(value);
20490 trackByIdObjFn = function(key) {
20495 match = lhs.match(/^(?:([\$\w]+)|\(([\$\w]+)\s*,\s*([\$\w]+)\))$/);
20497 throw ngRepeatMinErr('iidexp', "'_item_' in '_item_ in _collection_' should be an identifier or '(_key_, _value_)' expression, but got '{0}'.",
20500 valueIdentifier = match[3] || match[1];
20501 keyIdentifier = match[2];
20503 // Store a list of elements from previous run. This is a hash where key is the item from the
20504 // iterator, and the value is objects with following properties.
20505 // - scope: bound scope
20506 // - element: previous element.
20507 // - index: position
20508 var lastBlockMap = {};
20511 $scope.$watchCollection(rhs, function ngRepeatAction(collection){
20513 previousNode = $element[0], // current position of the node
20515 // Same as lastBlockMap but it has the current state. It will become the
20516 // lastBlockMap on the next iteration.
20520 key, value, // key/value of iteration
20524 block, // last object information {scope, element, id}
20525 nextBlockOrder = [],
20529 if (isArrayLike(collection)) {
20530 collectionKeys = collection;
20531 trackByIdFn = trackByIdExpFn || trackByIdArrayFn;
20533 trackByIdFn = trackByIdExpFn || trackByIdObjFn;
20534 // if object, extract keys, sort them and use to determine order of iteration over obj props
20535 collectionKeys = [];
20536 for (key in collection) {
20537 if (collection.hasOwnProperty(key) && key.charAt(0) != '$') {
20538 collectionKeys.push(key);
20541 collectionKeys.sort();
20544 arrayLength = collectionKeys.length;
20546 // locate existing items
20547 length = nextBlockOrder.length = collectionKeys.length;
20548 for(index = 0; index < length; index++) {
20549 key = (collection === collectionKeys) ? index : collectionKeys[index];
20550 value = collection[key];
20551 trackById = trackByIdFn(key, value, index);
20552 assertNotHasOwnProperty(trackById, '`track by` id');
20553 if(lastBlockMap.hasOwnProperty(trackById)) {
20554 block = lastBlockMap[trackById];
20555 delete lastBlockMap[trackById];
20556 nextBlockMap[trackById] = block;
20557 nextBlockOrder[index] = block;
20558 } else if (nextBlockMap.hasOwnProperty(trackById)) {
20559 // restore lastBlockMap
20560 forEach(nextBlockOrder, function(block) {
20561 if (block && block.scope) lastBlockMap[block.id] = block;
20563 // This is a duplicate and we need to throw an error
20564 throw ngRepeatMinErr('dupes',
20565 "Duplicates in a repeater are not allowed. Use 'track by' expression to specify unique keys. Repeater: {0}, Duplicate key: {1}, Duplicate value: {2}",
20566 expression, trackById, toJson(value));
20568 // new never before seen block
20569 nextBlockOrder[index] = { id: trackById };
20570 nextBlockMap[trackById] = false;
20574 // remove existing items
20575 for (key in lastBlockMap) {
20576 // lastBlockMap is our own object so we don't need to use special hasOwnPropertyFn
20577 if (lastBlockMap.hasOwnProperty(key)) {
20578 block = lastBlockMap[key];
20579 elementsToRemove = getBlockElements(block.clone);
20580 $animate.leave(elementsToRemove);
20581 forEach(elementsToRemove, function(element) { element[NG_REMOVED] = true; });
20582 block.scope.$destroy();
20586 // we are not using forEach for perf reasons (trying to avoid #call)
20587 for (index = 0, length = collectionKeys.length; index < length; index++) {
20588 key = (collection === collectionKeys) ? index : collectionKeys[index];
20589 value = collection[key];
20590 block = nextBlockOrder[index];
20591 if (nextBlockOrder[index - 1]) previousNode = getBlockEnd(nextBlockOrder[index - 1]);
20594 // if we have already seen this object, then we need to reuse the
20595 // associated scope/element
20596 childScope = block.scope;
20598 nextNode = previousNode;
20600 nextNode = nextNode.nextSibling;
20601 } while(nextNode && nextNode[NG_REMOVED]);
20603 if (getBlockStart(block) != nextNode) {
20604 // existing item which got moved
20605 $animate.move(getBlockElements(block.clone), null, jqLite(previousNode));
20607 previousNode = getBlockEnd(block);
20609 // new item which we don't know about
20610 childScope = $scope.$new();
20613 childScope[valueIdentifier] = value;
20614 if (keyIdentifier) childScope[keyIdentifier] = key;
20615 childScope.$index = index;
20616 childScope.$first = (index === 0);
20617 childScope.$last = (index === (arrayLength - 1));
20618 childScope.$middle = !(childScope.$first || childScope.$last);
20619 // jshint bitwise: false
20620 childScope.$odd = !(childScope.$even = (index&1) === 0);
20621 // jshint bitwise: true
20623 if (!block.scope) {
20624 $transclude(childScope, function(clone) {
20625 clone[clone.length++] = document.createComment(' end ngRepeat: ' + expression + ' ');
20626 $animate.enter(clone, null, jqLite(previousNode));
20627 previousNode = clone;
20628 block.scope = childScope;
20629 // Note: We only need the first/last node of the cloned nodes.
20630 // However, we need to keep the reference to the jqlite wrapper as it might be changed later
20631 // by a directive with templateUrl when its template arrives.
20632 block.clone = clone;
20633 nextBlockMap[block.id] = block;
20637 lastBlockMap = nextBlockMap;
20642 function getBlockStart(block) {
20643 return block.clone[0];
20646 function getBlockEnd(block) {
20647 return block.clone[block.clone.length - 1];
20656 * The `ngShow` directive shows or hides the given HTML element based on the expression
20657 * provided to the `ngShow` attribute. The element is shown or hidden by removing or adding
20658 * the `.ng-hide` CSS class onto the element. The `.ng-hide` CSS class is predefined
20659 * in AngularJS and sets the display style to none (using an !important flag).
20660 * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
20663 * <!-- when $scope.myValue is truthy (element is visible) -->
20664 * <div ng-show="myValue"></div>
20666 * <!-- when $scope.myValue is falsy (element is hidden) -->
20667 * <div ng-show="myValue" class="ng-hide"></div>
20670 * When the `ngShow` expression evaluates to false then the `.ng-hide` CSS class is added to the class attribute
20671 * on the element causing it to become hidden. When true, the `.ng-hide` CSS class is removed
20672 * from the element causing the element not to appear hidden.
20674 * <div class="alert alert-warning">
20675 * **Note:** Here is a list of values that ngShow will consider as a falsy value (case insensitive):<br />
20676 * "f" / "0" / "false" / "no" / "n" / "[]"
20679 * ## Why is !important used?
20681 * You may be wondering why !important is used for the `.ng-hide` CSS class. This is because the `.ng-hide` selector
20682 * can be easily overridden by heavier selectors. For example, something as simple
20683 * as changing the display style on a HTML list item would make hidden elements appear visible.
20684 * This also becomes a bigger issue when dealing with CSS frameworks.
20686 * By using !important, the show and hide behavior will work as expected despite any clash between CSS selector
20687 * specificity (when !important isn't used with any conflicting styles). If a developer chooses to override the
20688 * styling to change how to hide an element then it is just a matter of using !important in their own CSS code.
20690 * ### Overriding `.ng-hide`
20692 * By default, the `.ng-hide` class will style the element with `display:none!important`. If you wish to change
20693 * the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide`
20698 * //this is just another form of hiding an element
20699 * display:block!important;
20700 * position:absolute;
20706 * By default you don't need to override in CSS anything and the animations will work around the display style.
20708 * ## A note about animations with `ngShow`
20710 * Animations in ngShow/ngHide work with the show and hide events that are triggered when the directive expression
20711 * is true and false. This system works like the animation system present with ngClass except that
20712 * you must also include the !important flag to override the display property
20713 * so that you can perform an animation when the element is hidden during the time of the animation.
20717 * //a working example can be found at the bottom of this page
20719 * .my-element.ng-hide-add, .my-element.ng-hide-remove {
20720 * transition:0.5s linear all;
20723 * .my-element.ng-hide-add { ... }
20724 * .my-element.ng-hide-add.ng-hide-add-active { ... }
20725 * .my-element.ng-hide-remove { ... }
20726 * .my-element.ng-hide-remove.ng-hide-remove-active { ... }
20729 * Keep in mind that, as of AngularJS version 1.2.17 (and 1.3.0-beta.11), there is no need to change the display
20730 * property to block during animation states--ngAnimate will handle the style toggling automatically for you.
20733 * addClass: `.ng-hide` - happens after the `ngShow` expression evaluates to a truthy value and the just before contents are set to visible
20734 * removeClass: `.ng-hide` - happens after the `ngShow` expression evaluates to a non truthy value and just before the contents are set to hidden
20737 * @param {expression} ngShow If the {@link guide/expression expression} is truthy
20738 * then the element is shown or hidden respectively.
20741 <example module="ngAnimate" deps="angular-animate.js" animations="true">
20742 <file name="index.html">
20743 Click me: <input type="checkbox" ng-model="checked"><br/>
20746 <div class="check-element animate-show" ng-show="checked">
20747 <span class="glyphicon glyphicon-thumbs-up"></span> I show up when your checkbox is checked.
20752 <div class="check-element animate-show" ng-hide="checked">
20753 <span class="glyphicon glyphicon-thumbs-down"></span> I hide when your checkbox is checked.
20757 <file name="glyphicons.css">
20758 @import url(//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap-glyphicons.css);
20760 <file name="animations.css">
20762 -webkit-transition:all linear 0.5s;
20763 transition:all linear 0.5s;
20767 border:1px solid black;
20771 .animate-show.ng-hide {
20779 border:1px solid black;
20783 <file name="protractor.js" type="protractor">
20784 var thumbsUp = element(by.css('span.glyphicon-thumbs-up'));
20785 var thumbsDown = element(by.css('span.glyphicon-thumbs-down'));
20787 it('should check ng-show / ng-hide', function() {
20788 expect(thumbsUp.isDisplayed()).toBeFalsy();
20789 expect(thumbsDown.isDisplayed()).toBeTruthy();
20791 element(by.model('checked')).click();
20793 expect(thumbsUp.isDisplayed()).toBeTruthy();
20794 expect(thumbsDown.isDisplayed()).toBeFalsy();
20799 var ngShowDirective = ['$animate', function($animate) {
20800 return function(scope, element, attr) {
20801 scope.$watch(attr.ngShow, function ngShowWatchAction(value){
20802 $animate[toBoolean(value) ? 'removeClass' : 'addClass'](element, 'ng-hide');
20813 * The `ngHide` directive shows or hides the given HTML element based on the expression
20814 * provided to the `ngHide` attribute. The element is shown or hidden by removing or adding
20815 * the `ng-hide` CSS class onto the element. The `.ng-hide` CSS class is predefined
20816 * in AngularJS and sets the display style to none (using an !important flag).
20817 * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
20820 * <!-- when $scope.myValue is truthy (element is hidden) -->
20821 * <div ng-hide="myValue" class="ng-hide"></div>
20823 * <!-- when $scope.myValue is falsy (element is visible) -->
20824 * <div ng-hide="myValue"></div>
20827 * When the `.ngHide` expression evaluates to true then the `.ng-hide` CSS class is added to the class attribute
20828 * on the element causing it to become hidden. When false, the `.ng-hide` CSS class is removed
20829 * from the element causing the element not to appear hidden.
20831 * <div class="alert alert-warning">
20832 * **Note:** Here is a list of values that ngHide will consider as a falsy value (case insensitive):<br />
20833 * "f" / "0" / "false" / "no" / "n" / "[]"
20836 * ## Why is !important used?
20838 * You may be wondering why !important is used for the `.ng-hide` CSS class. This is because the `.ng-hide` selector
20839 * can be easily overridden by heavier selectors. For example, something as simple
20840 * as changing the display style on a HTML list item would make hidden elements appear visible.
20841 * This also becomes a bigger issue when dealing with CSS frameworks.
20843 * By using !important, the show and hide behavior will work as expected despite any clash between CSS selector
20844 * specificity (when !important isn't used with any conflicting styles). If a developer chooses to override the
20845 * styling to change how to hide an element then it is just a matter of using !important in their own CSS code.
20847 * ### Overriding `.ng-hide`
20849 * By default, the `.ng-hide` class will style the element with `display:none!important`. If you wish to change
20850 * the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide`
20855 * //this is just another form of hiding an element
20856 * display:block!important;
20857 * position:absolute;
20863 * By default you don't need to override in CSS anything and the animations will work around the display style.
20865 * ## A note about animations with `ngHide`
20867 * Animations in ngShow/ngHide work with the show and hide events that are triggered when the directive expression
20868 * is true and false. This system works like the animation system present with ngClass, except that the `.ng-hide`
20869 * CSS class is added and removed for you instead of your own CSS class.
20873 * //a working example can be found at the bottom of this page
20875 * .my-element.ng-hide-add, .my-element.ng-hide-remove {
20876 * transition:0.5s linear all;
20879 * .my-element.ng-hide-add { ... }
20880 * .my-element.ng-hide-add.ng-hide-add-active { ... }
20881 * .my-element.ng-hide-remove { ... }
20882 * .my-element.ng-hide-remove.ng-hide-remove-active { ... }
20885 * Keep in mind that, as of AngularJS version 1.2.17 (and 1.3.0-beta.11), there is no need to change the display
20886 * property to block during animation states--ngAnimate will handle the style toggling automatically for you.
20889 * removeClass: `.ng-hide` - happens after the `ngHide` expression evaluates to a truthy value and just before the contents are set to hidden
20890 * addClass: `.ng-hide` - happens after the `ngHide` expression evaluates to a non truthy value and just before the contents are set to visible
20893 * @param {expression} ngHide If the {@link guide/expression expression} is truthy then
20894 * the element is shown or hidden respectively.
20897 <example module="ngAnimate" deps="angular-animate.js" animations="true">
20898 <file name="index.html">
20899 Click me: <input type="checkbox" ng-model="checked"><br/>
20902 <div class="check-element animate-hide" ng-show="checked">
20903 <span class="glyphicon glyphicon-thumbs-up"></span> I show up when your checkbox is checked.
20908 <div class="check-element animate-hide" ng-hide="checked">
20909 <span class="glyphicon glyphicon-thumbs-down"></span> I hide when your checkbox is checked.
20913 <file name="glyphicons.css">
20914 @import url(//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap-glyphicons.css);
20916 <file name="animations.css">
20918 -webkit-transition:all linear 0.5s;
20919 transition:all linear 0.5s;
20923 border:1px solid black;
20927 .animate-hide.ng-hide {
20935 border:1px solid black;
20939 <file name="protractor.js" type="protractor">
20940 var thumbsUp = element(by.css('span.glyphicon-thumbs-up'));
20941 var thumbsDown = element(by.css('span.glyphicon-thumbs-down'));
20943 it('should check ng-show / ng-hide', function() {
20944 expect(thumbsUp.isDisplayed()).toBeFalsy();
20945 expect(thumbsDown.isDisplayed()).toBeTruthy();
20947 element(by.model('checked')).click();
20949 expect(thumbsUp.isDisplayed()).toBeTruthy();
20950 expect(thumbsDown.isDisplayed()).toBeFalsy();
20955 var ngHideDirective = ['$animate', function($animate) {
20956 return function(scope, element, attr) {
20957 scope.$watch(attr.ngHide, function ngHideWatchAction(value){
20958 $animate[toBoolean(value) ? 'addClass' : 'removeClass'](element, 'ng-hide');
20969 * The `ngStyle` directive allows you to set CSS style on an HTML element conditionally.
20972 * @param {expression} ngStyle
20974 * {@link guide/expression Expression} which evals to an
20975 * object whose keys are CSS style names and values are corresponding values for those CSS
20978 * Since some CSS style names are not valid keys for an object, they must be quoted.
20979 * See the 'background-color' style in the example below.
20983 <file name="index.html">
20984 <input type="button" value="set color" ng-click="myStyle={color:'red'}">
20985 <input type="button" value="set background" ng-click="myStyle={'background-color':'blue'}">
20986 <input type="button" value="clear" ng-click="myStyle={}">
20988 <span ng-style="myStyle">Sample Text</span>
20989 <pre>myStyle={{myStyle}}</pre>
20991 <file name="style.css">
20996 <file name="protractor.js" type="protractor">
20997 var colorSpan = element(by.css('span'));
20999 it('should check ng-style', function() {
21000 expect(colorSpan.getCssValue('color')).toBe('rgba(0, 0, 0, 1)');
21001 element(by.css('input[value=\'set color\']')).click();
21002 expect(colorSpan.getCssValue('color')).toBe('rgba(255, 0, 0, 1)');
21003 element(by.css('input[value=clear]')).click();
21004 expect(colorSpan.getCssValue('color')).toBe('rgba(0, 0, 0, 1)');
21009 var ngStyleDirective = ngDirective(function(scope, element, attr) {
21010 scope.$watch(attr.ngStyle, function ngStyleWatchAction(newStyles, oldStyles) {
21011 if (oldStyles && (newStyles !== oldStyles)) {
21012 forEach(oldStyles, function(val, style) { element.css(style, '');});
21014 if (newStyles) element.css(newStyles);
21024 * The `ngSwitch` directive is used to conditionally swap DOM structure on your template based on a scope expression.
21025 * Elements within `ngSwitch` but without `ngSwitchWhen` or `ngSwitchDefault` directives will be preserved at the location
21026 * as specified in the template.
21028 * The directive itself works similar to ngInclude, however, instead of downloading template code (or loading it
21029 * from the template cache), `ngSwitch` simply chooses one of the nested elements and makes it visible based on which element
21030 * matches the value obtained from the evaluated expression. In other words, you define a container element
21031 * (where you place the directive), place an expression on the **`on="..."` attribute**
21032 * (or the **`ng-switch="..."` attribute**), define any inner elements inside of the directive and place
21033 * a when attribute per element. The when attribute is used to inform ngSwitch which element to display when the on
21034 * expression is evaluated. If a matching expression is not found via a when attribute then an element with the default
21035 * attribute is displayed.
21037 * <div class="alert alert-info">
21038 * Be aware that the attribute values to match against cannot be expressions. They are interpreted
21039 * as literal string values to match against.
21040 * For example, **`ng-switch-when="someVal"`** will match against the string `"someVal"` not against the
21041 * value of the expression `$scope.someVal`.
21045 * enter - happens after the ngSwitch contents change and the matched child element is placed inside the container
21046 * leave - happens just after the ngSwitch contents change and just before the former contents are removed from the DOM
21051 * <ANY ng-switch="expression">
21052 * <ANY ng-switch-when="matchValue1">...</ANY>
21053 * <ANY ng-switch-when="matchValue2">...</ANY>
21054 * <ANY ng-switch-default>...</ANY>
21061 * @param {*} ngSwitch|on expression to match against <tt>ng-switch-when</tt>.
21062 * On child elements add:
21064 * * `ngSwitchWhen`: the case statement to match against. If match then this
21065 * case will be displayed. If the same match appears multiple times, all the
21066 * elements will be displayed.
21067 * * `ngSwitchDefault`: the default case when no other case match. If there
21068 * are multiple default cases, all of them will be displayed when no other
21073 <example module="switchExample" deps="angular-animate.js" animations="true">
21074 <file name="index.html">
21075 <div ng-controller="ExampleController">
21076 <select ng-model="selection" ng-options="item for item in items">
21078 <tt>selection={{selection}}</tt>
21080 <div class="animate-switch-container"
21081 ng-switch on="selection">
21082 <div class="animate-switch" ng-switch-when="settings">Settings Div</div>
21083 <div class="animate-switch" ng-switch-when="home">Home Span</div>
21084 <div class="animate-switch" ng-switch-default>default</div>
21088 <file name="script.js">
21089 angular.module('switchExample', ['ngAnimate'])
21090 .controller('ExampleController', ['$scope', function($scope) {
21091 $scope.items = ['settings', 'home', 'other'];
21092 $scope.selection = $scope.items[0];
21095 <file name="animations.css">
21096 .animate-switch-container {
21099 border:1px solid black;
21108 .animate-switch.ng-animate {
21109 -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
21110 transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
21119 .animate-switch.ng-leave.ng-leave-active,
21120 .animate-switch.ng-enter {
21123 .animate-switch.ng-leave,
21124 .animate-switch.ng-enter.ng-enter-active {
21128 <file name="protractor.js" type="protractor">
21129 var switchElem = element(by.css('[ng-switch]'));
21130 var select = element(by.model('selection'));
21132 it('should start in settings', function() {
21133 expect(switchElem.getText()).toMatch(/Settings Div/);
21135 it('should change to home', function() {
21136 select.all(by.css('option')).get(1).click();
21137 expect(switchElem.getText()).toMatch(/Home Span/);
21139 it('should select default', function() {
21140 select.all(by.css('option')).get(2).click();
21141 expect(switchElem.getText()).toMatch(/default/);
21146 var ngSwitchDirective = ['$animate', function($animate) {
21149 require: 'ngSwitch',
21151 // asks for $scope to fool the BC controller module
21152 controller: ['$scope', function ngSwitchController() {
21155 link: function(scope, element, attr, ngSwitchController) {
21156 var watchExpr = attr.ngSwitch || attr.on,
21157 selectedTranscludes = [],
21158 selectedElements = [],
21159 previousElements = [],
21160 selectedScopes = [];
21162 scope.$watch(watchExpr, function ngSwitchWatchAction(value) {
21164 for (i = 0, ii = previousElements.length; i < ii; ++i) {
21165 previousElements[i].remove();
21167 previousElements.length = 0;
21169 for (i = 0, ii = selectedScopes.length; i < ii; ++i) {
21170 var selected = selectedElements[i];
21171 selectedScopes[i].$destroy();
21172 previousElements[i] = selected;
21173 $animate.leave(selected, function() {
21174 previousElements.splice(i, 1);
21178 selectedElements.length = 0;
21179 selectedScopes.length = 0;
21181 if ((selectedTranscludes = ngSwitchController.cases['!' + value] || ngSwitchController.cases['?'])) {
21182 scope.$eval(attr.change);
21183 forEach(selectedTranscludes, function(selectedTransclude) {
21184 var selectedScope = scope.$new();
21185 selectedScopes.push(selectedScope);
21186 selectedTransclude.transclude(selectedScope, function(caseElement) {
21187 var anchor = selectedTransclude.element;
21189 selectedElements.push(caseElement);
21190 $animate.enter(caseElement, anchor.parent(), anchor);
21199 var ngSwitchWhenDirective = ngDirective({
21200 transclude: 'element',
21202 require: '^ngSwitch',
21203 link: function(scope, element, attrs, ctrl, $transclude) {
21204 ctrl.cases['!' + attrs.ngSwitchWhen] = (ctrl.cases['!' + attrs.ngSwitchWhen] || []);
21205 ctrl.cases['!' + attrs.ngSwitchWhen].push({ transclude: $transclude, element: element });
21209 var ngSwitchDefaultDirective = ngDirective({
21210 transclude: 'element',
21212 require: '^ngSwitch',
21213 link: function(scope, element, attr, ctrl, $transclude) {
21214 ctrl.cases['?'] = (ctrl.cases['?'] || []);
21215 ctrl.cases['?'].push({ transclude: $transclude, element: element });
21221 * @name ngTransclude
21225 * Directive that marks the insertion point for the transcluded DOM of the nearest parent directive that uses transclusion.
21227 * Any existing content of the element that this directive is placed on will be removed before the transcluded content is inserted.
21232 <example module="transcludeExample">
21233 <file name="index.html">
21235 angular.module('transcludeExample', [])
21236 .directive('pane', function(){
21240 scope: { title:'@' },
21241 template: '<div style="border: 1px solid black;">' +
21242 '<div style="background-color: gray">{{title}}</div>' +
21243 '<div ng-transclude></div>' +
21247 .controller('ExampleController', ['$scope', function($scope) {
21248 $scope.title = 'Lorem Ipsum';
21249 $scope.text = 'Neque porro quisquam est qui dolorem ipsum quia dolor...';
21252 <div ng-controller="ExampleController">
21253 <input ng-model="title"><br>
21254 <textarea ng-model="text"></textarea> <br/>
21255 <pane title="{{title}}">{{text}}</pane>
21258 <file name="protractor.js" type="protractor">
21259 it('should have transcluded', function() {
21260 var titleElement = element(by.model('title'));
21261 titleElement.clear();
21262 titleElement.sendKeys('TITLE');
21263 var textElement = element(by.model('text'));
21264 textElement.clear();
21265 textElement.sendKeys('TEXT');
21266 expect(element(by.binding('title')).getText()).toEqual('TITLE');
21267 expect(element(by.binding('text')).getText()).toEqual('TEXT');
21273 var ngTranscludeDirective = ngDirective({
21274 link: function($scope, $element, $attrs, controller, $transclude) {
21275 if (!$transclude) {
21276 throw minErr('ngTransclude')('orphan',
21277 'Illegal use of ngTransclude directive in the template! ' +
21278 'No parent directive that requires a transclusion found. ' +
21280 startingTag($element));
21283 $transclude(function(clone) {
21285 $element.append(clone);
21296 * Load the content of a `<script>` element into {@link ng.$templateCache `$templateCache`}, so that the
21297 * template can be used by {@link ng.directive:ngInclude `ngInclude`},
21298 * {@link ngRoute.directive:ngView `ngView`}, or {@link guide/directive directives}. The type of the
21299 * `<script>` element must be specified as `text/ng-template`, and a cache name for the template must be
21300 * assigned through the element's `id`, which can then be used as a directive's `templateUrl`.
21302 * @param {string} type Must be set to `'text/ng-template'`.
21303 * @param {string} id Cache name of the template.
21307 <file name="index.html">
21308 <script type="text/ng-template" id="/tpl.html">
21309 Content of the template.
21312 <a ng-click="currentTpl='/tpl.html'" id="tpl-link">Load inlined template</a>
21313 <div id="tpl-content" ng-include src="currentTpl"></div>
21315 <file name="protractor.js" type="protractor">
21316 it('should load template defined inside script tag', function() {
21317 element(by.css('#tpl-link')).click();
21318 expect(element(by.css('#tpl-content')).getText()).toMatch(/Content of the template/);
21323 var scriptDirective = ['$templateCache', function($templateCache) {
21327 compile: function(element, attr) {
21328 if (attr.type == 'text/ng-template') {
21329 var templateUrl = attr.id,
21330 // IE is not consistent, in scripts we have to read .text but in other nodes we have to read .textContent
21331 text = element[0].text;
21333 $templateCache.put(templateUrl, text);
21339 var ngOptionsMinErr = minErr('ngOptions');
21346 * HTML `SELECT` element with angular data-binding.
21350 * The `ngOptions` attribute can be used to dynamically generate a list of `<option>`
21351 * elements for the `<select>` element using the array or object obtained by evaluating the
21352 * `ngOptions` comprehension_expression.
21354 * When an item in the `<select>` menu is selected, the array element or object property
21355 * represented by the selected option will be bound to the model identified by the `ngModel`
21358 * <div class="alert alert-warning">
21359 * **Note:** `ngModel` compares by reference, not value. This is important when binding to an
21360 * array of objects. See an example [in this jsfiddle](http://jsfiddle.net/qWzTb/).
21363 * Optionally, a single hard-coded `<option>` element, with the value set to an empty string, can
21364 * be nested into the `<select>` element. This element will then represent the `null` or "not selected"
21365 * option. See example below for demonstration.
21367 * <div class="alert alert-warning">
21368 * **Note:** `ngOptions` provides an iterator facility for the `<option>` element which should be used instead
21369 * of {@link ng.directive:ngRepeat ngRepeat} when you want the
21370 * `select` model to be bound to a non-string value. This is because an option element can only
21371 * be bound to string values at present.
21374 * @param {string} ngModel Assignable angular expression to data-bind to.
21375 * @param {string=} name Property name of the form under which the control is published.
21376 * @param {string=} required The control is considered valid only if value is entered.
21377 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21378 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21379 * `required` when you want to data-bind to the `required` attribute.
21380 * @param {comprehension_expression=} ngOptions in one of the following forms:
21382 * * for array data sources:
21383 * * `label` **`for`** `value` **`in`** `array`
21384 * * `select` **`as`** `label` **`for`** `value` **`in`** `array`
21385 * * `label` **`group by`** `group` **`for`** `value` **`in`** `array`
21386 * * `select` **`as`** `label` **`group by`** `group` **`for`** `value` **`in`** `array` **`track by`** `trackexpr`
21387 * * for object data sources:
21388 * * `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
21389 * * `select` **`as`** `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
21390 * * `label` **`group by`** `group` **`for (`**`key`**`,`** `value`**`) in`** `object`
21391 * * `select` **`as`** `label` **`group by`** `group`
21392 * **`for` `(`**`key`**`,`** `value`**`) in`** `object`
21396 * * `array` / `object`: an expression which evaluates to an array / object to iterate over.
21397 * * `value`: local variable which will refer to each item in the `array` or each property value
21398 * of `object` during iteration.
21399 * * `key`: local variable which will refer to a property name in `object` during iteration.
21400 * * `label`: The result of this expression will be the label for `<option>` element. The
21401 * `expression` will most likely refer to the `value` variable (e.g. `value.propertyName`).
21402 * * `select`: The result of this expression will be bound to the model of the parent `<select>`
21403 * element. If not specified, `select` expression will default to `value`.
21404 * * `group`: The result of this expression will be used to group options using the `<optgroup>`
21406 * * `trackexpr`: Used when working with an array of objects. The result of this expression will be
21407 * used to identify the objects in the array. The `trackexpr` will most likely refer to the
21408 * `value` variable (e.g. `value.propertyName`).
21411 <example module="selectExample">
21412 <file name="index.html">
21414 angular.module('selectExample', [])
21415 .controller('ExampleController', ['$scope', function($scope) {
21417 {name:'black', shade:'dark'},
21418 {name:'white', shade:'light'},
21419 {name:'red', shade:'dark'},
21420 {name:'blue', shade:'dark'},
21421 {name:'yellow', shade:'light'}
21423 $scope.myColor = $scope.colors[2]; // red
21426 <div ng-controller="ExampleController">
21428 <li ng-repeat="color in colors">
21429 Name: <input ng-model="color.name">
21430 [<a href ng-click="colors.splice($index, 1)">X</a>]
21433 [<a href ng-click="colors.push({})">add</a>]
21437 Color (null not allowed):
21438 <select ng-model="myColor" ng-options="color.name for color in colors"></select><br>
21440 Color (null allowed):
21441 <span class="nullable">
21442 <select ng-model="myColor" ng-options="color.name for color in colors">
21443 <option value="">-- choose color --</option>
21447 Color grouped by shade:
21448 <select ng-model="myColor" ng-options="color.name group by color.shade for color in colors">
21452 Select <a href ng-click="myColor = { name:'not in list', shade: 'other' }">bogus</a>.<br>
21454 Currently selected: {{ {selected_color:myColor} }}
21455 <div style="border:solid 1px black; height:20px"
21456 ng-style="{'background-color':myColor.name}">
21460 <file name="protractor.js" type="protractor">
21461 it('should check ng-options', function() {
21462 expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('red');
21463 element.all(by.model('myColor')).first().click();
21464 element.all(by.css('select[ng-model="myColor"] option')).first().click();
21465 expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('black');
21466 element(by.css('.nullable select[ng-model="myColor"]')).click();
21467 element.all(by.css('.nullable select[ng-model="myColor"] option')).first().click();
21468 expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('null');
21474 var ngOptionsDirective = valueFn({ terminal: true });
21475 // jshint maxlen: false
21476 var selectDirective = ['$compile', '$parse', function($compile, $parse) {
21477 //000011111111110000000000022222222220000000000000000000003333333333000000000000004444444444444440000000005555555555555550000000666666666666666000000000000000777777777700000000000000000008888888888
21478 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]+?))?$/,
21479 nullModelCtrl = {$setViewValue: noop};
21480 // jshint maxlen: 100
21484 require: ['select', '?ngModel'],
21485 controller: ['$element', '$scope', '$attrs', function($element, $scope, $attrs) {
21488 ngModelCtrl = nullModelCtrl,
21493 self.databound = $attrs.ngModel;
21496 self.init = function(ngModelCtrl_, nullOption_, unknownOption_) {
21497 ngModelCtrl = ngModelCtrl_;
21498 nullOption = nullOption_;
21499 unknownOption = unknownOption_;
21503 self.addOption = function(value) {
21504 assertNotHasOwnProperty(value, '"option value"');
21505 optionsMap[value] = true;
21507 if (ngModelCtrl.$viewValue == value) {
21508 $element.val(value);
21509 if (unknownOption.parent()) unknownOption.remove();
21514 self.removeOption = function(value) {
21515 if (this.hasOption(value)) {
21516 delete optionsMap[value];
21517 if (ngModelCtrl.$viewValue == value) {
21518 this.renderUnknownOption(value);
21524 self.renderUnknownOption = function(val) {
21525 var unknownVal = '? ' + hashKey(val) + ' ?';
21526 unknownOption.val(unknownVal);
21527 $element.prepend(unknownOption);
21528 $element.val(unknownVal);
21529 unknownOption.prop('selected', true); // needed for IE
21533 self.hasOption = function(value) {
21534 return optionsMap.hasOwnProperty(value);
21537 $scope.$on('$destroy', function() {
21538 // disable unknown option so that we don't do work when the whole select is being destroyed
21539 self.renderUnknownOption = noop;
21543 link: function(scope, element, attr, ctrls) {
21544 // if ngModel is not defined, we don't need to do anything
21545 if (!ctrls[1]) return;
21547 var selectCtrl = ctrls[0],
21548 ngModelCtrl = ctrls[1],
21549 multiple = attr.multiple,
21550 optionsExp = attr.ngOptions,
21551 nullOption = false, // if false, user will not be able to select it (used by ngOptions)
21553 // we can't just jqLite('<option>') since jqLite is not smart enough
21554 // to create it in <select> and IE barfs otherwise.
21555 optionTemplate = jqLite(document.createElement('option')),
21556 optGroupTemplate =jqLite(document.createElement('optgroup')),
21557 unknownOption = optionTemplate.clone();
21559 // find "null" option
21560 for(var i = 0, children = element.children(), ii = children.length; i < ii; i++) {
21561 if (children[i].value === '') {
21562 emptyOption = nullOption = children.eq(i);
21567 selectCtrl.init(ngModelCtrl, nullOption, unknownOption);
21569 // required validator
21571 ngModelCtrl.$isEmpty = function(value) {
21572 return !value || value.length === 0;
21576 if (optionsExp) setupAsOptions(scope, element, ngModelCtrl);
21577 else if (multiple) setupAsMultiple(scope, element, ngModelCtrl);
21578 else setupAsSingle(scope, element, ngModelCtrl, selectCtrl);
21581 ////////////////////////////
21585 function setupAsSingle(scope, selectElement, ngModelCtrl, selectCtrl) {
21586 ngModelCtrl.$render = function() {
21587 var viewValue = ngModelCtrl.$viewValue;
21589 if (selectCtrl.hasOption(viewValue)) {
21590 if (unknownOption.parent()) unknownOption.remove();
21591 selectElement.val(viewValue);
21592 if (viewValue === '') emptyOption.prop('selected', true); // to make IE9 happy
21594 if (isUndefined(viewValue) && emptyOption) {
21595 selectElement.val('');
21597 selectCtrl.renderUnknownOption(viewValue);
21602 selectElement.on('change', function() {
21603 scope.$apply(function() {
21604 if (unknownOption.parent()) unknownOption.remove();
21605 ngModelCtrl.$setViewValue(selectElement.val());
21610 function setupAsMultiple(scope, selectElement, ctrl) {
21612 ctrl.$render = function() {
21613 var items = new HashMap(ctrl.$viewValue);
21614 forEach(selectElement.find('option'), function(option) {
21615 option.selected = isDefined(items.get(option.value));
21619 // we have to do it on each watch since ngModel watches reference, but
21620 // we need to work of an array, so we need to see if anything was inserted/removed
21621 scope.$watch(function selectMultipleWatch() {
21622 if (!equals(lastView, ctrl.$viewValue)) {
21623 lastView = shallowCopy(ctrl.$viewValue);
21628 selectElement.on('change', function() {
21629 scope.$apply(function() {
21631 forEach(selectElement.find('option'), function(option) {
21632 if (option.selected) {
21633 array.push(option.value);
21636 ctrl.$setViewValue(array);
21641 function setupAsOptions(scope, selectElement, ctrl) {
21644 if (!(match = optionsExp.match(NG_OPTIONS_REGEXP))) {
21645 throw ngOptionsMinErr('iexp',
21646 "Expected expression in form of " +
21647 "'_select_ (as _label_)? for (_key_,)?_value_ in _collection_'" +
21648 " but got '{0}'. Element: {1}",
21649 optionsExp, startingTag(selectElement));
21652 var displayFn = $parse(match[2] || match[1]),
21653 valueName = match[4] || match[6],
21654 keyName = match[5],
21655 groupByFn = $parse(match[3] || ''),
21656 valueFn = $parse(match[2] ? match[1] : valueName),
21657 valuesFn = $parse(match[7]),
21659 trackFn = track ? $parse(match[8]) : null,
21660 // This is an array of array of existing option groups in DOM.
21661 // We try to reuse these if possible
21662 // - optionGroupsCache[0] is the options with no option group
21663 // - optionGroupsCache[?][0] is the parent: either the SELECT or OPTGROUP element
21664 optionGroupsCache = [[{element: selectElement, label:''}]];
21667 // compile the element since there might be bindings in it
21668 $compile(nullOption)(scope);
21670 // remove the class, which is added automatically because we recompile the element and it
21671 // becomes the compilation root
21672 nullOption.removeClass('ng-scope');
21674 // we need to remove it before calling selectElement.empty() because otherwise IE will
21675 // remove the label from the element. wtf?
21676 nullOption.remove();
21679 // clear contents, we'll add what's needed based on the model
21680 selectElement.empty();
21682 selectElement.on('change', function() {
21683 scope.$apply(function() {
21685 collection = valuesFn(scope) || [],
21687 key, value, optionElement, index, groupIndex, length, groupLength, trackIndex;
21691 for (groupIndex = 0, groupLength = optionGroupsCache.length;
21692 groupIndex < groupLength;
21694 // list of options for that group. (first item has the parent)
21695 optionGroup = optionGroupsCache[groupIndex];
21697 for(index = 1, length = optionGroup.length; index < length; index++) {
21698 if ((optionElement = optionGroup[index].element)[0].selected) {
21699 key = optionElement.val();
21700 if (keyName) locals[keyName] = key;
21702 for (trackIndex = 0; trackIndex < collection.length; trackIndex++) {
21703 locals[valueName] = collection[trackIndex];
21704 if (trackFn(scope, locals) == key) break;
21707 locals[valueName] = collection[key];
21709 value.push(valueFn(scope, locals));
21714 key = selectElement.val();
21717 } else if (key === ''){
21721 for (trackIndex = 0; trackIndex < collection.length; trackIndex++) {
21722 locals[valueName] = collection[trackIndex];
21723 if (trackFn(scope, locals) == key) {
21724 value = valueFn(scope, locals);
21729 locals[valueName] = collection[key];
21730 if (keyName) locals[keyName] = key;
21731 value = valueFn(scope, locals);
21735 ctrl.$setViewValue(value);
21740 ctrl.$render = render;
21742 scope.$watchCollection(valuesFn, render);
21743 scope.$watchCollection(function () {
21745 values = valuesFn(scope);
21747 var toDisplay = new Array(values.length);
21748 for (var i = 0, ii = values.length; i < ii; i++) {
21749 locals[valueName] = values[i];
21750 toDisplay[i] = displayFn(scope, locals);
21757 scope.$watchCollection(function() { return ctrl.$modelValue; }, render);
21760 function getSelectedSet() {
21761 var selectedSet = false;
21763 var modelValue = ctrl.$modelValue;
21764 if (trackFn && isArray(modelValue)) {
21765 selectedSet = new HashMap([]);
21767 for (var trackIndex = 0; trackIndex < modelValue.length; trackIndex++) {
21768 locals[valueName] = modelValue[trackIndex];
21769 selectedSet.put(trackFn(scope, locals), modelValue[trackIndex]);
21772 selectedSet = new HashMap(modelValue);
21775 return selectedSet;
21779 function render() {
21780 // Temporary location for the option groups before we render them
21781 var optionGroups = {'':[]},
21782 optionGroupNames = [''],
21786 existingParent, existingOptions, existingOption,
21787 modelValue = ctrl.$modelValue,
21788 values = valuesFn(scope) || [],
21789 keys = keyName ? sortedKeys(values) : values,
21791 groupLength, length,
21795 selectedSet = getSelectedSet(),
21801 // We now build up the list of options we need (we merge later)
21802 for (index = 0; length = keys.length, index < length; index++) {
21807 if ( key.charAt(0) === '$' ) continue;
21808 locals[keyName] = key;
21811 locals[valueName] = values[key];
21813 optionGroupName = groupByFn(scope, locals) || '';
21814 if (!(optionGroup = optionGroups[optionGroupName])) {
21815 optionGroup = optionGroups[optionGroupName] = [];
21816 optionGroupNames.push(optionGroupName);
21819 selected = isDefined(
21820 selectedSet.remove(trackFn ? trackFn(scope, locals) : valueFn(scope, locals))
21824 var modelCast = {};
21825 modelCast[valueName] = modelValue;
21826 selected = trackFn(scope, modelCast) === trackFn(scope, locals);
21828 selected = modelValue === valueFn(scope, locals);
21830 selectedSet = selectedSet || selected; // see if at least one item is selected
21832 label = displayFn(scope, locals); // what will be seen by the user
21834 // doing displayFn(scope, locals) || '' overwrites zero values
21835 label = isDefined(label) ? label : '';
21837 // either the index into array or key from object
21838 id: trackFn ? trackFn(scope, locals) : (keyName ? keys[index] : index),
21840 selected: selected // determine if we should be selected
21844 if (nullOption || modelValue === null) {
21845 // insert null option if we have a placeholder, or the model is null
21846 optionGroups[''].unshift({id:'', label:'', selected:!selectedSet});
21847 } else if (!selectedSet) {
21848 // option could not be found, we have to insert the undefined item
21849 optionGroups[''].unshift({id:'?', label:'', selected:true});
21853 // Now we need to update the list of DOM nodes to match the optionGroups we computed above
21854 for (groupIndex = 0, groupLength = optionGroupNames.length;
21855 groupIndex < groupLength;
21857 // current option group name or '' if no group
21858 optionGroupName = optionGroupNames[groupIndex];
21860 // list of options for that group. (first item has the parent)
21861 optionGroup = optionGroups[optionGroupName];
21863 if (optionGroupsCache.length <= groupIndex) {
21864 // we need to grow the optionGroups
21866 element: optGroupTemplate.clone().attr('label', optionGroupName),
21867 label: optionGroup.label
21869 existingOptions = [existingParent];
21870 optionGroupsCache.push(existingOptions);
21871 selectElement.append(existingParent.element);
21873 existingOptions = optionGroupsCache[groupIndex];
21874 existingParent = existingOptions[0]; // either SELECT (no group) or OPTGROUP element
21876 // update the OPTGROUP label if not the same.
21877 if (existingParent.label != optionGroupName) {
21878 existingParent.element.attr('label', existingParent.label = optionGroupName);
21882 lastElement = null; // start at the beginning
21883 for(index = 0, length = optionGroup.length; index < length; index++) {
21884 option = optionGroup[index];
21885 if ((existingOption = existingOptions[index+1])) {
21887 lastElement = existingOption.element;
21888 if (existingOption.label !== option.label) {
21889 lastElement.text(existingOption.label = option.label);
21891 if (existingOption.id !== option.id) {
21892 lastElement.val(existingOption.id = option.id);
21894 // lastElement.prop('selected') provided by jQuery has side-effects
21895 if (lastElement[0].selected !== option.selected) {
21896 lastElement.prop('selected', (existingOption.selected = option.selected));
21899 // The selected item wouldn't visually update on IE without this.
21900 // Tested on Win7: IE9, IE10 and IE11. Future IEs should be tested as well
21901 lastElement.prop('selected', existingOption.selected);
21907 // if it's a null option
21908 if (option.id === '' && nullOption) {
21909 // put back the pre-compiled element
21910 element = nullOption;
21912 // jQuery(v1.4.2) Bug: We should be able to chain the method calls, but
21913 // in this version of jQuery on some browser the .text() returns a string
21914 // rather then the element.
21915 (element = optionTemplate.clone())
21917 .prop('selected', option.selected)
21918 .attr('selected', option.selected)
21919 .text(option.label);
21922 existingOptions.push(existingOption = {
21924 label: option.label,
21926 selected: option.selected
21929 lastElement.after(element);
21931 existingParent.element.append(element);
21933 lastElement = element;
21936 // remove any excessive OPTIONs in a group
21937 index++; // increment since the existingOptions[0] is parent element not OPTION
21938 while(existingOptions.length > index) {
21939 existingOptions.pop().element.remove();
21942 // remove any excessive OPTGROUPs from select
21943 while(optionGroupsCache.length > groupIndex) {
21944 optionGroupsCache.pop()[0].element.remove();
21952 var optionDirective = ['$interpolate', function($interpolate) {
21953 var nullSelectCtrl = {
21961 compile: function(element, attr) {
21962 if (isUndefined(attr.value)) {
21963 var interpolateFn = $interpolate(element.text(), true);
21964 if (!interpolateFn) {
21965 attr.$set('value', element.text());
21969 return function (scope, element, attr) {
21970 var selectCtrlName = '$selectController',
21971 parent = element.parent(),
21972 selectCtrl = parent.data(selectCtrlName) ||
21973 parent.parent().data(selectCtrlName); // in case we are in optgroup
21975 if (selectCtrl && selectCtrl.databound) {
21976 // For some reason Opera defaults to true and if not overridden this messes up the repeater.
21977 // We don't want the view to drive the initialization of the model anyway.
21978 element.prop('selected', false);
21980 selectCtrl = nullSelectCtrl;
21983 if (interpolateFn) {
21984 scope.$watch(interpolateFn, function interpolateWatchAction(newVal, oldVal) {
21985 attr.$set('value', newVal);
21986 if (newVal !== oldVal) selectCtrl.removeOption(oldVal);
21987 selectCtrl.addOption(newVal);
21990 selectCtrl.addOption(attr.value);
21993 element.on('$destroy', function() {
21994 selectCtrl.removeOption(attr.value);
22001 var styleDirective = valueFn({
22006 if (window.angular.bootstrap) {
22007 //AngularJS is already loaded, so we can return here...
22008 console.log('WARNING: Tried to load angular more than once.');
22012 //try to bind to jquery now so that one can write angular.element().read()
22013 //but we will rebind on bootstrap again.
22016 publishExternalAPI(angular);
22018 jqLite(document).ready(function() {
22019 angularInit(document, bootstrap);
22022 })(window, document);
22024 !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{display:none !important;}ng\\:form{display:block;}.ng-animate-block-transitions{transition:0s all!important;-webkit-transition:0s all!important;}.ng-hide-add-active,.ng-hide-remove{display:block!important;}</style>');